]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Tue, 4 Aug 2026 23:47:10 +0000 (19:47 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 4 Aug 2026 23:47:10 +0000 (19:47 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
675 files changed:
queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-fix-some-style-issues.patch [new file with mode: 0644]
queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-5.10/series
queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch [new file with mode: 0644]
queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch [new file with mode: 0644]
queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch [new file with mode: 0644]
queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch [new file with mode: 0644]
queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch [new file with mode: 0644]
queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch [new file with mode: 0644]
queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch [new file with mode: 0644]
queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch [new file with mode: 0644]
queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch [new file with mode: 0644]
queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch [new file with mode: 0644]
queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch [new file with mode: 0644]
queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch [new file with mode: 0644]
queue-6.1/ipvs-fix-the-checksum-validations.patch [new file with mode: 0644]
queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch [new file with mode: 0644]
queue-6.1/ksmbd-return-success-for-deferred-final-close.patch [new file with mode: 0644]
queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch [new file with mode: 0644]
queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch [new file with mode: 0644]
queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch [new file with mode: 0644]
queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch [new file with mode: 0644]
queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch [new file with mode: 0644]
queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch [new file with mode: 0644]
queue-6.1/scsi-libsas-delete-struct-scsi_core.patch [new file with mode: 0644]
queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch [new file with mode: 0644]
queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch [new file with mode: 0644]
queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-6.1/series
queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch [new file with mode: 0644]
queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch [new file with mode: 0644]
queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch [new file with mode: 0644]
queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch [new file with mode: 0644]
queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch [new file with mode: 0644]
queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch [new file with mode: 0644]
queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch [new file with mode: 0644]
queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch [new file with mode: 0644]
queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch [new file with mode: 0644]
queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch [new file with mode: 0644]
queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch [new file with mode: 0644]
queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch [new file with mode: 0644]
queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch [new file with mode: 0644]
queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch [new file with mode: 0644]
queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch [new file with mode: 0644]
queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch [new file with mode: 0644]
queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch [new file with mode: 0644]
queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch [new file with mode: 0644]
queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch [new file with mode: 0644]
queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-6.12/hwmon-ina226-add-support-for-sy24655.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch [new file with mode: 0644]
queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch [new file with mode: 0644]
queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch [new file with mode: 0644]
queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch [new file with mode: 0644]
queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch [new file with mode: 0644]
queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch [new file with mode: 0644]
queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch [new file with mode: 0644]
queue-6.12/idpf-adjust-txq-ring-count-minimum.patch [new file with mode: 0644]
queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch [new file with mode: 0644]
queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch [new file with mode: 0644]
queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch [new file with mode: 0644]
queue-6.12/ipvs-fix-the-checksum-validations.patch [new file with mode: 0644]
queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch [new file with mode: 0644]
queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch [new file with mode: 0644]
queue-6.12/ksmbd-return-success-for-deferred-final-close.patch [new file with mode: 0644]
queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch [new file with mode: 0644]
queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch [new file with mode: 0644]
queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch [new file with mode: 0644]
queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch [new file with mode: 0644]
queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch [new file with mode: 0644]
queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch [new file with mode: 0644]
queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch [new file with mode: 0644]
queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch [new file with mode: 0644]
queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch [new file with mode: 0644]
queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch [new file with mode: 0644]
queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch [new file with mode: 0644]
queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch [new file with mode: 0644]
queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch [new file with mode: 0644]
queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch [new file with mode: 0644]
queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch [new file with mode: 0644]
queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch [new file with mode: 0644]
queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch [new file with mode: 0644]
queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch [new file with mode: 0644]
queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch [new file with mode: 0644]
queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch [new file with mode: 0644]
queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch [new file with mode: 0644]
queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch [new file with mode: 0644]
queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch [new file with mode: 0644]
queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch [new file with mode: 0644]
queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch [new file with mode: 0644]
queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch [new file with mode: 0644]
queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch [new file with mode: 0644]
queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch [new file with mode: 0644]
queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch [new file with mode: 0644]
queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch [new file with mode: 0644]
queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch [new file with mode: 0644]
queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch [new file with mode: 0644]
queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch [new file with mode: 0644]
queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci-add-initial-support-for-past.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch [new file with mode: 0644]
queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch [new file with mode: 0644]
queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch [new file with mode: 0644]
queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch [new file with mode: 0644]
queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch [new file with mode: 0644]
queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch [new file with mode: 0644]
queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch [new file with mode: 0644]
queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch [new file with mode: 0644]
queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch [new file with mode: 0644]
queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch [new file with mode: 0644]
queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch [new file with mode: 0644]
queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch [new file with mode: 0644]
queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch [new file with mode: 0644]
queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch [new file with mode: 0644]
queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch [new file with mode: 0644]
queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch [new file with mode: 0644]
queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch [new file with mode: 0644]
queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch [new file with mode: 0644]
queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch [new file with mode: 0644]
queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch [new file with mode: 0644]
queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch [new file with mode: 0644]
queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch [new file with mode: 0644]
queue-6.18/idpf-adjust-txq-ring-count-minimum.patch [new file with mode: 0644]
queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch [new file with mode: 0644]
queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch [new file with mode: 0644]
queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch [new file with mode: 0644]
queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch [new file with mode: 0644]
queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch [new file with mode: 0644]
queue-6.18/ipvs-fix-the-checksum-validations.patch [new file with mode: 0644]
queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch [new file with mode: 0644]
queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch [new file with mode: 0644]
queue-6.18/ksmbd-return-success-for-deferred-final-close.patch [new file with mode: 0644]
queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch [new file with mode: 0644]
queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch [new file with mode: 0644]
queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch [new file with mode: 0644]
queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch [new file with mode: 0644]
queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch [new file with mode: 0644]
queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch [new file with mode: 0644]
queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch [new file with mode: 0644]
queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch [new file with mode: 0644]
queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch [new file with mode: 0644]
queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch [new file with mode: 0644]
queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch [new file with mode: 0644]
queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch [new file with mode: 0644]
queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch [new file with mode: 0644]
queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch [new file with mode: 0644]
queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch [new file with mode: 0644]
queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch [new file with mode: 0644]
queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch [new file with mode: 0644]
queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch [new file with mode: 0644]
queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch [new file with mode: 0644]
queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch [new file with mode: 0644]
queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch [new file with mode: 0644]
queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch [new file with mode: 0644]
queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch [new file with mode: 0644]
queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch [new file with mode: 0644]
queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch [new file with mode: 0644]
queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch [new file with mode: 0644]
queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch [new file with mode: 0644]
queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch [new file with mode: 0644]
queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch [new file with mode: 0644]
queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch [new file with mode: 0644]
queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch [new file with mode: 0644]
queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch [new file with mode: 0644]
queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch [new file with mode: 0644]
queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch [new file with mode: 0644]
queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch [new file with mode: 0644]
queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch [new file with mode: 0644]
queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch [new file with mode: 0644]
queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch [new file with mode: 0644]
queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch [new file with mode: 0644]
queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch [new file with mode: 0644]
queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch [new file with mode: 0644]
queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch [new file with mode: 0644]
queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch [new file with mode: 0644]
queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch [new file with mode: 0644]
queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch [new file with mode: 0644]
queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch [new file with mode: 0644]
queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch [new file with mode: 0644]
queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch [new file with mode: 0644]
queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch [new file with mode: 0644]
queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch [new file with mode: 0644]
queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch [new file with mode: 0644]
queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch [new file with mode: 0644]
queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch [new file with mode: 0644]
queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch [new file with mode: 0644]
queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch [new file with mode: 0644]
queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch [new file with mode: 0644]
queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch [new file with mode: 0644]
queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch [new file with mode: 0644]
queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch [new file with mode: 0644]
queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch [new file with mode: 0644]
queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch [new file with mode: 0644]
queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch [new file with mode: 0644]
queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch [new file with mode: 0644]
queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch [new file with mode: 0644]
queue-6.6/ipvs-fix-the-checksum-validations.patch [new file with mode: 0644]
queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch [new file with mode: 0644]
queue-6.6/ksmbd-return-success-for-deferred-final-close.patch [new file with mode: 0644]
queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch [new file with mode: 0644]
queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch [new file with mode: 0644]
queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch [new file with mode: 0644]
queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch [new file with mode: 0644]
queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch [new file with mode: 0644]
queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch [new file with mode: 0644]
queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch [new file with mode: 0644]
queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch [new file with mode: 0644]
queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch [new file with mode: 0644]
queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch [new file with mode: 0644]
queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch [new file with mode: 0644]
queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-6.6/series
queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch [new file with mode: 0644]
queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch [new file with mode: 0644]
queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch [new file with mode: 0644]
queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch [new file with mode: 0644]
queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch [new file with mode: 0644]
queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch [new file with mode: 0644]
queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch [new file with mode: 0644]
queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch [new file with mode: 0644]
queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch [new file with mode: 0644]
queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch [new file with mode: 0644]
queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch [new file with mode: 0644]
queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch [new file with mode: 0644]
queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch [new file with mode: 0644]
queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch [new file with mode: 0644]
queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch [new file with mode: 0644]
queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch [new file with mode: 0644]
queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch [new file with mode: 0644]
queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch [new file with mode: 0644]
queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch [new file with mode: 0644]
queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch [new file with mode: 0644]
queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch [new file with mode: 0644]
queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch [new file with mode: 0644]
queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch [new file with mode: 0644]
queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch [new file with mode: 0644]
queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch [new file with mode: 0644]
queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch [new file with mode: 0644]
queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch [new file with mode: 0644]
queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch [new file with mode: 0644]
queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch [new file with mode: 0644]
queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch [new file with mode: 0644]
queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch [new file with mode: 0644]
queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch [new file with mode: 0644]
queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch [new file with mode: 0644]
queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch [new file with mode: 0644]
queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch [new file with mode: 0644]
queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch [new file with mode: 0644]
queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch [new file with mode: 0644]
queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch [new file with mode: 0644]
queue-7.1/erofs-remove-fscache-backend-entirely.patch [new file with mode: 0644]
queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch [new file with mode: 0644]
queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch [new file with mode: 0644]
queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch [new file with mode: 0644]
queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch [new file with mode: 0644]
queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch [new file with mode: 0644]
queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch [new file with mode: 0644]
queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch [new file with mode: 0644]
queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch [new file with mode: 0644]
queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch [new file with mode: 0644]
queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch [new file with mode: 0644]
queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch [new file with mode: 0644]
queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch [new file with mode: 0644]
queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch [new file with mode: 0644]
queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch [new file with mode: 0644]
queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch [new file with mode: 0644]
queue-7.1/idpf-adjust-txq-ring-count-minimum.patch [new file with mode: 0644]
queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch [new file with mode: 0644]
queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch [new file with mode: 0644]
queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch [new file with mode: 0644]
queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch [new file with mode: 0644]
queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch [new file with mode: 0644]
queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch [new file with mode: 0644]
queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch [new file with mode: 0644]
queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch [new file with mode: 0644]
queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch [new file with mode: 0644]
queue-7.1/ipvs-fix-the-checksum-validations.patch [new file with mode: 0644]
queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch [new file with mode: 0644]
queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch [new file with mode: 0644]
queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch [new file with mode: 0644]
queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch [new file with mode: 0644]
queue-7.1/ksmbd-return-success-for-deferred-final-close.patch [new file with mode: 0644]
queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch [new file with mode: 0644]
queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch [new file with mode: 0644]
queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch [new file with mode: 0644]
queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch [new file with mode: 0644]
queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch [new file with mode: 0644]
queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch [new file with mode: 0644]
queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch [new file with mode: 0644]
queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch [new file with mode: 0644]
queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch [new file with mode: 0644]
queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch [new file with mode: 0644]
queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch [new file with mode: 0644]
queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch [new file with mode: 0644]
queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch [new file with mode: 0644]
queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch [new file with mode: 0644]
queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch [new file with mode: 0644]
queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch [new file with mode: 0644]
queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch [new file with mode: 0644]
queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch [new file with mode: 0644]
queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch [new file with mode: 0644]
queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch [new file with mode: 0644]
queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch [new file with mode: 0644]
queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch [new file with mode: 0644]
queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch [new file with mode: 0644]
queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch [new file with mode: 0644]
queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch [new file with mode: 0644]
queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch [new file with mode: 0644]
queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch [new file with mode: 0644]
queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch [new file with mode: 0644]
queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch [new file with mode: 0644]
queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch [new file with mode: 0644]
queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch [new file with mode: 0644]
queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch [new file with mode: 0644]
queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch [new file with mode: 0644]
queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch [new file with mode: 0644]
queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch [new file with mode: 0644]
queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch [new file with mode: 0644]
queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch [new file with mode: 0644]
queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch [new file with mode: 0644]
queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch [new file with mode: 0644]
queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch [new file with mode: 0644]
queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch [new file with mode: 0644]
queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch [new file with mode: 0644]
queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch [new file with mode: 0644]
queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch [new file with mode: 0644]
queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch [new file with mode: 0644]
queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch [new file with mode: 0644]
queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch [new file with mode: 0644]
queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch [new file with mode: 0644]
queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch [new file with mode: 0644]
queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch [new file with mode: 0644]
queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch [new file with mode: 0644]
queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch [new file with mode: 0644]
queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch [new file with mode: 0644]
queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch [new file with mode: 0644]
queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch [new file with mode: 0644]
queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch [new file with mode: 0644]
queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch [new file with mode: 0644]
queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch [new file with mode: 0644]
queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch [new file with mode: 0644]
queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch [new file with mode: 0644]
queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch [new file with mode: 0644]
queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch [new file with mode: 0644]
queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch [new file with mode: 0644]
queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch [new file with mode: 0644]
queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch [new file with mode: 0644]
queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch [new file with mode: 0644]
queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch [new file with mode: 0644]
queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch [new file with mode: 0644]
queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch [new file with mode: 0644]
queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch [new file with mode: 0644]
queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch [new file with mode: 0644]
queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch [new file with mode: 0644]
queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch [new file with mode: 0644]
queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch [new file with mode: 0644]
queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch [new file with mode: 0644]
queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch [new file with mode: 0644]
queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch [new file with mode: 0644]
queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch [new file with mode: 0644]
queue-7.1/series
queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch [new file with mode: 0644]
queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch [new file with mode: 0644]
queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch [new file with mode: 0644]
queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch [new file with mode: 0644]
queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch [new file with mode: 0644]
queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch [new file with mode: 0644]
queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch [new file with mode: 0644]
queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch [new file with mode: 0644]
queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch [new file with mode: 0644]
queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch [new file with mode: 0644]
queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch [new file with mode: 0644]
queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch [new file with mode: 0644]

diff --git a/queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..293c6c0
--- /dev/null
@@ -0,0 +1,61 @@
+From 745281a66272d2931ea55d12bd01bf916506f005 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index c7df685be4ea5..531cd172cb5cb 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2393,8 +2393,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..ec5217f
--- /dev/null
@@ -0,0 +1,61 @@
+From c4e521d616202dba019c1f1ce6ec73084a20174b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index 9bdc6392382a6..c0daa18e7d8f9 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1990,8 +1990,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..692f506
--- /dev/null
@@ -0,0 +1,66 @@
+From 60838c5fd4de2d701273702903b0f3d45a681b28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index b537a83678e11..f700986830190 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..71b9dbf
--- /dev/null
@@ -0,0 +1,60 @@
+From 8a8ad74d3a3c06c5b31f175052e52dcaa17ffabf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 6649532712f4a..c9c3bef3ae895 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -5744,6 +5744,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+               goto unlock;
+       }
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -5789,6 +5793,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+ unlock:
+       mutex_unlock(&conn->chan_lock);
+-- 
+2.53.0
+
diff --git a/queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..ac75be9
--- /dev/null
@@ -0,0 +1,57 @@
+From b17c6285bba195e62cb850953701b248badb4bcf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index d493b66ae8d29..f23862b465ca6 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1827,13 +1827,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol\n");
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..bbd7d10
--- /dev/null
@@ -0,0 +1,60 @@
+From deb6b2b8a5c22e795066ee0ff958def1efe2e576 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index f5f9c86c50bc2..d846309921a7e 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -895,16 +895,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..e716c13
--- /dev/null
@@ -0,0 +1,47 @@
+From db8078eedeff5b6c2f1e9aad57a65d9dcffe156f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index e14dd1051e58c..f39b7fcfcd38a 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch b/queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch
new file mode 100644 (file)
index 0000000..eac7354
--- /dev/null
@@ -0,0 +1,834 @@
+From 90c75123f9cffaceedd7614a827c214380bd53a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 26 Aug 2021 14:41:19 +1200
+Subject: hwmon: (adt7470) Convert to use regmap
+
+From: Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+[ Upstream commit ef67959c42539bfece4d7c4335c07656703bb027 ]
+
+Convert the adt7470 to using regmap which allows better error handling.
+
+Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Link: https://lore.kernel.org/r/20210826024121.15665-3-chris.packham@alliedtelesis.co.nz
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 414 +++++++++++++++++++++++-----------------
+ 1 file changed, 241 insertions(+), 173 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 95c5e219adeff..274d38a9ba589 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -18,6 +18,7 @@
+ #include <linux/delay.h>
+ #include <linux/log2.h>
+ #include <linux/kthread.h>
++#include <linux/regmap.h>
+ #include <linux/slab.h>
+ #include <linux/util_macros.h>
+ #include <linux/sched.h>
+@@ -36,7 +37,10 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ADT7470_REG_PWM_MAX_BASE_ADDR         0x38
+ #define ADT7470_REG_PWM_MAX_MAX_ADDR          0x3B
+ #define ADT7470_REG_CFG                               0x40
++#define               ADT7470_STRT_MASK               0x01
++#define               ADT7470_TEST_MASK               0x02
+ #define               ADT7470_FSPD_MASK               0x04
++#define               ADT7470_T05_STB_MASK            0x80
+ #define ADT7470_REG_ALARM1                    0x41
+ #define               ADT7470_R1T_ALARM               0x01
+ #define               ADT7470_R2T_ALARM               0x02
+@@ -138,7 +142,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ADT7470_FREQ_SHIFT    4
+ struct adt7470_data {
+-      struct i2c_client       *client;
++      struct regmap           *regmap;
+       struct mutex            lock;
+       char                    sensors_valid;
+       char                    limits_valid;
+@@ -172,52 +176,76 @@ struct adt7470_data {
+  * 16-bit registers on the ADT7470 are low-byte first.  The data sheet says
+  * that the low byte must be read before the high byte.
+  */
+-static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg)
++static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg,
++                                       unsigned int *val)
+ {
+-      u16 foo;
++      u8 regval[2];
++      int err;
++
++      err = regmap_bulk_read(data->regmap, reg, &regval, 2);
++      if (err < 0)
++              return err;
++
++      *val = regval[0] | (regval[1] << 8);
+-      foo = i2c_smbus_read_byte_data(client, reg);
+-      foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
+-      return foo;
++      return 0;
+ }
+-static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg,
+-                                        u16 value)
++static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg,
++                                        unsigned int val)
+ {
+-      return i2c_smbus_write_byte_data(client, reg, value & 0xFF)
+-             || i2c_smbus_write_byte_data(client, reg + 1, value >> 8);
++      u8 regval[2];
++
++      regval[0] = val & 0xFF;
++      regval[1] = val >> 8;
++
++      return regmap_bulk_write(data->regmap, reg, &regval, 2);
+ }
+ /* Probe for temperature sensors.  Assumes lock is held */
+-static int adt7470_read_temperatures(struct i2c_client *client,
+-                                   struct adt7470_data *data)
++static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+       unsigned long res;
++      unsigned int pwm_cfg[2];
++      int err;
+       int i;
+-      u8 cfg, pwm[4], pwm_cfg[2];
++      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+-      pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0));
+-      pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2));
++      err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
++      if (err < 0)
++              return err;
++      err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]);
++      if (err < 0)
++              return err;
+       /* set manual pwm to whatever it is set to now */
+-      for (i = 0; i < ADT7470_FAN_COUNT; i++)
+-              pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i));
++      err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
++                             ADT7470_PWM_COUNT);
++      if (err < 0)
++              return err;
+       /* put pwm in manual mode */
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0),
+-              pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK));
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2),
+-              pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK));
++      err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0),
++                               ADT7470_PWM_AUTO_MASK, 0);
++      if (err < 0)
++              return err;
++      err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
++                               ADT7470_PWM_AUTO_MASK, 0);
++      if (err < 0)
++              return err;
+       /* write pwm control to whatever it was */
+-      for (i = 0; i < ADT7470_FAN_COUNT; i++)
+-              i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]);
++      err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
++                              ADT7470_PWM_COUNT);
++      if (err < 0)
++              return err;
+       /* start reading temperature sensors */
+-      cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+-      cfg |= 0x80;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
++                               ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
++      if (err < 0)
++              return err;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -225,26 +253,31 @@ static int adt7470_read_temperatures(struct i2c_client *client,
+                                   TEMP_COLLECTION_TIME));
+       /* done reading temperature sensors */
+-      cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+-      cfg &= ~0x80;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
++                               ADT7470_T05_STB_MASK, 0);
++      if (err < 0)
++              return err;
+       /* restore pwm[1-4] config registers */
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
+-
+-      if (res) {
+-              pr_err("ha ha, interrupted\n");
++      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err < 0)
++              return err;
++      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err < 0)
++              return err;
++
++      if (res)
+               return -EAGAIN;
+-      }
+       /* Only count fans if we have to */
+       if (data->num_temp_sensors >= 0)
+               return 0;
++      err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0],
++                             ADT7470_TEMP_COUNT);
++      if (err < 0)
++              return err;
+       for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
+-              data->temp[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_TEMP_REG(i));
+               if (data->temp[i])
+                       data->num_temp_sensors = i + 1;
+       }
+@@ -259,7 +292,7 @@ static int adt7470_update_thread(void *p)
+       while (!kthread_should_stop()) {
+               mutex_lock(&data->lock);
+-              adt7470_read_temperatures(client, data);
++              adt7470_read_temperatures(data);
+               mutex_unlock(&data->lock);
+               if (kthread_should_stop())
+@@ -273,89 +306,116 @@ static int adt7470_update_thread(void *p)
+ static int adt7470_update_sensors(struct adt7470_data *data)
+ {
+-      struct i2c_client *client = data->client;
+-      u8 cfg;
++      unsigned int val;
++      int err;
+       int i;
+       if (!data->temperatures_probed)
+-              adt7470_read_temperatures(client, data);
++              err = adt7470_read_temperatures(data);
+       else
+-              for (i = 0; i < ADT7470_TEMP_COUNT; i++)
+-                      data->temp[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_TEMP_REG(i));
++              err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0],
++                                     ADT7470_TEMP_COUNT);
++      if (err < 0)
++              return err;
+-      for (i = 0; i < ADT7470_FAN_COUNT; i++)
+-              data->fan[i] = adt7470_read_word_data(client,
+-                                              ADT7470_REG_FAN(i));
++      for (i = 0; i < ADT7470_FAN_COUNT; i++) {
++              err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val);
++              if (err < 0)
++                      return err;
++              data->fan[i] =  val;
++      }
+-      for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+-              int reg;
+-              int reg_mask;
++      err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT);
++      if (err < 0)
++              return err;
+-              data->pwm[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_REG_PWM(i));
++      for (i = 0; i < ADT7470_PWM_COUNT; i++) {
++              unsigned int mask;
+               if (i % 2)
+-                      reg_mask = ADT7470_PWM2_AUTO_MASK;
++                      mask = ADT7470_PWM2_AUTO_MASK;
+               else
+-                      reg_mask = ADT7470_PWM1_AUTO_MASK;
++                      mask = ADT7470_PWM1_AUTO_MASK;
+-              reg = ADT7470_REG_PWM_CFG(i);
+-              if (i2c_smbus_read_byte_data(client, reg) & reg_mask)
+-                      data->pwm_automatic[i] = 1;
+-              else
+-                      data->pwm_automatic[i] = 0;
++              err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val);
++              if (err < 0)
++                      return err;
++              data->pwm_automatic[i] = !!(val & mask);
+-              reg = ADT7470_REG_PWM_AUTO_TEMP(i);
+-              cfg = i2c_smbus_read_byte_data(client, reg);
++              err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val);
++              if (err < 0)
++                      return err;
+               if (!(i % 2))
+-                      data->pwm_auto_temp[i] = cfg >> 4;
++                      data->pwm_auto_temp[i] = val >> 4;
+               else
+-                      data->pwm_auto_temp[i] = cfg & 0xF;
++                      data->pwm_auto_temp[i] = val & 0xF;
+       }
+-      if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) &
+-          ADT7470_FSPD_MASK)
+-              data->force_pwm_max = 1;
+-      else
+-              data->force_pwm_max = 0;
++      err = regmap_read(data->regmap, ADT7470_REG_CFG, &val);
++      if (err < 0)
++              return err;
++      data->force_pwm_max = !!(val & ADT7470_FSPD_MASK);
++
++      err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val);
++      if (err < 0)
++              return err;
++      data->alarm = val;
++      if (data->alarm & ADT7470_OOL_ALARM) {
++              err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val);
++              if (err < 0)
++                      return err;
++              data->alarm |= ALARM2(val);
++      }
+-      data->alarm = i2c_smbus_read_byte_data(client, ADT7470_REG_ALARM1);
+-      if (data->alarm & ADT7470_OOL_ALARM)
+-              data->alarm |= ALARM2(i2c_smbus_read_byte_data(client,
+-                                                      ADT7470_REG_ALARM2));
+-      data->alarms_mask = adt7470_read_word_data(client,
+-                                                 ADT7470_REG_ALARM1_MASK);
++      err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val);
++      if (err < 0)
++              return err;
++      data->alarms_mask = val;
+       return 0;
+ }
+ static int adt7470_update_limits(struct adt7470_data *data)
+ {
+-      struct i2c_client *client = data->client;
++      unsigned int val;
++      int err;
+       int i;
+       for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
+-              data->temp_min[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_TEMP_MIN_REG(i));
+-              data->temp_max[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_TEMP_MAX_REG(i));
++              err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val);
++              if (err < 0)
++                      return err;
++              data->temp_min[i] = (s8)val;
++              err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val);
++              if (err < 0)
++                      return err;
++              data->temp_max[i] = (s8)val;
+       }
+       for (i = 0; i < ADT7470_FAN_COUNT; i++) {
+-              data->fan_min[i] = adt7470_read_word_data(client,
+-                                              ADT7470_REG_FAN_MIN(i));
+-              data->fan_max[i] = adt7470_read_word_data(client,
+-                                              ADT7470_REG_FAN_MAX(i));
++              err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val);
++              if (err < 0)
++                      return err;
++              data->fan_min[i] = val;
++              err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val);
++              if (err < 0)
++                      return err;
++              data->fan_max[i] = val;
+       }
+       for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+-              data->pwm_max[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_REG_PWM_MAX(i));
+-              data->pwm_min[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_REG_PWM_MIN(i));
+-              data->pwm_tmin[i] = i2c_smbus_read_byte_data(client,
+-                                              ADT7470_REG_PWM_TMIN(i));
++              err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val);
++              if (err < 0)
++                      return err;
++              data->pwm_max[i] = val;
++              err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val);
++              if (err < 0)
++                      return err;
++              data->pwm_min[i] = val;
++              err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val);
++              if (err < 0)
++                      return err;
++              data->pwm_tmin[i] = (s8)val;
+       }
+       return 0;
+@@ -491,8 +551,8 @@ static ssize_t temp_min_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -502,11 +562,11 @@ static ssize_t temp_min_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->temp_min[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index),
++      err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t temp_max_show(struct device *dev,
+@@ -527,8 +587,8 @@ static ssize_t temp_max_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -538,11 +598,10 @@ static ssize_t temp_max_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->temp_max[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index),
+-                                temp);
++      err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(attr->index), temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+@@ -575,6 +634,7 @@ static ssize_t alarm_mask_store(struct device *dev,
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       long mask;
++      int err;
+       if (kstrtoul(buf, 0, &mask))
+               return -EINVAL;
+@@ -584,10 +644,10 @@ static ssize_t alarm_mask_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->alarms_mask = mask;
+-      adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask);
++      err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t fan_max_show(struct device *dev,
+@@ -612,8 +672,8 @@ static ssize_t fan_max_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp) || !temp)
+               return -EINVAL;
+@@ -623,10 +683,10 @@ static ssize_t fan_max_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->fan_max[attr->index] = temp;
+-      adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp);
++      err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(attr->index), temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t fan_min_show(struct device *dev,
+@@ -651,8 +711,8 @@ static ssize_t fan_min_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp) || !temp)
+               return -EINVAL;
+@@ -662,10 +722,10 @@ static ssize_t fan_min_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->fan_min[attr->index] = temp;
+-      adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp);
++      err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(attr->index), temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
+@@ -700,24 +760,20 @@ static ssize_t force_pwm_max_store(struct device *dev,
+                                  const char *buf, size_t count)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
+-      u8 reg;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+       mutex_lock(&data->lock);
+       data->force_pwm_max = temp;
+-      reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+-      if (temp)
+-              reg |= ADT7470_FSPD_MASK;
+-      else
+-              reg &= ~ADT7470_FSPD_MASK;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
++                               ADT7470_FSPD_MASK,
++                               temp ? ADT7470_FSPD_MASK : 0);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
+@@ -737,8 +793,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -747,10 +803,10 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr,
+       mutex_lock(&data->lock);
+       data->pwm[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp);
++      err = regmap_write(data->regmap, ADT7470_REG_PWM(attr->index), temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+@@ -762,13 +818,20 @@ static ssize_t pwm1_freq_show(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
+-      unsigned char cfg_reg_1;
+-      unsigned char cfg_reg_2;
++      unsigned int cfg_reg_1, cfg_reg_2;
+       int index;
++      int err;
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
+       mutex_lock(&data->lock);
+-      cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+-      cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
++      err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1);
++      if (err < 0)
++              goto out;
++      err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2);
++      if (err < 0)
++              goto out;
+       mutex_unlock(&data->lock);
+       index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
+@@ -778,6 +841,10 @@ static ssize_t pwm1_freq_show(struct device *dev,
+               index = ARRAY_SIZE(adt7470_freq_map) - 1;
+       return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
++
++out:
++      mutex_unlock(&data->lock);
++      return err;
+ }
+ static ssize_t pwm1_freq_store(struct device *dev,
+@@ -785,11 +852,10 @@ static ssize_t pwm1_freq_store(struct device *dev,
+                              const char *buf, size_t count)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long freq;
+       int index;
+       int low_freq = ADT7470_CFG_LF;
+-      unsigned char val;
++      int err;
+       if (kstrtol(buf, 10, &freq))
+               return -EINVAL;
+@@ -805,16 +871,19 @@ static ssize_t pwm1_freq_store(struct device *dev,
+       mutex_lock(&data->lock);
+       /* Configuration Register 1 */
+-      val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+-                                (val & ~ADT7470_CFG_LF) | low_freq);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
++                               ADT7470_CFG_LF, low_freq);
++      if (err < 0)
++              goto out;
++
+       /* Configuration Register 2 */
+-      val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+-              (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
++                               ADT7470_FREQ_MASK,
++                               index << ADT7470_FREQ_SHIFT);
++out:
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_max_show(struct device *dev,
+@@ -835,8 +904,8 @@ static ssize_t pwm_max_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -845,11 +914,11 @@ static ssize_t pwm_max_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->pwm_max[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index),
+-                                temp);
++      err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index),
++                         temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_min_show(struct device *dev,
+@@ -870,8 +939,8 @@ static ssize_t pwm_min_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -880,11 +949,11 @@ static ssize_t pwm_min_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->pwm_min[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index),
+-                                temp);
++      err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index),
++                         temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_tmax_show(struct device *dev,
+@@ -918,8 +987,8 @@ static ssize_t pwm_tmin_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       long temp;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -929,11 +998,11 @@ static ssize_t pwm_tmin_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->pwm_tmin[attr->index] = temp;
+-      i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index),
+-                                temp);
++      err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index),
++                         temp);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_auto_show(struct device *dev,
+@@ -954,11 +1023,9 @@ static ssize_t pwm_auto_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+-      int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index);
+       int pwm_auto_reg_mask;
+       long temp;
+-      u8 reg;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -974,15 +1041,12 @@ static ssize_t pwm_auto_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->pwm_automatic[attr->index] = temp;
+-      reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
+-      if (temp)
+-              reg |= pwm_auto_reg_mask;
+-      else
+-              reg &= ~pwm_auto_reg_mask;
+-      i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(attr->index),
++                               pwm_auto_reg_mask,
++                               temp ? pwm_auto_reg_mask : 0);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t pwm_auto_temp_show(struct device *dev,
+@@ -1017,10 +1081,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = dev_get_drvdata(dev);
+-      struct i2c_client *client = data->client;
+       int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index);
++      unsigned int mask, val;
+       long temp;
+-      u8 reg;
++      int err;
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+@@ -1031,20 +1095,19 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       mutex_lock(&data->lock);
+       data->pwm_automatic[attr->index] = temp;
+-      reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
+       if (!(attr->index % 2)) {
+-              reg &= 0xF;
+-              reg |= (temp << 4) & 0xF0;
++              mask = 0xF0;
++              val = (temp << 4) & 0xF0;
+       } else {
+-              reg &= 0xF0;
+-              reg |= temp & 0xF;
++              mask = 0x0F;
++              val = temp & 0x0F;
+       }
+-      i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
++      err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
+       mutex_unlock(&data->lock);
+-      return count;
++      return err < 0 ? err : count;
+ }
+ static ssize_t alarm_show(struct device *dev,
+@@ -1053,6 +1116,9 @@ static ssize_t alarm_show(struct device *dev,
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       if (data->alarm & attr->index)
+               return sprintf(buf, "1\n");
+       else
+@@ -1288,23 +1354,19 @@ static int adt7470_detect(struct i2c_client *client,
+       return 0;
+ }
+-static void adt7470_init_client(struct i2c_client *client)
+-{
+-      int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+-
+-      if (reg < 0) {
+-              dev_err(&client->dev, "cannot read configuration register\n");
+-      } else {
+-              /* start monitoring (and do a self-test) */
+-              i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3);
+-      }
+-}
++static const struct regmap_config adt7470_regmap_config = {
++      .reg_bits = 8,
++      .val_bits = 8,
++      .use_single_read = true,
++      .use_single_write = true,
++};
+ static int adt7470_probe(struct i2c_client *client)
+ {
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+       if (!data)
+@@ -1312,15 +1374,21 @@ static int adt7470_probe(struct i2c_client *client)
+       data->num_temp_sensors = -1;
+       data->auto_update_interval = AUTO_UPDATE_INTERVAL;
++      data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config);
++      if (IS_ERR(data->regmap))
++              return PTR_ERR(data->regmap);
+       i2c_set_clientdata(client, data);
+-      data->client = client;
+       mutex_init(&data->lock);
+       dev_info(&client->dev, "%s chip found\n", client->name);
+       /* Initialize the ADT7470 chip */
+-      adt7470_init_client(client);
++      err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
++                               ADT7470_STRT_MASK | ADT7470_TEST_MASK,
++                               ADT7470_STRT_MASK | ADT7470_TEST_MASK);
++      if (err < 0)
++              return err;
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch b/queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch
new file mode 100644 (file)
index 0000000..ce3fd0f
--- /dev/null
@@ -0,0 +1,333 @@
+From 12f0b6dc7d732afd9f7b6e4570fe31bcfd6423d9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 20 Oct 2020 11:34:22 +1300
+Subject: hwmon: (adt7470) Create functions for updating readings and limits
+
+From: Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+[ Upstream commit ad00a02e34b481396938c5fa62ee642bff7fbb08 ]
+
+Split the body of adt7470_update_device() into two helper functions
+adt7470_update_sensors() and adt7470_update_limits(). Although neither
+of the new helpers returns an error yet lay the groundwork for
+propagating failures through to the sysfs readers.
+
+Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Link: https://lore.kernel.org/r/20201019223423.31488-2-chris.packham@alliedtelesis.co.nz
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 154 ++++++++++++++++++++++++++++++----------
+ 1 file changed, 118 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 23a447c1f0cf0..b8697975d8b6c 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -270,37 +270,11 @@ static int adt7470_update_thread(void *p)
+       return 0;
+ }
+-static struct adt7470_data *adt7470_update_device(struct device *dev)
++static int adt7470_update_sensors(struct adt7470_data *data)
+ {
+-      struct adt7470_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+-      unsigned long local_jiffies = jiffies;
+       u8 cfg;
+       int i;
+-      int need_sensors = 1;
+-      int need_limits = 1;
+-
+-      /*
+-       * Figure out if we need to update the shadow registers.
+-       * Lockless means that we may occasionally report out of
+-       * date data.
+-       */
+-      if (time_before(local_jiffies, data->sensors_last_updated +
+-                      SENSOR_REFRESH_INTERVAL) &&
+-          data->sensors_valid)
+-              need_sensors = 0;
+-
+-      if (time_before(local_jiffies, data->limits_last_updated +
+-                      LIMIT_REFRESH_INTERVAL) &&
+-          data->limits_valid)
+-              need_limits = 0;
+-
+-      if (!need_sensors && !need_limits)
+-              return data;
+-
+-      mutex_lock(&data->lock);
+-      if (!need_sensors)
+-              goto no_sensor_update;
+       if (!data->temperatures_probed)
+               adt7470_read_temperatures(client, data);
+@@ -352,12 +326,13 @@ static struct adt7470_data *adt7470_update_device(struct device *dev)
+       data->alarms_mask = adt7470_read_word_data(client,
+                                                  ADT7470_REG_ALARM1_MASK);
+-      data->sensors_last_updated = local_jiffies;
+-      data->sensors_valid = 1;
++      return 0;
++}
+-no_sensor_update:
+-      if (!need_limits)
+-              goto out;
++static int adt7470_update_limits(struct adt7470_data *data)
++{
++      struct i2c_client *client = data->client;
++      int i;
+       for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
+               data->temp_min[i] = i2c_smbus_read_byte_data(client,
+@@ -382,12 +357,55 @@ static struct adt7470_data *adt7470_update_device(struct device *dev)
+                                               ADT7470_REG_PWM_TMIN(i));
+       }
+-      data->limits_last_updated = local_jiffies;
+-      data->limits_valid = 1;
++      return 0;
++}
++static struct adt7470_data *adt7470_update_device(struct device *dev)
++{
++      struct adt7470_data *data = dev_get_drvdata(dev);
++      unsigned long local_jiffies = jiffies;
++      int need_sensors = 1;
++      int need_limits = 1;
++      int err;
++
++      /*
++       * Figure out if we need to update the shadow registers.
++       * Lockless means that we may occasionally report out of
++       * date data.
++       */
++      if (time_before(local_jiffies, data->sensors_last_updated +
++                      SENSOR_REFRESH_INTERVAL) &&
++          data->sensors_valid)
++              need_sensors = 0;
++
++      if (time_before(local_jiffies, data->limits_last_updated +
++                      LIMIT_REFRESH_INTERVAL) &&
++          data->limits_valid)
++              need_limits = 0;
++
++      if (!need_sensors && !need_limits)
++              return data;
++
++      mutex_lock(&data->lock);
++      if (need_sensors) {
++              err = adt7470_update_sensors(data);
++              if (err < 0)
++                      goto out;
++              data->sensors_last_updated = local_jiffies;
++              data->sensors_valid = 1;
++      }
++
++      if (need_limits) {
++              err = adt7470_update_limits(data);
++              if (err < 0)
++                      goto out;
++              data->limits_last_updated = local_jiffies;
++              data->limits_valid = 1;
++      }
+ out:
+       mutex_unlock(&data->lock);
+-      return data;
++
++      return err < 0 ? ERR_PTR(err) : data;
+ }
+ static ssize_t auto_update_interval_show(struct device *dev,
+@@ -395,6 +413,10 @@ static ssize_t auto_update_interval_show(struct device *dev,
+                                        char *buf)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->auto_update_interval);
+ }
+@@ -422,6 +444,10 @@ static ssize_t num_temp_sensors_show(struct device *dev,
+                                    char *buf)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->num_temp_sensors);
+ }
+@@ -451,6 +477,10 @@ static ssize_t temp_min_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]);
+ }
+@@ -483,6 +513,10 @@ static ssize_t temp_max_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]);
+ }
+@@ -515,6 +549,10 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]);
+ }
+@@ -524,6 +562,9 @@ static ssize_t alarm_mask_show(struct device *dev,
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%x\n", data->alarms_mask);
+ }
+@@ -554,6 +595,9 @@ static ssize_t fan_max_show(struct device *dev,
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       if (FAN_DATA_VALID(data->fan_max[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan_max[attr->index]));
+@@ -590,6 +634,9 @@ static ssize_t fan_min_show(struct device *dev,
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       if (FAN_DATA_VALID(data->fan_min[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan_min[attr->index]));
+@@ -626,6 +673,9 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       if (FAN_DATA_VALID(data->fan[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan[attr->index]));
+@@ -637,6 +687,10 @@ static ssize_t force_pwm_max_show(struct device *dev,
+                                 struct device_attribute *devattr, char *buf)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->force_pwm_max);
+ }
+@@ -670,6 +724,10 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->pwm[attr->index]);
+ }
+@@ -763,6 +821,10 @@ static ssize_t pwm_max_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->pwm_max[attr->index]);
+ }
+@@ -794,6 +856,10 @@ static ssize_t pwm_min_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
+ }
+@@ -825,6 +891,10 @@ static ssize_t pwm_tmax_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       /* the datasheet says that tmax = tmin + 20C */
+       return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index]));
+ }
+@@ -834,6 +904,10 @@ static ssize_t pwm_tmin_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]);
+ }
+@@ -866,6 +940,10 @@ static ssize_t pwm_auto_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++
+       return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]);
+ }
+@@ -911,8 +989,12 @@ static ssize_t pwm_auto_temp_show(struct device *dev,
+ {
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+-      u8 ctrl = data->pwm_auto_temp[attr->index];
++      u8 ctrl;
++
++      if (IS_ERR(data))
++              return PTR_ERR(data);
++      ctrl = data->pwm_auto_temp[attr->index];
+       if (ctrl)
+               return sprintf(buf, "%d\n", 1 << (ctrl - 1));
+       else
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..a9c9277
--- /dev/null
@@ -0,0 +1,51 @@
+From addfcd4619cb8eb128cee5d4f3f4bf95a875ed98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 71e357956ce4c..8450737dd4bde 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -408,7 +408,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..38b4bb4
--- /dev/null
@@ -0,0 +1,57 @@
+From cbc8ebee5894b413583fdb7ab18f8f86fcfcea7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 274d38a9ba589..c8f6b9af0f139 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1093,8 +1093,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1105,6 +1107,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-fix-some-style-issues.patch b/queue-5.10/hwmon-adt7470-fix-some-style-issues.patch
new file mode 100644 (file)
index 0000000..4780985
--- /dev/null
@@ -0,0 +1,60 @@
+From 61ea7b201743520391ebebfb2e420b7e822d4104 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 26 Aug 2021 14:41:18 +1200
+Subject: hwmon: (adt7470) Fix some style issues
+
+From: Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+[ Upstream commit 23bd022aa6182367e1add7b4d05777cdba283756 ]
+
+In preparation for the changes that follow fix up some existing style
+issues.
+Specifically:
+- add blank line between variable declaration and code
+- use strscpy instead of strlcpy
+- remove unnecessary braces
+
+Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Link: https://lore.kernel.org/r/20210826024121.15665-2-chris.packham@alliedtelesis.co.nz
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index b8697975d8b6c..95c5e219adeff 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -175,6 +175,7 @@ struct adt7470_data {
+ static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg)
+ {
+       u16 foo;
++
+       foo = i2c_smbus_read_byte_data(client, reg);
+       foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
+       return foo;
+@@ -1282,7 +1283,7 @@ static int adt7470_detect(struct i2c_client *client,
+       if (revision != ADT7470_REVISION)
+               return -ENODEV;
+-      strlcpy(info->type, "adt7470", I2C_NAME_SIZE);
++      strscpy(info->type, "adt7470", I2C_NAME_SIZE);
+       return 0;
+ }
+@@ -1331,9 +1332,8 @@ static int adt7470_probe(struct i2c_client *client)
+       data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
+                                       dev_name(hwmon_dev));
+-      if (IS_ERR(data->auto_update)) {
++      if (IS_ERR(data->auto_update))
+               return PTR_ERR(data->auto_update);
+-      }
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..a66e459
--- /dev/null
@@ -0,0 +1,62 @@
+From 77f9c298cedb2037a7139f64841c847c9fc5340d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 8450737dd4bde..23a447c1f0cf0 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -66,8 +66,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..4da0acf
--- /dev/null
@@ -0,0 +1,74 @@
+From 4165624dc46d0e8573d210b58f79a5e4a44cc43c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
+index 71cfc1c5bd12e..5428b5b4043da 100644
+--- a/drivers/hwmon/nct6775.c
++++ b/drivers/hwmon/nct6775.c
+@@ -851,12 +851,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..f76948c
--- /dev/null
@@ -0,0 +1,39 @@
+From fc52a806f4f5ff6392f7baaae82053db9284b82c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index e90ab980b836f..ddd8399a7feea 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -408,7 +408,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
+-- 
+2.53.0
+
diff --git a/queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..6b6680c
--- /dev/null
@@ -0,0 +1,64 @@
+From 6f0d805c38b085095225fb879ae394b417a69afa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 3f609316a096e..165ac0d38bcfc 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..62eb82c
--- /dev/null
@@ -0,0 +1,63 @@
+From d3aad9586cfc1a34b9ac977be4476efeed9577b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 165ac0d38bcfc..8b938e61ecd8d 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..b4a6061
--- /dev/null
@@ -0,0 +1,92 @@
+From 16e0b628990a88d35eb7346bc54b05b323ff445d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index cbdd01f311625..012c750d49874 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -842,8 +842,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->phy_state.interface = iface;
+@@ -866,28 +866,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->cur_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..0e1ea81
--- /dev/null
@@ -0,0 +1,48 @@
+From cb06d222dcac8d9a8cc143b29a5c4df9a8748513 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 22405744b2a36..c2333b8224c61 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1077,7 +1077,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1186,6 +1188,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..a1181e8
--- /dev/null
@@ -0,0 +1,50 @@
+From 322a6f59d892d532399635617039790d41be6fc3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 9c745d48f54b0..22405744b2a36 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..d75579e
--- /dev/null
@@ -0,0 +1,96 @@
+From 9f1d44aad084db4481eafed4c7404e2616617767 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index c620521c42bc6..dfb9708f53404 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 4326d5ea0400d..0f6147ea9e67b 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1622,7 +1622,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index 76a8bbb44951c..7687d3d15df22 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -313,7 +313,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..8acd993
--- /dev/null
@@ -0,0 +1,69 @@
+From 50499df170a23d43820095c5781123f1d061ddda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index ae0c4cd2dd1c5..df296551ba6f2 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -159,9 +159,7 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -170,15 +168,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..d02f203
--- /dev/null
@@ -0,0 +1,83 @@
+From e9a89d5243f31521d7447907fe0317643d2a7715 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 9c5cfd74a0ee4..26918fa575afe 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               vfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..0b8aa16
--- /dev/null
@@ -0,0 +1,42 @@
+From 11e44d9e9ba01497ec3f895cdad38c7ee15c3849 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index 50a36639e0ced..ca2b8ae98bd26 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -840,8 +840,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..29a2116
--- /dev/null
@@ -0,0 +1,38 @@
+From 0288d2e5dc143d90e2e1d52f25b0c2fc91bf8502 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..ec8adc4
--- /dev/null
@@ -0,0 +1,38 @@
+From 86e0c60425f0883fd9421be225d2c0b2aa5e6055 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..22210fa
--- /dev/null
@@ -0,0 +1,38 @@
+From 933b9bdfe5a4833a40672b17a638b85f251b1cc0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..ce539b3
--- /dev/null
@@ -0,0 +1,169 @@
+From 16c00fd5110a14cce11da9ca1596418f31d80c6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index 16c3f32e5ca73..a3103b2ed734f 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -113,7 +113,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -993,21 +993,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1044,6 +1029,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1052,9 +1039,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2573,9 +2568,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2605,17 +2604,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..7f91d83
--- /dev/null
@@ -0,0 +1,118 @@
+From 2a62ca0ffd50c306beca64ec3891bdd4cdcc2f59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ec45664f38767..810a9b76101f5 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index f643d1f59c3be..f23233e0a53c7 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -844,6 +844,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index b560d06e6d96d..071f2a2f514ff 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..a139164
--- /dev/null
@@ -0,0 +1,85 @@
+From 992c9437cd6093ed71784188ff32040dd3874bdf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 071f2a2f514ff..f66cbf0b9895f 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..b02d1b5
--- /dev/null
@@ -0,0 +1,56 @@
+From 9eff8256421689e36ac2f38c56ac26940d1c16c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 7e82ddce5031e..8cd20cc9809f1 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -840,7 +840,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..2f7dd65
--- /dev/null
@@ -0,0 +1,71 @@
+From 3b9bdcf73657f9eda520bf6a9b8a7a8240528626 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index 83f14b2c8804b..0aa883ab6ff02 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -722,13 +722,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               spin_lock(&conn->session->back_lock);
+               task = iscsi_itt_to_ctask(conn, hdr->itt);
+@@ -745,6 +738,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               } else
+                       rc = ISCSI_ERR_PROTO;
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..6c3e119
--- /dev/null
@@ -0,0 +1,66 @@
+From 1febe617f816473534ed9ebd292b1b4e3670c4a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index 36c2bd2016f22..d472d0ea7957b 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
index 2e649fc0592394cc8f15d224701ac41c7a05f828..0e4690b6c64d182f4f1f4e2c8fae92bddaba0a7e 100644 (file)
@@ -200,3 +200,38 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 net-qrtr-ns-limit-the-maximum-server-registration-pe.patch
 net-qrtr-ns-raise-node-count-limit-to-512.patch
 tls-separate-no-async-decryption-request-handling-fr.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-create-functions-for-updating-readings.patch
+hwmon-adt7470-fix-some-style-issues.patch
+hwmon-adt7470-convert-to-use-regmap.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
diff --git a/queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..705f253
--- /dev/null
@@ -0,0 +1,85 @@
+From 6c7562e9bf0ea8d714c5c2cace7110d2d71d068a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+index a19e5e7c7d0f4..67ff1669cab2a 100644
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -1774,8 +1774,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1887,8 +1889,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -2317,8 +2321,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..be09cc3
--- /dev/null
@@ -0,0 +1,43 @@
+From bf1ada81c1e671b72ddd48aa962e6319fefdc330 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index da2231ac13dfc..e8ae2d92a6787 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -31,6 +31,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..76bbb34
--- /dev/null
@@ -0,0 +1,61 @@
+From c1fae0f093b25893b0403c41b3e46344f922bf1b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index 3cf41870978da..4f173f86e157e 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..e707083
--- /dev/null
@@ -0,0 +1,61 @@
+From 5e47c6da7e8d3c92b880f0270d99e3d8f8f8d955 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index 736cd70be7255..909f2e2f18910 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1990,8 +1990,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..ecb9a03
--- /dev/null
@@ -0,0 +1,66 @@
+From 574af16c048482cf6920dbb7946e20de22733517 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index 70304b8f15ace..861965af48b9c 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..d2c674b
--- /dev/null
@@ -0,0 +1,60 @@
+From c6076f6b0131ef728ba13108eb7b366a087ca2f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 196b3bdca2644..913c0d04a481a 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -5734,6 +5734,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+               goto unlock;
+       }
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -5779,6 +5783,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+ unlock:
+       mutex_unlock(&conn->chan_lock);
+-- 
+2.53.0
+
diff --git a/queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..fdc5c66
--- /dev/null
@@ -0,0 +1,57 @@
+From 1cfc9e18c6a4b6f370a9fb8619d5e009b990c649 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index de6992580c38c..34939744dd412 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1827,13 +1827,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol\n");
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..0b3b60f
--- /dev/null
@@ -0,0 +1,60 @@
+From cc2b45f30c13cf436bfa67f01b3e4aaca0a0020c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 5cadd4d2b8246..fa216ef645d58 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -895,16 +895,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..e89df03
--- /dev/null
@@ -0,0 +1,51 @@
+From 0f6a792fbfc21e024f8b1e762cf923bce1b9964c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+index 27f3e91425580..993c5743192b3 100644
+--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+@@ -172,10 +172,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+-- 
+2.53.0
+
diff --git a/queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..61a5943
--- /dev/null
@@ -0,0 +1,47 @@
+From 7c62323d58e38460269df77a947ec69e10983d19 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index e7be0710220e5..dcde28af8a0dc 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6190,10 +6190,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..868e5fc
--- /dev/null
@@ -0,0 +1,51 @@
+From ef9bb63f5d8760e0cf7f2569f20e51a8e6683281 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 6aa6152e00337..0874d3684fbce 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..b66acc0
--- /dev/null
@@ -0,0 +1,83 @@
+From 916313c30a9a6dfce670ca0754a10166fb012322 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 57c78e82a5f27..6aa6152e00337 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..e35da3c
--- /dev/null
@@ -0,0 +1,80 @@
+From 5d68d9a82b697d1871b2d53bc1a215a03068b5ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 3fcc10d0d3639..394f0aba3493a 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..4d2fde0
--- /dev/null
@@ -0,0 +1,115 @@
+From e10e44ad1dfc794e4473e26ed5d4e9b2f6004d2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index c67cd037a93fd..57c78e82a5f27 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..9131f21
--- /dev/null
@@ -0,0 +1,57 @@
+From 678bec18a9226b2e37b8f0df11b04809b8ed4972 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 394f0aba3493a..e4be1750d9b60 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..2273221
--- /dev/null
@@ -0,0 +1,62 @@
+From de570a25c13e2da344bd92a0f2ec1c266713b2fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 68658cb566a1a..8226213f768d5 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..89a3a28
--- /dev/null
@@ -0,0 +1,71 @@
+From cf20052fa4b1f94fe50968b1a2b69fdd96ee3454 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 0874d3684fbce..68658cb566a1a 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..eda88e9
--- /dev/null
@@ -0,0 +1,111 @@
+From 34e6cc94a320c2d27679f99f01275b1822d35ece Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 8226213f768d5..3fcc10d0d3639 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..f1def27
--- /dev/null
@@ -0,0 +1,74 @@
+From 6469377cc6c7c1301d15058cc066d01f7537cd17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
+index 71cfc1c5bd12e..5428b5b4043da 100644
+--- a/drivers/hwmon/nct6775.c
++++ b/drivers/hwmon/nct6775.c
+@@ -851,12 +851,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..7016625
--- /dev/null
@@ -0,0 +1,39 @@
+From ec2bf7ef570fa7c797519f2d377fa458eeb707af Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index 1ef214a8a01b7..84497fd177d81 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -419,7 +419,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+-- 
+2.53.0
+
diff --git a/queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..c13409d
--- /dev/null
@@ -0,0 +1,64 @@
+From 4e49148fbf44d1669b79907236b050274ebb0552 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 08aaa9429ab36..85791380ca724 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..3ba161d
--- /dev/null
@@ -0,0 +1,63 @@
+From e7305369ef84b059d231c848d13215cdeb30d8be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 85791380ca724..fc26d339a962b 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..e2fbbf8
--- /dev/null
@@ -0,0 +1,40 @@
+From fdfb9825660ec246a21e0acdf6b7d51ffcbda561 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index fd2de35ffb3cf..5fd22bb4f5b60 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..6318444
--- /dev/null
@@ -0,0 +1,92 @@
+From 80637c0564dba3ad8b2ba714b96359f185d5cbfa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 514e7f9e0339c..84e3953090bee 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -857,8 +857,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->phy_state.interface = iface;
+@@ -881,28 +881,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->cur_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..cd830e1
--- /dev/null
@@ -0,0 +1,48 @@
+From 6e99d03d3acea63552d7409d0cdca705a49df26f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 8c3aad051c2e5..f453c546478a0 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1077,7 +1077,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1186,6 +1188,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..28ddb27
--- /dev/null
@@ -0,0 +1,50 @@
+From e24071c4bf4c3d5709b9ce082c242647b5f9bf6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 1fe687b594f9d..8c3aad051c2e5 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..6f4303d
--- /dev/null
@@ -0,0 +1,96 @@
+From 6c8d56edaeff059eba36ee76f727da192e545ccd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index c620521c42bc6..dfb9708f53404 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 4326d5ea0400d..0f6147ea9e67b 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1622,7 +1622,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index 76a8bbb44951c..7687d3d15df22 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -313,7 +313,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..b2cec62
--- /dev/null
@@ -0,0 +1,69 @@
+From 954ede6d2dd9f83fd38dc5fa820388d016281e72 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index dbafb964bd706..f24d1276f5315 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -216,9 +216,7 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -227,15 +225,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..8c19461
--- /dev/null
@@ -0,0 +1,83 @@
+From e96475015fdad126bead7f247760addf327575c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 9c5cfd74a0ee4..26918fa575afe 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               vfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..04faa8d
--- /dev/null
@@ -0,0 +1,42 @@
+From 416e84a3b59dfdf8cbf124d9a25b32268dc117c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index 9d39b9fc2f6a8..a8a97197d6b76 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -849,8 +849,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch b/queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch
new file mode 100644 (file)
index 0000000..68eddbb
--- /dev/null
@@ -0,0 +1,41 @@
+From d4ff91fda4a620b8a36d73e156c2bff19d9df866 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Jul 2023 15:45:39 -0400
+Subject: phy: zynqmp: Allow variation in refclk rate
+
+From: Sean Anderson <sean.anderson@seco.com>
+
+[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ]
+
+Due to limited available frequency ratios, the reference clock rate may
+not be exactly the same as the required rate. Allow a small (100 ppm)
+deviation.
+
+Signed-off-by: Sean Anderson <sean.anderson@seco.com>
+Link: https://lore.kernel.org/r/20230711194542.898230-1-sean.anderson@seco.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index ac9a9124a36de..fb67db31e94a3 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -957,7 +957,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+               rate = clk_get_rate(clk);
+               for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      if (rate == ssc_lookup[i].refclk_rate) {
++                      /* Allow an error of 100 ppm */
++                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+                               gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+                               break;
+                       }
+-- 
+2.53.0
+
diff --git a/queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..38cdfc7
--- /dev/null
@@ -0,0 +1,76 @@
+From e4ba857f57005ae7d58b46ac82c88318c88ec01d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index a476f1343253f..4048aff0798ec 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..a4cef0f
--- /dev/null
@@ -0,0 +1,56 @@
+From ae588d51e2503f8cfebd8672ea6240d4f3c81fdc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 4048aff0798ec..b1bbc859b6ccb 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1045,6 +1045,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1054,12 +1060,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
new file mode 100644 (file)
index 0000000..d8203da
--- /dev/null
@@ -0,0 +1,172 @@
+From 8bf4e5988c55d0e8f7a44bce228658a583f85527 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Apr 2025 08:35:47 +0200
+Subject: phy-zynqmp: Postpone getting clock rate until actually needed
+
+From: Mike Looijmans <mike.looijmans@topic.nl>
+
+[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
+
+At probe time the driver would display the following error and abort:
+  xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
+
+At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't
+initialized the clock yet, so clk_get_rate() likely returns 0 if the clock
+is programmable. So this driver only works if the clock is fixed.
+
+The PHY driver doesn't need to know the clock frequency at probe yet, so
+wait until the associated driver initializes the lane before requesting the
+clock rate setting.
+
+In addition to allowing the driver to be used with programmable clocks,
+this also reduces the driver's runtime memory footprint by removing an
+array of pointers from struct xpsgtr_phy.
+
+Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
+Acked-by: Michal Simek <michal.simek@amd.com>
+Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++----------------
+ 1 file changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index fb67db31e94a3..a476f1343253f 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -228,7 +228,6 @@ struct xpsgtr_phy {
+  * @siou: siou base address
+  * @gtr_mutex: mutex for locking
+  * @phys: PHY lanes
+- * @refclk_sscs: spread spectrum settings for the reference clocks
+  * @clk: reference clocks
+  * @tx_term_fix: fix for GT issue
+  * @saved_icm_cfg0: stored value of ICM CFG0 register
+@@ -241,7 +240,6 @@ struct xpsgtr_dev {
+       void __iomem *siou;
+       struct mutex gtr_mutex; /* mutex for locking */
+       struct xpsgtr_phy phys[NUM_LANES];
+-      const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
+       struct clk *clk[NUM_LANES];
+       bool tx_term_fix;
+       unsigned int saved_icm_cfg0;
+@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
+       return ret;
+ }
++/* Get the spread spectrum (SSC) settings for the reference clock rate */
++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy)
++{
++      unsigned long rate;
++      struct clk *clk;
++      unsigned int i;
++
++      clk = gtr_phy->dev->clk[gtr_phy->refclk];
++      rate = clk_get_rate(clk);
++
++      for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
++              /* Allow an error of 100 ppm */
++              unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++              if (abs(rate - ssc_lookup[i].refclk_rate) < error)
++                      return &ssc_lookup[i];
++      }
++
++      dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n",
++              rate, gtr_phy->refclk);
++
++      return NULL;
++}
++
+ /* Configure PLL and spread-sprectrum clock. */
+-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+ {
+       const struct xpsgtr_ssc *ssc;
+       u32 step_size;
+-      ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk];
++      ssc = xpsgtr_find_sscs(gtr_phy);
++      if (!ssc)
++              return -EINVAL;
++
+       step_size = ssc->step_size;
+       xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
+@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+       xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB,
+                          STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) |
+                          FORCE_STEP_SIZE | FORCE_STEPS);
++
++      return 0;
+ }
+ /* Configure the lane protocol. */
+@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+        * Configure the PLL, the lane protocol, and perform protocol-specific
+        * initialization.
+        */
+-      xpsgtr_configure_pll(gtr_phy);
++      ret = xpsgtr_configure_pll(gtr_phy);
++      if (ret)
++              goto out;
++
+       xpsgtr_lane_set_protocol(gtr_phy);
+       switch (gtr_phy->protocol) {
+@@ -854,8 +884,7 @@ static struct phy *xpsgtr_xlate(struct device *dev,
+       }
+       refclk = args->args[3];
+-      if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) ||
+-          !gtr_dev->refclk_sscs[refclk]) {
++      if (refclk >= ARRAY_SIZE(gtr_dev->clk)) {
+               dev_err(dev, "Invalid reference clock number %u\n", refclk);
+               return ERR_PTR(-EINVAL);
+       }
+@@ -931,9 +960,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+ {
+       unsigned int refclk;
+-      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
+-              unsigned long rate;
+-              unsigned int i;
++      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) {
+               struct clk *clk;
+               char name[8];
+@@ -949,29 +976,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+                       continue;
+               gtr_dev->clk[refclk] = clk;
+-
+-              /*
+-               * Get the spread spectrum (SSC) settings for the reference
+-               * clock rate.
+-               */
+-              rate = clk_get_rate(clk);
+-
+-              for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      /* Allow an error of 100 ppm */
+-                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
+-
+-                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+-                              gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+-                              break;
+-                      }
+-              }
+-
+-              if (i == ARRAY_SIZE(ssc_lookup)) {
+-                      dev_err(gtr_dev->dev,
+-                              "Invalid rate %lu for reference clock %u\n",
+-                              rate, refclk);
+-                      return -EINVAL;
+-              }
+       }
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..64353e9
--- /dev/null
@@ -0,0 +1,38 @@
+From fe23e4b247477dc691ed24a0eb29352414e48f35 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..583c88f
--- /dev/null
@@ -0,0 +1,38 @@
+From d7184cc622ca674be9e21e5abe1aa2bc48e1fcfa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..b00eda4
--- /dev/null
@@ -0,0 +1,38 @@
+From 015067e812966a6339e3de7b6db79975cf0652d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..559d894
--- /dev/null
@@ -0,0 +1,169 @@
+From f9fcfc96045136f83953029679f05810d406e35b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index 0d5e96a3b9a21..5dd642647071a 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -108,7 +108,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -992,21 +992,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1043,6 +1028,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1051,9 +1038,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2596,9 +2591,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2628,17 +2627,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..4a2c160
--- /dev/null
@@ -0,0 +1,118 @@
+From da0e4f6ff80f0bb0d8352693c60fbbccf758b39a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ec45664f38767..810a9b76101f5 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index 5289afbb61aa7..e50e01abb0799 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index b560d06e6d96d..071f2a2f514ff 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..960be6f
--- /dev/null
@@ -0,0 +1,85 @@
+From 625bebb9887b095d8b3dc7ee23f3a5d21c58ed01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 071f2a2f514ff..f66cbf0b9895f 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..3da8701
--- /dev/null
@@ -0,0 +1,56 @@
+From 795a3615ec51039fa64c8fb8dba49f3b5ecfebf4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 225aa82799609..fbb5eecbb7caa 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -866,7 +866,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..6b1cf69
--- /dev/null
@@ -0,0 +1,71 @@
+From 69265c316159969194cd87940bc48f08cb1b3209 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index 883005757ddb8..8e9f6e14c6e5a 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -759,13 +759,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -773,6 +766,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..929a13d
--- /dev/null
@@ -0,0 +1,66 @@
+From 6d3cabdb5d961e69c7ee6acd72c3346b8c5ffce9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index 2e29121f96fa3..c92f9b13d8ecc 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
index f2c8434b1cb0d75de5db391ad8bf8d4396bb4c73..818da7454326756eb246edb34a30b49979952f2a 100644 (file)
@@ -243,3 +243,47 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 net-qrtr-ns-limit-the-maximum-server-registration-pe.patch
 net-qrtr-ns-raise-node-count-limit-to-512.patch
 tls-separate-no-async-decryption-request-handling-fr.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+phy-zynqmp-allow-variation-in-refclk-rate.patch
+phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
diff --git a/queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..36ea5a0
--- /dev/null
@@ -0,0 +1,85 @@
+From 0a10de74295e0e153fcd2de3d60faee837d7f0b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+index e6541bd5c63df..11a400663b9f2 100644
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -1661,8 +1661,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1774,8 +1776,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -2205,8 +2209,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..347ee03
--- /dev/null
@@ -0,0 +1,43 @@
+From 8a2f4eedc80b92ae7883c15b8cc230dc54bd71f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index 4d9e5c830dbe1..c523ce5aa4958 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..5b5cd43
--- /dev/null
@@ -0,0 +1,52 @@
+From 8c74f3f245216af830df6e0bc4404bca5e47e1ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index 10b34bc4b67d4..e41fd43f6a6ec 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch
new file mode 100644 (file)
index 0000000..0e3793b
--- /dev/null
@@ -0,0 +1,135 @@
+From 4abaeb6e5740613ef47db455e8d605ba77465ee3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jan 2025 14:14:47 +0900
+Subject: ahci: Introduce ahci_ignore_port() helper
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ]
+
+libahci and AHCI drivers may ignore some ports if the port is invalid
+(its ID does not correspond to a valid physical port) or if the user
+explicitly requested the port to be ignored with the mask_port_map
+ahci module parameter. Such port that shall be ignored can be identified
+by checking that the bit corresponding to the port ID is not set in the
+mask_port_map field of struct ahci_host_priv. E.g. code such as:
+"if (!(hpriv->mask_port_map & (1 << portid)))".
+
+Replace all direct use of the mask_port_map field to detect such port
+with the new helper inline function ahci_ignore_port() to make the code
+more readable/easier to understand.
+
+The comment describing the mask_port_map field of struct ahci_host_priv
+is also updated to be more accurate.
+
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Niklas Cassel <cassel@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci.h             | 13 ++++++++++++-
+ drivers/ata/ahci_brcm.c        |  2 +-
+ drivers/ata/ahci_ceva.c        |  4 ++--
+ drivers/ata/libahci_platform.c |  6 +++---
+ 4 files changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index f9c5906a8afa8..9864427e5c234 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -330,7 +330,7 @@ struct ahci_port_priv {
+ struct ahci_host_priv {
+       /* Input fields */
+       unsigned int            flags;          /* AHCI_HFLAG_* */
+-      u32                     mask_port_map;  /* mask out particular bits */
++      u32                     mask_port_map;  /* Mask of valid ports */
+       void __iomem *          mmio;           /* bus-independent mem map */
+       u32                     cap;            /* cap to use */
+@@ -381,6 +381,17 @@ struct ahci_host_priv {
+                                                 int port);
+ };
++/*
++ * Return true if a port should be ignored because it is excluded from
++ * the host port map.
++ */
++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv,
++                                  unsigned int portid)
++{
++      return portid >= hpriv->nports ||
++              !(hpriv->mask_port_map & (1 << portid));
++}
++
+ extern int ahci_ignore_sss;
+ extern const struct attribute_group *ahci_shost_groups[];
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index cb5d550d6b7ed..ee708dafd08d1 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index c179b8b328587..738e0da50d5b4 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_power_on(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index 155ef2aa90a6c..45144dbc252ba 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -48,7 +48,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -72,7 +72,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+@@ -93,7 +93,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+-- 
+2.53.0
+
diff --git a/queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..861f2f8
--- /dev/null
@@ -0,0 +1,61 @@
+From e5eecc03eda5e1a405f0b208a295ee4f765eea03 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index f5acf84ee20cc..e64e2352c6aa4 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..350ef7c
--- /dev/null
@@ -0,0 +1,61 @@
+From aad8f396d0b7b4f1ee6e1b459ac3af485444671f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index 44aa58fcc23f8..7b399564785c4 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..8299943
--- /dev/null
@@ -0,0 +1,66 @@
+From 564abe5bc165af524d05a33eefde9192f7f83ed7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index ca0b4f360c1a0..65409f0d2e0e5 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
new file mode 100644 (file)
index 0000000..5f63ff2
--- /dev/null
@@ -0,0 +1,76 @@
+From 86e3adfe2d29dbf00883b4f2386a2c93aa29b141 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 23:55:26 +0530
+Subject: ata: ahci_ceva: fix error paths in
+ ceva_ahci_platform_enable_resources()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
+
+On phy_init() failure the error path fallsthrough to disable_rsts, which
+deasserts the controller reset and then enters disable_phys calling
+phy_power_off() on PHYs that were never powered on. That corrupts the PHY
+power_count and triggers an extra runtime PM put.
+
+Use a separate exit_phys path that unwinds with phy_exit() only and falls
+through to disable_clks while the controller remains in reset.  Reserve
+phy_power_off() for the phy_power_on() failure path only, and skip
+masked-out ports in both unwind loops.
+
+On phy_power_on() failure re-assert the controller reset before disabling
+clocks and regulators, matching the teardown order used by
+ahci_platform_enable_resources() and ahci_platform_disable_resources().
+
+Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_ceva.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 738e0da50d5b4..0651d4065f7f7 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+-                      goto disable_rsts;
++                      goto exit_phys;
+       }
+       /* De-assert the controller reset */
+@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       return 0;
+-disable_rsts:
+-      ahci_platform_deassert_rsts(hpriv);
+-
+ disable_phys:
+       while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
++      ahci_platform_assert_rsts(hpriv);
++      goto disable_clks;
++
++exit_phys:
++      while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
++              phy_exit(hpriv->phys[i]);
++      }
+ disable_clks:
+       ahci_platform_disable_clks(hpriv);
+-- 
+2.53.0
+
diff --git a/queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch
new file mode 100644 (file)
index 0000000..029d5ee
--- /dev/null
@@ -0,0 +1,178 @@
+From 675ba85d4bc97bc934a2ac66c655f36fecbbdd21 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jan 2025 13:13:33 +0100
+Subject: ata: libahci_platform: support non-consecutive port numbers
+
+From: Josua Mayer <josua@solid-run.com>
+
+[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ]
+
+So far ahci_platform relied on number of child nodes in firmware to
+allocate arrays and expected port numbers to start from 0 without holes.
+This number of ports is then set in private structure for use when
+configuring phys and regulators.
+
+Some platforms may not use every port of an ahci controller.
+E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading
+to the following errors during boot:
+[    1.719476] ahci f2540000.sata: invalid port number 1
+[    1.724562] ahci f2540000.sata: No port enabled
+
+Update all accessesors of ahci_host_priv phys and target_pwrs arrays to
+support holes. Access is gated by hpriv->mask_port_map which has a bit
+set for each enabled port.
+
+Update ahci_platform_get_resources to ignore holes in the port numbers
+and enable ports defined in firmware by their reg property only.
+
+When firmware does not define children it is assumed that there is
+exactly one port, using index 0.
+
+Signed-off-by: Josua Mayer <josua@solid-run.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_brcm.c        |  3 +++
+ drivers/ata/ahci_ceva.c        |  6 +++++
+ drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++-----
+ 3 files changed, 43 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index 6f216eb256100..cb5d550d6b7ed 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 50e07ea60e45c..c179b8b328587 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_rsts;
+@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_power_on(hpriv->phys[i]);
+               if (rc) {
+                       phy_exit(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index b9e336bacf179..155ef2aa90a6c 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -48,6 +48,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+@@ -69,6 +72,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -87,6 +93,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -434,6 +443,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,
+       return 0;
+ }
++static u32 ahci_platform_find_max_port_id(struct device *dev)
++{
++      u32 max_port = 0;
++
++      for_each_child_of_node_scoped(dev->of_node, child) {
++              u32 port;
++
++              if (!of_property_read_u32(child, "reg", &port))
++                      max_port = max(max_port, port);
++      }
++
++      return max_port;
++}
++
+ /**
+  * ahci_platform_get_resources - Get platform resources
+  * @pdev: platform device to get resources for
+@@ -461,6 +484,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+       struct ahci_host_priv *hpriv;
+       struct device_node *child;
+       u32 mask_port_map = 0;
++      u32 max_port;
+       if (!devres_open_group(dev, NULL, GFP_KERNEL))
+               return ERR_PTR(-ENOMEM);
+@@ -552,15 +576,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+               goto err_out;
+       }
++      /* find maximum port id for allocating structures */
++      max_port = ahci_platform_find_max_port_id(dev);
+       /*
+-       * If no sub-node was found, we still need to set nports to
+-       * one in order to be able to use the
++       * Set nports according to maximum port id. Clamp at
++       * AHCI_MAX_PORTS, warning message for invalid port id
++       * is generated later.
++       * When DT has no sub-nodes max_port is 0, nports is 1,
++       * in order to be able to use the
+        * ahci_platform_[en|dis]able_[phys|regulators] functions.
+        */
+-      if (child_nodes)
+-              hpriv->nports = child_nodes;
+-      else
+-              hpriv->nports = 1;
++      hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1);
+       hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL);
+       if (!hpriv->phys) {
+@@ -633,6 +659,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+                * If no sub-node was found, keep this for device tree
+                * compatibility
+                */
++              hpriv->mask_port_map |= BIT(0);
++
+               rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node);
+               if (rc)
+                       goto err_out;
+-- 
+2.53.0
+
diff --git a/queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
new file mode 100644 (file)
index 0000000..bb66602
--- /dev/null
@@ -0,0 +1,44 @@
+From b05da1c6b2969569d52e61c5c8c7f5bb356b5705 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 15:31:37 -0700
+Subject: ata: sata_mv: accept 1 or 2 resources in platform probe
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
+
+Board files in arch/arm/plat-orion, arch/arm/mach-dove,
+arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the
+"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ).
+Those devices are rejected with -EINVAL, so SATA no longer probes on
+legacy Marvell Orion/Kirkwood-style boards.
+
+Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2
+resources (legacy, IRQ supplied as a second resource) so both probing
+paths work.
+
+Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone")
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/sata_mv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
+index 9cf540017a5e5..298bf6330de57 100644
+--- a/drivers/ata/sata_mv.c
++++ b/drivers/ata/sata_mv.c
+@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
+       /*
+        * Simple resource validation ..
+        */
+-      if (unlikely(pdev->num_resources != 1)) {
++      if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
+               dev_err(&pdev->dev, "invalid number of resources\n");
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..f505879
--- /dev/null
@@ -0,0 +1,60 @@
+From ec2fe9e5559611c175e09fc1cedeb5c8c7765028 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 1da637f81db34..b5603531a9d60 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+               goto unlock;
+       }
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+ unlock:
+       mutex_unlock(&conn->chan_lock);
+-- 
+2.53.0
+
diff --git a/queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..828fc82
--- /dev/null
@@ -0,0 +1,57 @@
+From b24eb3cfc813e3d42bdbb0a980f823fdb73cd316 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 6ee4849e4b789..645e0bd16782d 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1825,13 +1825,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol\n");
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..82d518d
--- /dev/null
@@ -0,0 +1,60 @@
+From ac76dc9e4b2bbdd75070e36a0ba001ab6bb786e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 7ca0c26f9e872..f6b5ad7683734 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -933,16 +933,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..43dc805
--- /dev/null
@@ -0,0 +1,51 @@
+From f7ff63535b0c95c9c81fe23e17587a90216161da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+index aba26ec9a1425..59b310f7327c8 100644
+--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+@@ -183,10 +183,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+-- 
+2.53.0
+
diff --git a/queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..6ce606c
--- /dev/null
@@ -0,0 +1,47 @@
+From 8f1406262fb0e7ed599305cc7dbf3de72edac618 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index 486cbc8ab2242..866267df1f2c9 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..a0666b8
--- /dev/null
@@ -0,0 +1,51 @@
+From 824e0ca38497802996e38fc03b22f1b9581ca420 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 1e837760c55db..9569f1faadeb4 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..2efe098
--- /dev/null
@@ -0,0 +1,83 @@
+From ec06c2c30a5c1fef13916b81fde2a4df5079e37f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 81d7c4ec06b0d..1e837760c55db 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..36b0ae8
--- /dev/null
@@ -0,0 +1,80 @@
+From c83640a6d4aae7e786c317570c7f5d9b97def2ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 1ac26a510bfe2..8010ed6f86a18 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..34c2ec5
--- /dev/null
@@ -0,0 +1,115 @@
+From de3892e07ddbb1d81ee5b958e4f13956bb0bd1e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 927f8df05b7c9..81d7c4ec06b0d 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..d41fa24
--- /dev/null
@@ -0,0 +1,57 @@
+From 10861844d2aa4eee21ae050b0bdf6c2bef008a6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 8010ed6f86a18..9783593a1de76 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..6acc8ae
--- /dev/null
@@ -0,0 +1,62 @@
+From 436e22790126d22d947e66f3e954962a3344c177 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 7fae4a2b3dea0..a5bef97c8e0d3 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..fae72fd
--- /dev/null
@@ -0,0 +1,71 @@
+From 0705359c0aca88b0ed47f4276ff6edf9ff88c703 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 9569f1faadeb4..7fae4a2b3dea0 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..281aa81
--- /dev/null
@@ -0,0 +1,111 @@
+From 866b14dcce4f298b2eb1c788d6ec6214fb84d8ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index a5bef97c8e0d3..1ac26a510bfe2 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
new file mode 100644 (file)
index 0000000..97bfe1a
--- /dev/null
@@ -0,0 +1,54 @@
+From 7f896c56a62a1f8f13299e9cdcba12a8f900b544 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 15:27:28 -0700
+Subject: hwmon: (lm90) Only report alarms if driver is ready
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
+
+Userspace can read sysfs attributes before driver registration is complete,
+immediately after devm_hwmon_device_register_with_info() has been called.
+At that time, data->hwmon_dev is not yet initialized. This can trigger
+a NULL pointer access since lm90_update_device() and with it
+lm90_update_alarms_locked() will be called. This call schedules
+report_work and lm90_report_alarms(), which passes the still-NULL
+data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer
+dereference.
+
+Fix the problem by only scheduling the report and alert workers
+data->hwmon_dev is set.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/lm90.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
+index db595f7d01f8a..7ce75e64cc3ca 100644
+--- a/drivers/hwmon/lm90.c
++++ b/drivers/hwmon/lm90.c
+@@ -1148,7 +1148,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+               check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+                       (data->config & 0x80);
+-              if (force || check_enable)
++              if (data->hwmon_dev && (force || check_enable))
+                       schedule_work(&data->report_work);
+               /*
+@@ -1156,7 +1156,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+                * alarms are all clear, and alerts are currently disabled.
+                * Otherwise (re)schedule worker if needed.
+                */
+-              if (check_enable) {
++              if (check_enable && data->hwmon_dev) {
+                       if (!(data->current_alarms & data->alert_alarms)) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               lm90_update_confreg(data, data->config & ~0x80);
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch b/queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch
new file mode 100644 (file)
index 0000000..28089d7
--- /dev/null
@@ -0,0 +1,320 @@
+From 55ab5ac3aa6ed41ac6a7c9ff4af08b4de844dff7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 28 Dec 2022 05:57:44 -0800
+Subject: hwmon: (nct6755) Add support for NCT6799D
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aee395bb190564a3fa22aa65c60812c25410e94a ]
+
+NCT6799D is mostly compatible to NCT6798D, with minor variations.
+
+Note that NCT6798D and NCT6799D have a new means to select temperature
+sources, and to report temperatures from those sources. This is not
+currently implemented, meaning that most likely not all temperatures
+are reported.
+
+Cc: Sebastian Arnhold <sebastian.arnhold@posteo.de>
+Cc: Ahmad Khalifa <ahmad@khalifa.ws>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Tested-by: Sebastian Arnhold <sebastian.arnhold@posteo.de>
+Tested-by: Corentin Labbe <clabbe.montjoie@gmail.com>
+Link: https://lore.kernel.org/r/20221228135744.281752-1-linux@roeck-us.net
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c     | 55 ++++++++++++++++++++++++++++++--
+ drivers/hwmon/nct6775-i2c.c      |  2 ++
+ drivers/hwmon/nct6775-platform.c | 41 ++++++++++++++++++++++--
+ drivers/hwmon/nct6775.h          |  2 +-
+ 4 files changed, 94 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index ec3ff4e9a9abd..3a61acec7a345 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -33,6 +33,7 @@
+  *                                           (0xd451)
+  * nct6798d    14      7       7       2+6    0xd428 0xc1    0x5ca3
+  *                                           (0xd429)
++ * nct6799d    14      7       7       2+6    0xd802 0xc1    0x5ca3
+  *
+  * #temp lists the number of monitored temperature sources (first value) plus
+  * the number of directly connectable temperature sensors (second value).
+@@ -73,6 +74,7 @@ static const char * const nct6775_device_names[] = {
+       "nct6796",
+       "nct6797",
+       "nct6798",
++      "nct6799",
+ };
+ /* Common and NCT6775 specific data */
+@@ -381,7 +383,7 @@ static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+       0x39, 0x155 };
+ static const u16 NCT6779_REG_TEMP_OFFSET[] = {
+-      0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c };
++      0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c, 0x44d, 0x449 };
+ static const char *const nct6779_temp_label[] = {
+       "",
+@@ -654,6 +656,44 @@ static const char *const nct6798_temp_label[] = {
+ #define NCT6798_TEMP_MASK     0xbfff0ffe
+ #define NCT6798_VIRT_TEMP_MASK        0x80000c00
++static const char *const nct6799_temp_label[] = {
++      "",
++      "SYSTIN",
++      "CPUTIN",
++      "AUXTIN0",
++      "AUXTIN1",
++      "AUXTIN2",
++      "AUXTIN3",
++      "AUXTIN4",
++      "SMBUSMASTER 0",
++      "SMBUSMASTER 1",
++      "Virtual_TEMP",
++      "Virtual_TEMP",
++      "",
++      "AUXTIN5",
++      "",
++      "",
++      "PECI Agent 0",
++      "PECI Agent 1",
++      "PCH_CHIP_CPU_MAX_TEMP",
++      "PCH_CHIP_TEMP",
++      "PCH_CPU_TEMP",
++      "PCH_MCH_TEMP",
++      "Agent0 Dimm0",
++      "Agent0 Dimm1",
++      "Agent1 Dimm0",
++      "Agent1 Dimm1",
++      "BYTE_TEMP0",
++      "BYTE_TEMP1",
++      "PECI Agent 0 Calibration",     /* undocumented */
++      "PECI Agent 1 Calibration",     /* undocumented */
++      "",
++      "Virtual_TEMP"
++};
++
++#define NCT6799_TEMP_MASK     0xbfff2ffe
++#define NCT6799_VIRT_TEMP_MASK        0x80000c00
++
+ /* NCT6102D/NCT6106D specific data */
+ #define NCT6106_REG_VBAT      0x318
+@@ -1109,6 +1149,7 @@ bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)
+       case nct6796:
+       case nct6797:
+       case nct6798:
++      case nct6799:
+               return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
+                 (reg & 0xfff0) == 0x4c0 ||
+                 reg == 0x402 ||
+@@ -1462,6 +1503,7 @@ static int nct6775_update_pwm_limits(struct device *dev)
+               case nct6796:
+               case nct6797:
+               case nct6798:
++              case nct6799:
+                       err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], &reg);
+                       if (err)
+                               return err;
+@@ -3119,6 +3161,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
+               case nct6796:
+               case nct6797:
+               case nct6798:
++              case nct6799:
+                       err = nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val);
+                       if (err)
+                               break;
+@@ -3817,10 +3860,12 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+       case nct6796:
+       case nct6797:
+       case nct6798:
++      case nct6799:
+               data->in_num = 15;
+               data->pwm_num = (data->kind == nct6796 ||
+                                data->kind == nct6797 ||
+-                               data->kind == nct6798) ? 7 : 6;
++                               data->kind == nct6798 ||
++                               data->kind == nct6799) ? 7 : 6;
+               data->auto_pwm_num = 4;
+               data->has_fan_div = false;
+               data->temp_fixed_num = 6;
+@@ -3869,6 +3914,11 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                       data->temp_mask = NCT6798_TEMP_MASK;
+                       data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK;
+                       break;
++              case nct6799:
++                      data->temp_label = nct6799_temp_label;
++                      data->temp_mask = NCT6799_TEMP_MASK;
++                      data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK;
++                      break;
+               }
+               data->REG_CONFIG = NCT6775_REG_CONFIG;
+@@ -3928,6 +3978,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               case nct6796:
+               case nct6797:
+               case nct6798:
++              case nct6799:
+                       data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
+                       num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
+                       break;
+diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
+index e1bcd11461913..779ce65db1a15 100644
+--- a/drivers/hwmon/nct6775-i2c.c
++++ b/drivers/hwmon/nct6775-i2c.c
+@@ -87,6 +87,7 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
+       { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, },
+       { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, },
+       { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, },
++      { .compatible = "nuvoton,nct6799", .data = (void *)nct6799, },
+       { },
+ };
+ MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
+@@ -104,6 +105,7 @@ static const struct i2c_device_id nct6775_i2c_id[] = {
+       { "nct6796", nct6796 },
+       { "nct6797", nct6797 },
+       { "nct6798", nct6798 },
++      { "nct6799", nct6799 },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
+diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
+index 76c6b564d7fc4..5ca6f42c35747 100644
+--- a/drivers/hwmon/nct6775-platform.c
++++ b/drivers/hwmon/nct6775-platform.c
+@@ -35,6 +35,7 @@ static const char * const nct6775_sio_names[] __initconst = {
+       "NCT6796D",
+       "NCT6797D",
+       "NCT6798D",
++      "NCT6799D",
+ };
+ static unsigned short force_id;
+@@ -85,6 +86,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
+ #define SIO_NCT6796_ID                0xd420
+ #define SIO_NCT6797_ID                0xd450
+ #define SIO_NCT6798_ID                0xd428
++#define SIO_NCT6799_ID                0xd800
+ #define SIO_ID_MASK           0xFFF8
+ /*
+@@ -418,7 +420,7 @@ static int nct6775_resume(struct device *dev)
+       if (data->kind == nct6791 || data->kind == nct6792 ||
+           data->kind == nct6793 || data->kind == nct6795 ||
+           data->kind == nct6796 || data->kind == nct6797 ||
+-          data->kind == nct6798)
++          data->kind == nct6798 || data->kind == nct6799)
+               nct6791_enable_io_mapping(sio_data);
+       sio_data->sio_exit(sio_data);
+@@ -565,7 +567,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio
+       } else {
+               /*
+                * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
+-               * NCT6797D, NCT6798D
++               * NCT6797D, NCT6798D, NCT6799D
+                */
+               int cr1a = sio_data->sio_inb(sio_data, 0x1a);
+               int cr1b = sio_data->sio_inb(sio_data, 0x1b);
+@@ -575,12 +577,17 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio
+               int cr2b = sio_data->sio_inb(sio_data, 0x2b);
+               int cr2d = sio_data->sio_inb(sio_data, 0x2d);
+               int cr2f = sio_data->sio_inb(sio_data, 0x2f);
++              bool vsb_ctl_en = cr2f & BIT(0);
+               bool dsw_en = cr2f & BIT(3);
+               bool ddr4_en = cr2f & BIT(4);
++              bool as_seq1_en = cr2f & BIT(7);
+               int cre0;
++              int cre6;
+               int creb;
+               int cred;
++              cre6 = sio_data->sio_inb(sio_data, 0xe0);
++
+               sio_data->sio_select(sio_data, NCT6775_LD_12);
+               cre0 = sio_data->sio_inb(sio_data, 0xe0);
+               creb = sio_data->sio_inb(sio_data, 0xeb);
+@@ -683,6 +690,29 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio
+                       pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
+                       pwm7pin |= cr2d & BIT(7);
+                       pwm7pin |= creb & BIT(2);
++                      break;
++              case nct6799:
++                      fan4pin = cr1c & BIT(6);
++                      fan5pin = cr1c & BIT(7);
++
++                      fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
++                      fan6pin |= cre6 & BIT(5);
++                      fan6pin |= creb & BIT(5);
++                      fan6pin |= !as_seq1_en && (cr2a & BIT(4));
++
++                      fan7pin = cr1b & BIT(5);
++                      fan7pin |= !vsb_ctl_en && !(cr2b & BIT(2));
++                      fan7pin |= creb & BIT(3);
++
++                      pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
++                      pwm6pin |= !as_seq1_en && !(cred & BIT(2)) && (cr2a & BIT(3));
++                      pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
++                      pwm6pin |= cre6 & BIT(3);
++
++                      pwm7pin = !vsb_ctl_en && !(cr1d & (BIT(2) | BIT(3)));
++                      pwm7pin |= creb & BIT(2);
++                      pwm7pin |= cr2d & BIT(7);
++
+                       break;
+               default:        /* NCT6779D */
+                       break;
+@@ -838,6 +868,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
+       case nct6796:
+       case nct6797:
+       case nct6798:
++      case nct6799:
+               break;
+       }
+@@ -876,6 +907,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
+               case nct6796:
+               case nct6797:
+               case nct6798:
++              case nct6799:
+                       tmp |= 0x7e;
+                       break;
+               }
+@@ -1005,6 +1037,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
+       case SIO_NCT6798_ID:
+               sio_data->kind = nct6798;
+               break;
++      case SIO_NCT6799_ID:
++              sio_data->kind = nct6799;
++              break;
+       default:
+               if (val != 0xffff)
+                       pr_debug("unsupported chip ID: 0x%04x\n", val);
+@@ -1033,7 +1068,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
+       if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
+           sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+           sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
+-          sio_data->kind == nct6798)
++          sio_data->kind == nct6798 || sio_data->kind == nct6799)
+               nct6791_enable_io_mapping(sio_data);
+       sio_data->sio_exit(sio_data);
+diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
+index be41848c3cd29..44f79c5726a9c 100644
+--- a/drivers/hwmon/nct6775.h
++++ b/drivers/hwmon/nct6775.h
+@@ -5,7 +5,7 @@
+ #include <linux/types.h>
+ enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
+-           nct6793, nct6795, nct6796, nct6797, nct6798 };
++           nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
+ enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+ #define NUM_TEMP      10      /* Max number of temp attribute sets w/ limits*/
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch b/queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch
new file mode 100644 (file)
index 0000000..e4bc822
--- /dev/null
@@ -0,0 +1,166 @@
+From d6f39722469e686eb36439c30033627afd8fe80e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 19 Jul 2023 23:41:42 +0100
+Subject: hwmon: (nct6775) Add support for 18 IN readings for nct6799
+
+From: Ahmad Khalifa <ahmad@khalifa.ws>
+
+[ Upstream commit 4f65c15cf70eb22c074889af60b9d2bcffbb375a ]
+
+* Add additional VIN/IN_MIN/IN_MAX register values
+* Separate ALARM/BEEP bits for nct6799
+* Update scaling factors for nct6799
+
+Registers/alarms match for NCT6796D-S and NCT6799D-R
+Tested on NCT6799D-R for new IN/MIN/MAX and ALARMS
+
+Signed-off-by: Ahmad Khalifa <ahmad@khalifa.ws>
+Link: https://lore.kernel.org/r/20230719224142.411237-1-ahmad@khalifa.ws
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 51 +++++++++++++++++++++++++++++-------
+ drivers/hwmon/nct6775.h      |  5 ++--
+ 2 files changed, 45 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 34b13194c91c5..c2deff486ef3c 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -79,14 +79,17 @@ static const char * const nct6775_device_names[] = {
+ /* Common and NCT6775 specific data */
+-/* Voltage min/max registers for nr=7..14 are in bank 5 */
++/*
++ * Voltage min/max registers for nr=7..14 are in bank 5
++ * min/max: 15-17 for NCT6799 only
++ */
+ static const u16 NCT6775_REG_IN_MAX[] = {
+       0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a,
+-      0x55c, 0x55e, 0x560, 0x562 };
++      0x55c, 0x55e, 0x560, 0x562, 0x564, 0x570, 0x572 };
+ static const u16 NCT6775_REG_IN_MIN[] = {
+       0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b,
+-      0x55d, 0x55f, 0x561, 0x563 };
++      0x55d, 0x55f, 0x561, 0x563, 0x565, 0x571, 0x573 };
+ static const u16 NCT6775_REG_IN[] = {
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552
+ };
+@@ -255,7 +258,8 @@ static const s8 NCT6776_ALARM_BITS[NUM_ALARM_BITS] = {
+       12,  9,                                           /* intr0-intr1  */
+ };
+-static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 };
++/* 0xbf: nct6799 only */
++static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
+ static const s8 NCT6776_BEEP_BITS[NUM_BEEP_BITS] = {
+        0,  1,  2,  3,  4,  5,  6,  7,  8, -1, -1, -1,   /* in0-in11     */
+@@ -327,9 +331,16 @@ static const u16 NCT6776_REG_TSI_TEMP[] = {
+ /* NCT6779 specific data */
++/*
++ * 15-17 for NCT6799 only, register labels are:
++ *      CPUVC,  VIN1,  AVSB,  3VCC,  VIN0,  VIN8,  VIN4, 3VSB
++ *       VBAT,   VTT,  VIN5,  VIN6,  VIN2,  VIN3,  VIN7, VIN9
++ *       VHIF, VIN10
++ */
+ static const u16 NCT6779_REG_IN[] = {
+       0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487,
+-      0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e };
++      0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e, 0x48f,
++      0x470, 0x471};
+ static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = {
+       0x459, 0x45A, 0x45B, 0x568 };
+@@ -643,6 +654,22 @@ static const char *const nct6798_temp_label[] = {
+ #define NCT6798_TEMP_MASK     0xbfff0ffe
+ #define NCT6798_VIRT_TEMP_MASK        0x80000c00
++static const s8 NCT6799_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  8, -1, 20, 16, 17, 24, 25, 26,   /* in0-in11     */
++      27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12,  9,                                           /* intr0-intr1  */
++};
++
++static const s8 NCT6799_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,   /* in0-in11     */
++      12, 13, 14, 15, 34, 35, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      30, 31, 24                                        /* intr0-intr1, beep_en */
++};
++
+ static const char *const nct6799_temp_label[] = {
+       "",
+       "SYSTIN",
+@@ -937,12 +964,12 @@ static const u16 scale_in[15] = {
+ /*
+  * NCT6798 scaling:
+  *    CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT,  VTT,  IN5,  IN6, IN2,
+- *      IN3, IN7
+- * Additional scales to be added later: IN9 (800), VHIF (1600)
++ *      IN3, IN7,  IN9, VHIF, IN10
++ * 15-17 for NCT6799 only
+  */
+-static const u16 scale_in_6798[15] = {
++static const u16 scale_in_6798[NUM_IN] = {
+       800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 1600, 1600, 1600, 800,
+-      800, 800
++      800, 800,  800, 1600, 800
+ };
+ static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales)
+@@ -3970,7 +3997,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               case nct6796:
+               case nct6797:
+               case nct6798:
++                      data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
++                      num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
++                      break;
+               case nct6799:
++                      data->in_num = 18;
++                      data->ALARM_BITS = NCT6799_ALARM_BITS;
++                      data->BEEP_BITS = NCT6799_BEEP_BITS;
+                       data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
+                       num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
+                       break;
+diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
+index c752bc7bbe722..edcde39c47915 100644
+--- a/drivers/hwmon/nct6775.h
++++ b/drivers/hwmon/nct6775.h
+@@ -16,6 +16,7 @@ enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+ #define NUM_REG_BEEP  5       /* Max number of beep registers */
+ #define NUM_FAN               7
++#define NUM_IN                18
+ struct nct6775_data {
+       int addr;       /* IO base of hw monitor block */
+@@ -97,7 +98,7 @@ struct nct6775_data {
+       /* Register values */
+       u8 bank;                /* current register bank */
+       u8 in_num;              /* number of in inputs we have */
+-      u8 in[15][3];           /* [0]=in, [1]=in_max, [2]=in_min */
++      u8 in[NUM_IN][3];       /* [0]=in, [1]=in_max, [2]=in_min */
+       const u16 *scale_in;    /* internal scaling factors */
+       unsigned int rpm[NUM_FAN];
+       u16 fan_min[NUM_FAN];
+@@ -166,7 +167,7 @@ struct nct6775_data {
+       u16 have_temp;
+       u16 have_temp_fixed;
+       u16 have_tsi_temp;
+-      u16 have_in;
++      u32 have_in;
+       /* Remember extra register values over suspend/resume */
+       u8 vbat;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch b/queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch
new file mode 100644 (file)
index 0000000..dd2bc79
--- /dev/null
@@ -0,0 +1,276 @@
+From 43ac06f9c6b8920c589dfe82fa3b6051c8f0335e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Aug 2023 19:58:21 +0100
+Subject: hwmon: (nct6775) Additional TEMP registers for nct6799
+
+From: Ahmad Khalifa <ahmad@khalifa.ws>
+
+[ Upstream commit b7f1f7b2523a6a4382f12fe953380b847b80e09d ]
+
+Additional TEMP registers for nct6798d, nct6799d-r and nct6796d-s
+This allows the max/max_hyst/crit attributes to be shown/stored
+
+* Increase NUM_TEMP from 10 to 12
+* Separate TEMP/MON_TEMP/OVER/HYST/CRIT registers
+* Rename "PECI Calibration" to include "TSI" too
+* Update ALARM/BEEP bits for temps for 6799
+* For 6799, keep temp_fixed_num at 6, but increase
+  num_temp_alarms/num_temp_beeps to 7/8
+
+Tested with NCT6799D-R showing additional sysfs attributes:
+* temp3-temp8: max/max_hyst/beep/alarm
+* temp3-temp6: crit/offset
+
+Signed-off-by: Ahmad Khalifa <ahmad@khalifa.ws>
+Link: https://lore.kernel.org/r/20230802185820.3642399-1-ahmad@khalifa.ws
+[groeck: Addressed cosmetic checkpatch complaints]
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 150 ++++++++++++++++++++++++++++-------
+ drivers/hwmon/nct6775.h      |   2 +-
+ 2 files changed, 121 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index c2deff486ef3c..acb0fbb0eabca 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -616,6 +616,28 @@ static const char *const nct6796_temp_label[] = {
+ static const u16 NCT6796_REG_TSI_TEMP[] = { 0x409, 0x40b };
++static const u16 NCT6798_REG_TEMP[] = {
++      0x27, 0x150, 0x670, 0x672, 0x674, 0x676, 0x678, 0x67a};
++
++static const u16 NCT6798_REG_TEMP_SOURCE[] = {
++      0x621, 0x622, 0xc26, 0xc27, 0xc28, 0xc29, 0xc2a, 0xc2b };
++
++static const u16 NCT6798_REG_TEMP_MON[] = {
++      0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x4a0 };
++static const u16 NCT6798_REG_TEMP_OVER[] = {
++      0x39, 0x155, 0xc1a, 0xc1b, 0xc1c, 0xc1d, 0xc1e, 0xc1f };
++static const u16 NCT6798_REG_TEMP_HYST[] = {
++      0x3a, 0x153, 0xc20, 0xc21, 0xc22, 0xc23, 0xc24, 0xc25 };
++
++static const u16 NCT6798_REG_TEMP_CRIT[32] = {
++      0x135, 0x235, 0x335, 0x835, 0x935, 0xa35, 0xb35, 0 };
++
++static const u16 NCT6798_REG_TEMP_ALTERNATE[32] = {
++      0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0x496, 0,
++      0, 0, 0, 0, 0x4a2, 0, 0, 0,
++      0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
++      0x408, 0x419, 0x41a, 0x4f4, 0x4f5 };
++
+ static const char *const nct6798_temp_label[] = {
+       "",
+       "SYSTIN",
+@@ -654,11 +676,14 @@ static const char *const nct6798_temp_label[] = {
+ #define NCT6798_TEMP_MASK     0xbfff0ffe
+ #define NCT6798_VIRT_TEMP_MASK        0x80000c00
++static const u16 NCT6799_REG_ALARM[NUM_REG_ALARM] = {
++      0x459, 0x45A, 0x45B, 0x568, 0x45D, 0xc01 };
++
+ static const s8 NCT6799_ALARM_BITS[NUM_ALARM_BITS] = {
+        0,  1,  2,  3,  8, -1, 20, 16, 17, 24, 25, 26,   /* in0-in11     */
+       27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
+        6,  7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
+-       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++       4,  5, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1,   /* temp1-temp12 */
+       12,  9,                                           /* intr0-intr1  */
+ };
+@@ -666,10 +691,11 @@ static const s8 NCT6799_BEEP_BITS[NUM_BEEP_BITS] = {
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,   /* in0-in11     */
+       12, 13, 14, 15, 34, 35, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
+       25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
+-      16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1,   /* temp1-temp12 */
+       30, 31, 24                                        /* intr0-intr1, beep_en */
+ };
++/* PECI Calibration only for NCT6799D, not NCT6796D-S */
+ static const char *const nct6799_temp_label[] = {
+       "",
+       "SYSTIN",
+@@ -699,8 +725,8 @@ static const char *const nct6799_temp_label[] = {
+       "Agent1 Dimm1",
+       "BYTE_TEMP0",
+       "BYTE_TEMP1",
+-      "PECI Agent 0 Calibration",     /* undocumented */
+-      "PECI Agent 1 Calibration",     /* undocumented */
++      "PECI/TSI Agent 0 Calibration",
++      "PECI/TSI Agent 1 Calibration",
+       "",
+       "Virtual_TEMP"
+ };
+@@ -3878,13 +3904,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+       case nct6795:
+       case nct6796:
+       case nct6797:
+-      case nct6798:
+-      case nct6799:
+               data->in_num = 15;
+               data->pwm_num = (data->kind == nct6796 ||
+-                               data->kind == nct6797 ||
+-                               data->kind == nct6798 ||
+-                               data->kind == nct6799) ? 7 : 6;
++                               data->kind == nct6797) ? 7 : 6;
+               data->auto_pwm_num = 4;
+               data->has_fan_div = false;
+               data->temp_fixed_num = 6;
+@@ -3928,16 +3950,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                       data->temp_mask = NCT6796_TEMP_MASK;
+                       data->virt_temp_mask = NCT6796_VIRT_TEMP_MASK;
+                       break;
+-              case nct6798:
+-                      data->temp_label = nct6798_temp_label;
+-                      data->temp_mask = NCT6798_TEMP_MASK;
+-                      data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK;
+-                      break;
+-              case nct6799:
+-                      data->temp_label = nct6799_temp_label;
+-                      data->temp_mask = NCT6799_TEMP_MASK;
+-                      data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK;
+-                      break;
+               }
+               data->REG_CONFIG = NCT6775_REG_CONFIG;
+@@ -3996,14 +4008,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               case nct6795:
+               case nct6796:
+               case nct6797:
+-              case nct6798:
+-                      data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
+-                      num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
+-                      break;
+-              case nct6799:
+-                      data->in_num = 18;
+-                      data->ALARM_BITS = NCT6799_ALARM_BITS;
+-                      data->BEEP_BITS = NCT6799_BEEP_BITS;
+                       data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
+                       num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
+                       break;
+@@ -4012,9 +4016,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                       break;
+               }
+-              if (data->kind == nct6798 || data->kind == nct6799)
+-                      data->scale_in = scale_in_6798;
+-
+               reg_temp = NCT6779_REG_TEMP;
+               num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+               if (data->kind == nct6791) {
+@@ -4030,6 +4031,95 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6779_REG_TEMP_CRIT;
++              break;
++      case nct6798:
++      case nct6799:
++              data->in_num = data->kind == nct6799 ? 18 : 15;
++              data->scale_in = scale_in_6798;
++              data->pwm_num = 7;
++              data->auto_pwm_num = 4;
++              data->has_fan_div = false;
++              data->temp_fixed_num = 6;
++              data->num_temp_alarms = 7;
++              data->num_temp_beeps = 8;
++
++              data->ALARM_BITS = NCT6799_ALARM_BITS;
++              data->BEEP_BITS = NCT6799_BEEP_BITS;
++
++              data->fan_from_reg = fan_from_reg_rpm;
++              data->fan_from_reg_min = fan_from_reg13;
++              data->target_temp_mask = 0xff;
++              data->tolerance_mask = 0x07;
++              data->speed_tolerance_limit = 63;
++
++              switch (data->kind) {
++              default:
++              case nct6798:
++                      data->temp_label = nct6798_temp_label;
++                      data->temp_mask = NCT6798_TEMP_MASK;
++                      data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK;
++                      break;
++              case nct6799:
++                      data->temp_label = nct6799_temp_label;
++                      data->temp_mask = NCT6799_TEMP_MASK;
++                      data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK;
++                      break;
++              }
++
++              data->REG_CONFIG = NCT6775_REG_CONFIG;
++              data->REG_VBAT = NCT6775_REG_VBAT;
++              data->REG_DIODE = NCT6775_REG_DIODE;
++              data->DIODE_MASK = NCT6775_DIODE_MASK;
++              data->REG_VIN = NCT6779_REG_IN;
++              data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
++              data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
++              data->REG_TARGET = NCT6775_REG_TARGET;
++              data->REG_FAN = NCT6779_REG_FAN;
++              data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
++              data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
++              data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
++              data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
++              data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
++              data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
++              data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
++              data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
++              data->REG_PWM[0] = NCT6775_REG_PWM;
++              data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
++              data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
++              data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP;
++              data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE;
++              data->REG_PWM_READ = NCT6775_REG_PWM_READ;
++              data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
++              data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
++              data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
++              data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
++              data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
++              data->REG_CRITICAL_TEMP_TOLERANCE = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
++              data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE;
++              data->CRITICAL_PWM_ENABLE_MASK = NCT6779_CRITICAL_PWM_ENABLE_MASK;
++              data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM;
++              data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
++              data->REG_TEMP_SOURCE = NCT6798_REG_TEMP_SOURCE;
++              data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
++              data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL;
++              data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP;
++              data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
++              data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
++              data->REG_ALARM = NCT6799_REG_ALARM;
++              data->REG_BEEP = NCT6792_REG_BEEP;
++              data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP;
++              num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP);
++
++              reg_temp = NCT6798_REG_TEMP;
++              num_reg_temp = ARRAY_SIZE(NCT6798_REG_TEMP);
++              reg_temp_mon = NCT6798_REG_TEMP_MON;
++              num_reg_temp_mon = ARRAY_SIZE(NCT6798_REG_TEMP_MON);
++              reg_temp_over = NCT6798_REG_TEMP_OVER;
++              reg_temp_hyst = NCT6798_REG_TEMP_HYST;
++              reg_temp_config = NCT6779_REG_TEMP_CONFIG;
++              reg_temp_alternate = NCT6798_REG_TEMP_ALTERNATE;
++              reg_temp_crit = NCT6798_REG_TEMP_CRIT;
++
+               break;
+       default:
+               return -ENODEV;
+diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
+index edcde39c47915..296eff99d0038 100644
+--- a/drivers/hwmon/nct6775.h
++++ b/drivers/hwmon/nct6775.h
+@@ -8,7 +8,7 @@ enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
+            nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
+ enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+-#define NUM_TEMP      10      /* Max number of temp attribute sets w/ limits*/
++#define NUM_TEMP      12      /* Max number of temp attribute sets w/ limits*/
+ #define NUM_TEMP_FIXED        6       /* Max number of fixed temp attribute sets */
+ #define NUM_TSI_TEMP  8       /* Max number of TSI temp register pairs */
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch
new file mode 100644 (file)
index 0000000..52fdfd8
--- /dev/null
@@ -0,0 +1,90 @@
+From d5f1f62f560b852a3156175f7731ebf80f5c9f88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:14:36 -0700
+Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
+
+Unlike NCT6106, NCT6116 only has three temperature registers, and with
+it only three temperature source and temperature source configuration
+registers. The register addresses match those of NCT6106 and can be
+re-used.
+
+The code used a separate array to list the temperature source registers
+for NCT6116, but used the size of the NCT6106 register array to set
+the number of registers. The NCT6106 register array provides six addresses,
+while the temperature source register array for NCT6116 only provides three
+addresses. This causes a KASAN report.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775]
+Read of size 2 at addr ffffffffc19561a6 by task modprobe/954
+...
+Call Trace:
+ dump_stack+0x7d/0xa7
+ print_address_description.constprop.0+0x1c/0x220
+ ? __kasan_kmalloc.constprop.0+0xc9/0xd0
+ ? __kmalloc_node_track_caller+0x194/0x5b0
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+...
+
+Fix the problem by hard-coding the number of temperature and temperature
+configuration registers to three for NCT6116. Drop the unnecessary
+NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
+
+Reported-by: Florian Bezdeka <florian.bezdeka@siemens.com>
+Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index c7b3961e189ce..887102e51067a 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -845,8 +845,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+ static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+ static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+ static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+-      0xb0, 0xb1, 0xb2 };
+ static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+       0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+@@ -3645,7 +3643,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                 = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+               data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+               data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+-              data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
++              data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+@@ -3659,13 +3657,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
+-              num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
++              num_reg_temp = 3;
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
+               num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+-              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
++              num_reg_temp_config = 3;
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..0eeca02
--- /dev/null
@@ -0,0 +1,74 @@
+From cda70bf9296a12fa70a95ee4fccf71f7d215c0ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 887102e51067a..7c62cc35c5a17 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -790,12 +790,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch b/queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch
new file mode 100644 (file)
index 0000000..724a513
--- /dev/null
@@ -0,0 +1,118 @@
+From 538c44adbf70f8b571b8c8c4cd8b76ec6224931d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 21 Feb 2024 06:01:20 -0800
+Subject: hwmon: (nct6775) Fix access to temperature configuration registers
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d56e460e19ea8382f813eb489730248ec8d7eb73 ]
+
+The number of temperature configuration registers does
+not always match the total number of temperature registers.
+This can result in access errors reported if KASAN is enabled.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x5654/0x6fe9 nct6775_core
+
+Reported-by: Erhard Furtner <erhard_f@mailbox.org>
+Closes: https://lore.kernel.org/linux-hwmon/d51181d1-d26b-42b2-b002-3f5a4037721f@roeck-us.net/
+Fixes: b7f1f7b2523a ("hwmon: (nct6775) Additional TEMP registers for nct6799")
+Cc: Ahmad Khalifa <ahmad@khalifa.ws>
+Tested-by: Ahmad Khalifa <ahmad@khalifa.ws>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index acb0fbb0eabca..c7b3961e189ce 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -3506,6 +3506,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+       const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit;
+       const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL;
+       int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp;
++      int num_reg_temp_config;
+       struct device *hwmon_dev;
+       struct sensor_template_group tsi_temp_tg;
+@@ -3588,6 +3589,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+@@ -3663,6 +3665,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+@@ -3740,6 +3743,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6775_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+               reg_temp_config = NCT6775_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6775_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6775_REG_TEMP_CRIT;
+@@ -3815,6 +3819,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6775_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+               reg_temp_config = NCT6776_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6776_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6776_REG_TEMP_CRIT;
+@@ -3894,6 +3899,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6779_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+               reg_temp_config = NCT6779_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+@@ -4028,6 +4034,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6779_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+               reg_temp_config = NCT6779_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+@@ -4117,6 +4124,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp_over = NCT6798_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6798_REG_TEMP_HYST;
+               reg_temp_config = NCT6779_REG_TEMP_CONFIG;
++              num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG);
+               reg_temp_alternate = NCT6798_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6798_REG_TEMP_CRIT;
+@@ -4198,7 +4206,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                                 = reg_temp_crit[src - 1];
+                       if (reg_temp_crit_l && reg_temp_crit_l[i])
+                               data->reg_temp[4][src - 1] = reg_temp_crit_l[i];
+-                      data->reg_temp_config[src - 1] = reg_temp_config[i];
++                      if (i < num_reg_temp_config)
++                              data->reg_temp_config[src - 1] = reg_temp_config[i];
+                       data->temp_src[src - 1] = src;
+                       continue;
+               }
+@@ -4211,7 +4220,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               data->reg_temp[0][s] = reg_temp[i];
+               data->reg_temp[1][s] = reg_temp_over[i];
+               data->reg_temp[2][s] = reg_temp_hyst[i];
+-              data->reg_temp_config[s] = reg_temp_config[i];
++              if (i < num_reg_temp_config)
++                      data->reg_temp_config[s] = reg_temp_config[i];
+               if (reg_temp_crit_h && reg_temp_crit_h[i])
+                       data->reg_temp[3][s] = reg_temp_crit_h[i];
+               else if (reg_temp_crit[src - 1])
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch b/queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch
new file mode 100644 (file)
index 0000000..d1006a7
--- /dev/null
@@ -0,0 +1,112 @@
+From 7169928c943edac27d9136d40b0cb86330f202a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 19 Jul 2023 20:28:48 +0100
+Subject: hwmon: (nct6775) Fix IN scaling factors for 6798/6799
+
+From: Ahmad Khalifa <ahmad@khalifa.ws>
+
+[ Upstream commit 13558a2e6341d1ba6dff9f8e2febf97877067885 ]
+
+Scaling for VTT/VIN5/VIN6 registers were based on prior chips
+* Split scaling factors for 6798/6799 and assign at probe()
+* Pass them through driver data to sysfs functions
+
+Tested on nct6799 with old/new input/min/max
+
+Fixes: 0599682b826f ("hwmon: (nct6775) Add support for NCT6798D")
+Signed-off-by: Ahmad Khalifa <ahmad@khalifa.ws>
+Link: https://lore.kernel.org/r/20230719192848.337508-1-ahmad@khalifa.ws
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 28 ++++++++++++++++++++++------
+ drivers/hwmon/nct6775.h      |  1 +
+ 2 files changed, 23 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 3a61acec7a345..a7e5faf8d0278 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -955,14 +955,25 @@ static const u16 scale_in[15] = {
+       800, 800
+ };
+-static inline long in_from_reg(u8 reg, u8 nr)
++/*
++ * NCT6798 scaling:
++ *    CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT,  VTT,  IN5,  IN6, IN2,
++ *      IN3, IN7
++ * Additional scales to be added later: IN9 (800), VHIF (1600)
++ */
++static const u16 scale_in_6798[15] = {
++      800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 1600, 1600, 1600, 800,
++      800, 800
++};
++
++static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales)
+ {
+-      return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
++      return DIV_ROUND_CLOSEST(reg * scales[nr], 100);
+ }
+-static inline u8 in_to_reg(u32 val, u8 nr)
++static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scales)
+ {
+-      return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255);
++      return clamp_val(DIV_ROUND_CLOSEST(val * 100, scales[nr]), 0, 255);
+ }
+ /* TSI temperatures are in 8.3 format */
+@@ -1677,7 +1688,8 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+-      return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
++      return sprintf(buf, "%ld\n",
++                     in_from_reg(data->in[nr][index], nr, data->scale_in));
+ }
+ static ssize_t
+@@ -1695,7 +1707,7 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
+       if (err < 0)
+               return err;
+       mutex_lock(&data->update_lock);
+-      data->in[nr][index] = in_to_reg(val, nr);
++      data->in[nr][index] = in_to_reg(val, nr, data->scale_in);
+       err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]);
+       mutex_unlock(&data->update_lock);
+       return err ? : count;
+@@ -3472,6 +3484,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+       mutex_init(&data->update_lock);
+       data->name = nct6775_device_names[data->kind];
+       data->bank = 0xff;              /* Force initial bank selection */
++      data->scale_in = scale_in;
+       switch (data->kind) {
+       case nct6106:
+@@ -3987,6 +4000,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                       break;
+               }
++              if (data->kind == nct6798 || data->kind == nct6799)
++                      data->scale_in = scale_in_6798;
++
+               reg_temp = NCT6779_REG_TEMP;
+               num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+               if (data->kind == nct6791) {
+diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
+index 44f79c5726a9c..a84c6ce7275de 100644
+--- a/drivers/hwmon/nct6775.h
++++ b/drivers/hwmon/nct6775.h
+@@ -98,6 +98,7 @@ struct nct6775_data {
+       u8 bank;                /* current register bank */
+       u8 in_num;              /* number of in inputs we have */
+       u8 in[15][3];           /* [0]=in, [1]=in_max, [2]=in_min */
++      const u16 *scale_in;    /* internal scaling factors */
+       unsigned int rpm[NUM_FAN];
+       u16 fan_min[NUM_FAN];
+       u8 fan_pulses[NUM_FAN];
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch b/queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch
new file mode 100644 (file)
index 0000000..5131f08
--- /dev/null
@@ -0,0 +1,302 @@
+From afdc8c886cda6204472a14b543f8e0016f6253fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 17 Jul 2023 21:10:51 +0100
+Subject: hwmon: (nct6775) Increase and reorder ALARM/BEEP bits
+
+From: Ahmad Khalifa <ahmad@khalifa.ws>
+
+[ Upstream commit 3b7f4bde06daaff391a374fc27c8163b2847de34 ]
+
+* Increase available bits, IN: 16 to 24, FAN: 8 to 12,
+  TEMP: 6 to 12
+* Reorder alarm/beep definitions to match in order to allow
+  additional inputs in the future
+* Remove comments about 'unused' bits as probe() is a better
+  reference
+
+Testing note:
+* Tested on nct6799 with IN/FAN/TEMP, and changing min/max/high/hyst,
+  that triggers the corresponding alarms correctly. Good confirmation
+  on the original mapping of the registers and masks.
+  As to be expected, only 4 fans and 2 temps (fixed) have limits
+  currently on nct6799 on my board.
+* Trouble with testing intrusion alarms and beeps, no way to confirm
+  those. As I understand now, intrusion/caseopen is probably not
+  connected on my board.
+  And I haven't seen a buzzer on a board in ages.
+
+Signed-off-by: Ahmad Khalifa <ahmad@khalifa.ws>
+Link: https://lore.kernel.org/r/20230717201050.1657809-1-ahmad@khalifa.ws
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 169 +++++++++++++++--------------------
+ drivers/hwmon/nct6775.h      |  23 ++++-
+ 2 files changed, 93 insertions(+), 99 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index a7e5faf8d0278..34b13194c91c5 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -97,31 +97,23 @@ static const u16 NCT6775_REG_IN[] = {
+ static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
+-/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */
+-
+-static const s8 NCT6775_ALARM_BITS[] = {
+-      0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+-      17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
+-      -1,                             /* unused */
+-      6, 7, 11, -1, -1,               /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+-      12, -1 };                       /* intrusion0, intrusion1 */
++static const s8 NCT6775_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  8, 21, 20, 16, 17, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12, -1,                                           /* intr0-intr1  */
++};
+ static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e };
+-/*
+- * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures,
+- * 30..31 intrusion
+- */
+-static const s8 NCT6775_BEEP_BITS[] = {
+-      0, 1, 2, 3, 8, 9, 10, 16,       /* in0.. in7 */
+-      17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
+-      21,                             /* global beep enable */
+-      6, 7, 11, 28, -1,               /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+-      12, -1 };                       /* intrusion0, intrusion1 */
++static const s8 NCT6775_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  8,  9, 10, 16, 17, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, 28, -1, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12, -1, 21                                        /* intr0-intr1, beep_en */
++};
+ /* DC or PWM output fan configuration */
+ static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 };
+@@ -255,25 +247,23 @@ static const u16 NCT6775_REG_TSI_TEMP[] = { 0x669 };
+ #define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME
+ #define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME
+-static const s8 NCT6776_ALARM_BITS[] = {
+-      0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+-      17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
+-      -1,                             /* unused */
+-      6, 7, 11, 10, 23,               /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+-      12, 9 };                        /* intrusion0, intrusion1 */
++static const s8 NCT6776_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  8, 21, 20, 16, 17, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, 10, 23, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12,  9,                                           /* intr0-intr1  */
++};
+ static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 };
+-static const s8 NCT6776_BEEP_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 6, 7,         /* in0.. in7 */
+-      8, -1, -1, -1, -1, -1, -1,      /* in8..in14 */
+-      24,                             /* global beep enable */
+-      25, 26, 27, 28, 29,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, 18, 19, 20, 21,         /* temp1..temp6 */
+-      30, 31 };                       /* intrusion0, intrusion1 */
++static const s8 NCT6776_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  4,  5,  6,  7,  8, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      30, 31, 24                                        /* intr0-intr1, beep_en */
++};
+ static const u16 NCT6776_REG_TOLERANCE_H[] = {
+       0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c };
+@@ -344,23 +334,21 @@ static const u16 NCT6779_REG_IN[] = {
+ static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = {
+       0x459, 0x45A, 0x45B, 0x568 };
+-static const s8 NCT6779_ALARM_BITS[] = {
+-      0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+-      17, 24, 25, 26, 27, 28, 29,     /* in8..in14 */
+-      -1,                             /* unused */
+-      6, 7, 11, 10, 23,               /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+-      12, 9 };                        /* intrusion0, intrusion1 */
+-
+-static const s8 NCT6779_BEEP_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 6, 7,         /* in0.. in7 */
+-      8, 9, 10, 11, 12, 13, 14,       /* in8..in14 */
+-      24,                             /* global beep enable */
+-      25, 26, 27, 28, 29,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, -1, -1, -1, -1,         /* temp1..temp6 */
+-      30, 31 };                       /* intrusion0, intrusion1 */
++static const s8 NCT6779_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  8, 21, 20, 16, 17, 24, 25, 26,   /* in0-in11     */
++      27, 28, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, 10, 23, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12,  9,                                           /* intr0-intr1  */
++};
++
++static const s8 NCT6779_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,   /* in0-in11     */
++      12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      30, 31, 24                                        /* intr0-intr1, beep_en */
++};
+ static const u16 NCT6779_REG_FAN[] = {
+       0x4c0, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4ce };
+@@ -448,14 +436,13 @@ static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e };
+ static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
+       0x459, 0x45A, 0x45B, 0x568, 0x45D };
+-static const s8 NCT6791_ALARM_BITS[] = {
+-      0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+-      17, 24, 25, 26, 27, 28, 29,     /* in8..in14 */
+-      -1,                             /* unused */
+-      6, 7, 11, 10, 23, 33,           /* fan1..fan6 */
+-      -1, -1,                         /* unused */
+-      4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+-      12, 9 };                        /* intrusion0, intrusion1 */
++static const s8 NCT6791_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  8, 21, 20, 16, 17, 24, 25, 26,   /* in0-in11     */
++      27, 28, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++       6,  7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++       4,  5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      12,  9,                                           /* intr0-intr1  */
++};
+ /* NCT6792/NCT6793 specific data */
+@@ -763,27 +750,23 @@ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+ static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = {
+       0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d };
+-static const s8 NCT6106_ALARM_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 7, 8,         /* in0.. in7 */
+-      9, -1, -1, -1, -1, -1, -1,      /* in8..in14 */
+-      -1,                             /* unused */
+-      32, 33, 34, -1, -1,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, 18, 19, 20, 21,         /* temp1..temp6 */
+-      48, -1                          /* intrusion0, intrusion1 */
++static const s8 NCT6106_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  4,  5,  7,  8,  9, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      32, 33, 34, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      48, -1,                                           /* intr0-intr1  */
+ };
+ static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = {
+       0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 };
+-static const s8 NCT6106_BEEP_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 7, 8,         /* in0.. in7 */
+-      9, 10, 11, 12, -1, -1, -1,      /* in8..in14 */
+-      32,                             /* global beep enable */
+-      24, 25, 26, 27, 28,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, 18, 19, 20, 21,         /* temp1..temp6 */
+-      34, -1                          /* intrusion0, intrusion1 */
++static const s8 NCT6106_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  4,  5,  7,  8,  9, 10, 11, 12,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      34, -1, 32                                        /* intr0-intr1, beep_en */
+ };
+ static const u16 NCT6106_REG_TEMP_ALTERNATE[32] = {
+@@ -843,24 +826,20 @@ static const u16 NCT6116_REG_AUTO_TEMP[] = {
+ static const u16 NCT6116_REG_AUTO_PWM[] = {
+       0x164, 0x174, 0x184, 0x1d4, 0x1e4 };
+-static const s8 NCT6116_ALARM_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 7, 8,         /* in0.. in7 */
+-      9, -1, -1, -1, -1, -1, -1,      /* in8..in9 */
+-      -1,                             /* unused */
+-      32, 33, 34, 35, 36,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, 18, -1, -1, -1,         /* temp1..temp6 */
+-      48, -1                          /* intrusion0, intrusion1 */
++static const s8 NCT6116_ALARM_BITS[NUM_ALARM_BITS] = {
++       0,  1,  2,  3,  4,  5,  7,  8,  9, -1, -1, -1,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      32, 33, 34, 35, 36, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      48, -1,                                           /* intr0-intr1  */
+ };
+-static const s8 NCT6116_BEEP_BITS[] = {
+-      0, 1, 2, 3, 4, 5, 7, 8,         /* in0.. in7 */
+-      9, 10, 11, 12, -1, -1, -1,      /* in8..in14 */
+-      32,                             /* global beep enable */
+-      24, 25, 26, 27, 28,             /* fan1..fan5 */
+-      -1, -1, -1,                     /* unused */
+-      16, 17, 18, -1, -1, -1,         /* temp1..temp6 */
+-      34, -1                          /* intrusion0, intrusion1 */
++static const s8 NCT6116_BEEP_BITS[NUM_BEEP_BITS] = {
++       0,  1,  2,  3,  4,  5,  7,  8,  9, 10, 11, 12,   /* in0-in11     */
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* in12-in23    */
++      24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1,   /* fan1-fan12   */
++      16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1,   /* temp1-temp12 */
++      34, -1, 32                                        /* intr0-intr1, beep_en */
+ };
+ static const u16 NCT6116_REG_TSI_TEMP[] = { 0x59, 0x5b };
+diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
+index a84c6ce7275de..c752bc7bbe722 100644
+--- a/drivers/hwmon/nct6775.h
++++ b/drivers/hwmon/nct6775.h
+@@ -239,10 +239,25 @@ nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group *
+ #define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE   0x28
+-#define FAN_ALARM_BASE                16
+-#define TEMP_ALARM_BASE               24
+-#define INTRUSION_ALARM_BASE  30
+-#define BEEP_ENABLE_BASE      15
++/*
++ * ALARM_BITS and BEEP_BITS store bit-index for the mask of the registers
++ * loaded into data->alarm and data->beep.
++ *
++ * Every input register (IN/TEMP/FAN) must have a corresponding
++ *   ALARM/BEEP bit at the same index BITS[BASE + index]
++ * Set value to -1 to disable the visibility of that '*_alarm' attribute and
++ * to pad the bits until the next BASE
++ *
++ * Beep has an additional GLOBAL_BEEP_ENABLE bit
++ */
++#define VIN_ALARM_BASE                 0
++#define FAN_ALARM_BASE                24
++#define TEMP_ALARM_BASE               36
++#define INTRUSION_ALARM_BASE  48
++#define BEEP_ENABLE_BASE      50
++
++#define NUM_ALARM_BITS                (INTRUSION_ALARM_BASE + 4)
++#define NUM_BEEP_BITS         (BEEP_ENABLE_BASE + 1)
+ /*
+  * Not currently used:
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch
new file mode 100644 (file)
index 0000000..718e93b
--- /dev/null
@@ -0,0 +1,53 @@
+From de48d3a067c9c1a4878fbc5eb19bdbad879b177d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 09:54:23 -0700
+Subject: hwmon: (nzxt-smart2) DMA-align output buffer
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
+
+Sashiko reports:
+
+When send_output_report() calls hid_hw_output_report(), the underlying USB
+HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
+
+When the DMA mapping flushes or invalidates the cacheline, it will corrupt
+the adjacent variables (mutex, update_interval) that were modified
+concurrently by the CPU. This causes memory corruption due to cacheline
+sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA
+API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings
+for this violation.
+
+Any operation that triggers send_output_report() (like setting a fan speed
+or updating the interval) causes the USB DMA mapping. On systems with
+non-coherent caches, this structural bug causes immediate and deterministic
+memory corruption.
+
+Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.")
+Cc: Aleksandr Mezin <mezin.alexander@gmail.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nzxt-smart2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
+index 02816538d18eb..90df6a7fc88d7 100644
+--- a/drivers/hwmon/nzxt-smart2.c
++++ b/drivers/hwmon/nzxt-smart2.c
+@@ -203,7 +203,7 @@ struct drvdata {
+        */
+       struct mutex mutex;
+       long update_interval;
+-      u8 output_buffer[OUTPUT_REPORT_SIZE];
++      u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ };
+ static long scale_pwm_value(long val, long orig_max, long new_max)
+-- 
+2.53.0
+
diff --git a/queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..71ed0b8
--- /dev/null
@@ -0,0 +1,39 @@
+From d60fb056a397aa0b45a4adec4fef9348dd17649e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index 1715fafc4152f..d69fd8ca66eb5 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -442,7 +442,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+-- 
+2.53.0
+
diff --git a/queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch b/queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch
new file mode 100644 (file)
index 0000000..b739fec
--- /dev/null
@@ -0,0 +1,841 @@
+From e7b3f889b679a6bcfa8027fb950c463a917c0ab8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Apr 2024 15:19:52 +0000
+Subject: ipv6: introduce dst_rt6_info() helper
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit e8dfd42c17faf183415323db1ef0c977be0d6489 ]
+
+Instead of (struct rt6_info *)dst casts, we can use :
+
+ #define dst_rt6_info(_ptr) \
+         container_of_const(_ptr, struct rt6_info, dst)
+
+Some places needed missing const qualifiers :
+
+ip6_confirm_neigh(), ipv6_anycast_destination(),
+ipv6_unicast_destination(), has_gateway()
+
+v2: added missing parts (David Ahern)
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: e876b75b9020 ("ipvs: fix the checksum validations")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/core/addr.c                |  6 ++--
+ .../ethernet/mellanox/mlxsw/spectrum_span.c   |  2 +-
+ drivers/net/vrf.c                             |  2 +-
+ drivers/net/vxlan/vxlan_core.c                |  2 +-
+ drivers/s390/net/qeth_core.h                  |  4 +--
+ include/net/ip6_fib.h                         |  6 ++--
+ include/net/ip6_route.h                       | 11 ++++----
+ net/bluetooth/6lowpan.c                       |  2 +-
+ net/core/dst_cache.c                          |  2 +-
+ net/core/filter.c                             |  2 +-
+ net/ipv4/ip_tunnel.c                          |  2 +-
+ net/ipv6/icmp.c                               |  8 +++---
+ net/ipv6/ila/ila_lwt.c                        |  4 +--
+ net/ipv6/ip6_output.c                         | 18 ++++++------
+ net/ipv6/ip6mr.c                              |  2 +-
+ net/ipv6/ndisc.c                              |  2 +-
+ net/ipv6/ping.c                               |  2 +-
+ net/ipv6/raw.c                                |  4 +--
+ net/ipv6/route.c                              | 28 +++++++++----------
+ net/ipv6/tcp_ipv6.c                           |  4 +--
+ net/ipv6/udp.c                                | 11 +++-----
+ net/ipv6/xfrm6_policy.c                       |  2 +-
+ net/l2tp/l2tp_ip6.c                           |  2 +-
+ net/mpls/mpls_iptunnel.c                      |  2 +-
+ net/netfilter/ipvs/ip_vs_xmit.c               | 14 +++++-----
+ net/netfilter/nf_flow_table_core.c            |  8 ++----
+ net/netfilter/nf_flow_table_ip.c              |  4 +--
+ net/netfilter/nft_rt.c                        |  2 +-
+ net/sctp/ipv6.c                               |  2 +-
+ net/xfrm/xfrm_policy.c                        |  3 +-
+ 30 files changed, 77 insertions(+), 86 deletions(-)
+
+diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
+index 3e87e92e9993e..579fd879716dc 100644
+--- a/drivers/infiniband/core/addr.c
++++ b/drivers/infiniband/core/addr.c
+@@ -338,15 +338,15 @@ static int dst_fetch_ha(const struct dst_entry *dst,
+ static bool has_gateway(const struct dst_entry *dst, sa_family_t family)
+ {
+-      struct rtable *rt;
+-      struct rt6_info *rt6;
++      const struct rtable *rt;
++      const struct rt6_info *rt6;
+       if (family == AF_INET) {
+               rt = container_of(dst, struct rtable, dst);
+               return rt->rt_uses_gateway;
+       }
+-      rt6 = container_of(dst, struct rt6_info, dst);
++      rt6 = dst_rt6_info(dst);
+       return rt6->rt6i_flags & RTF_GATEWAY;
+ }
+diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+index 8883ef0127477..fa3fef2b74db0 100644
+--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
++++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+@@ -538,7 +538,7 @@ mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
+       if (!dst || dst->error)
+               goto out;
+-      rt6 = container_of(dst, struct rt6_info, dst);
++      rt6 = dst_rt6_info(dst);
+       dev = dst->dev;
+       *saddrp = fl6.saddr;
+diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
+index 51b34882827e9..65668113f715e 100644
+--- a/drivers/net/vrf.c
++++ b/drivers/net/vrf.c
+@@ -655,7 +655,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
+       skb->dev = dev;
+       rcu_read_lock();
+-      nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
++      nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
+       neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
+       if (unlikely(!neigh))
+               neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
+diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
+index 8e3e8a7cad1c9..5a709566e5a28 100644
+--- a/drivers/net/vxlan/vxlan_core.c
++++ b/drivers/net/vxlan/vxlan_core.c
+@@ -2715,7 +2715,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+               }
+               if (!info) {
+-                      u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags;
++                      u32 rt6i_flags = dst_rt6_info(ndst)->rt6i_flags;
+                       err = encap_bypass_if_local(skb, dev, vxlan, dst,
+                                                   dst_port, ifindex, vni,
+diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
+index 613eab7297046..5f17a2a5d0e33 100644
+--- a/drivers/s390/net/qeth_core.h
++++ b/drivers/s390/net/qeth_core.h
+@@ -956,7 +956,7 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb,
+       struct dst_entry *dst = skb_dst(skb);
+       struct rt6_info *rt;
+-      rt = (struct rt6_info *) dst;
++      rt = dst_rt6_info(dst);
+       if (dst) {
+               if (proto == htons(ETH_P_IPV6))
+                       dst = dst_check(dst, rt6_get_cookie(rt));
+@@ -978,7 +978,7 @@ static inline __be32 qeth_next_hop_v4_rcu(struct sk_buff *skb,
+ static inline struct in6_addr *qeth_next_hop_v6_rcu(struct sk_buff *skb,
+                                                   struct dst_entry *dst)
+ {
+-      struct rt6_info *rt = (struct rt6_info *) dst;
++      struct rt6_info *rt = dst_rt6_info(dst);
+       if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
+               return &rt->rt6i_gateway;
+diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
+index fa4e6af382e2a..ef38ee0912e1a 100644
+--- a/include/net/ip6_fib.h
++++ b/include/net/ip6_fib.h
+@@ -240,9 +240,11 @@ struct fib6_result {
+       for (rt = (w)->leaf; rt;                                        \
+            rt = rcu_dereference_protected(rt->fib6_next, 1))
+-static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst)
++#define dst_rt6_info(_ptr) container_of_const(_ptr, struct rt6_info, dst)
++
++static inline struct inet6_dev *ip6_dst_idev(const struct dst_entry *dst)
+ {
+-      return ((struct rt6_info *)dst)->rt6i_idev;
++      return dst_rt6_info(dst)->rt6i_idev;
+ }
+ static inline bool fib6_requires_src(const struct fib6_info *rt)
+diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
+index 4cd0839c86c92..7c0184c09392f 100644
+--- a/include/net/ip6_route.h
++++ b/include/net/ip6_route.h
+@@ -222,12 +222,11 @@ void rt6_uncached_list_del(struct rt6_info *rt);
+ static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb)
+ {
+       const struct dst_entry *dst = skb_dst(skb);
+-      const struct rt6_info *rt6 = NULL;
+       if (dst)
+-              rt6 = container_of(dst, struct rt6_info, dst);
++              return dst_rt6_info(dst);
+-      return rt6;
++      return NULL;
+ }
+ /*
+@@ -239,7 +238,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
+ {
+       struct ipv6_pinfo *np = inet6_sk(sk);
+-      np->dst_cookie = rt6_get_cookie((struct rt6_info *)dst);
++      np->dst_cookie = rt6_get_cookie(dst_rt6_info(dst));
+       sk_setup_caps(sk, dst);
+       np->daddr_cache = daddr;
+ #ifdef CONFIG_IPV6_SUBTREES
+@@ -252,7 +251,7 @@ void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
+ static inline bool ipv6_unicast_destination(const struct sk_buff *skb)
+ {
+-      struct rt6_info *rt = (struct rt6_info *) skb_dst(skb);
++      const struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
+       return rt->rt6i_flags & RTF_LOCAL;
+ }
+@@ -260,7 +259,7 @@ static inline bool ipv6_unicast_destination(const struct sk_buff *skb)
+ static inline bool ipv6_anycast_destination(const struct dst_entry *dst,
+                                           const struct in6_addr *daddr)
+ {
+-      struct rt6_info *rt = (struct rt6_info *)dst;
++      const struct rt6_info *rt = dst_rt6_info(dst);
+       return rt->rt6i_flags & RTF_ANYCAST ||
+               (rt->rt6i_dst.plen < 127 &&
+diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
+index c94e59b1f7133..e2196cf4f6f70 100644
+--- a/net/bluetooth/6lowpan.c
++++ b/net/bluetooth/6lowpan.c
+@@ -140,7 +140,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
+                                                 struct in6_addr *daddr,
+                                                 struct sk_buff *skb)
+ {
+-      struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
++      struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
+       int count = atomic_read(&dev->peer_count);
+       const struct in6_addr *nexthop;
+       struct lowpan_peer *peer;
+diff --git a/net/core/dst_cache.c b/net/core/dst_cache.c
+index 0ccfd5fa5cb9b..b17171345d649 100644
+--- a/net/core/dst_cache.c
++++ b/net/core/dst_cache.c
+@@ -112,7 +112,7 @@ void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
+       idst = this_cpu_ptr(dst_cache->cache);
+       dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
+-                                rt6_get_cookie((struct rt6_info *)dst));
++                                rt6_get_cookie(dst_rt6_info(dst)));
+       idst->in6_saddr = *saddr;
+ }
+ EXPORT_SYMBOL_GPL(dst_cache_set_ip6);
+diff --git a/net/core/filter.c b/net/core/filter.c
+index ce9f079d46e3b..91d337252fc90 100644
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -2226,7 +2226,7 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb,
+       rcu_read_lock();
+       if (!nh) {
+               dst = skb_dst(skb);
+-              nexthop = rt6_nexthop(container_of(dst, struct rt6_info, dst),
++              nexthop = rt6_nexthop(dst_rt6_info(dst),
+                                     &ipv6_hdr(skb)->daddr);
+       } else {
+               nexthop = &nh->ipv6_nh;
+diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
+index dcf9e9c52a22a..5dfb007f7792d 100644
+--- a/net/ipv4/ip_tunnel.c
++++ b/net/ipv4/ip_tunnel.c
+@@ -544,7 +544,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,
+               struct rt6_info *rt6;
+               __be32 daddr;
+-              rt6 = skb_valid_dst(skb) ? (struct rt6_info *)skb_dst(skb) :
++              rt6 = skb_valid_dst(skb) ? dst_rt6_info(skb_dst(skb)) :
+                                          NULL;
+               daddr = md ? dst : tunnel->parms.iph.daddr;
+diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
+index 877cb5e8ded7b..f8f7c1246f43b 100644
+--- a/net/ipv6/icmp.c
++++ b/net/ipv6/icmp.c
+@@ -214,7 +214,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
+       } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
+               res = true;
+       } else {
+-              struct rt6_info *rt = (struct rt6_info *)dst;
++              struct rt6_info *rt = dst_rt6_info(dst);
+               int tmo = net->ipv6.sysctl.icmpv6_time;
+               struct inet_peer *peer;
+@@ -245,7 +245,7 @@ static bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type,
+       dst = ip6_route_output(net, sk, fl6);
+       if (!dst->error) {
+-              struct rt6_info *rt = (struct rt6_info *)dst;
++              struct rt6_info *rt = dst_rt6_info(dst);
+               struct in6_addr prefsrc;
+               rt6_get_prefsrc(rt, &prefsrc);
+@@ -624,7 +624,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+       if (ip6_append_data(sk, icmpv6_getfrag, &msg,
+                           len + sizeof(struct icmp6hdr),
+                           sizeof(struct icmp6hdr),
+-                          &ipc6, &fl6, (struct rt6_info *)dst,
++                          &ipc6, &fl6, dst_rt6_info(dst),
+                           MSG_DONTWAIT)) {
+               ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
+               ip6_flush_pending_frames(sk);
+@@ -817,7 +817,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
+       if (ip6_append_data(sk, icmpv6_getfrag, &msg,
+                           skb->len + sizeof(struct icmp6hdr),
+                           sizeof(struct icmp6hdr), &ipc6, &fl6,
+-                          (struct rt6_info *)dst, MSG_DONTWAIT)) {
++                          dst_rt6_info(dst), MSG_DONTWAIT)) {
+               __ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
+               ip6_flush_pending_frames(sk);
+       } else {
+diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
+index 7397f764c66cc..7d574f5132e2f 100644
+--- a/net/ipv6/ila/ila_lwt.c
++++ b/net/ipv6/ila/ila_lwt.c
+@@ -38,7 +38,7 @@ static inline struct ila_params *ila_params_lwtunnel(
+ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+ {
+       struct dst_entry *orig_dst = skb_dst(skb);
+-      struct rt6_info *rt = (struct rt6_info *)orig_dst;
++      struct rt6_info *rt = dst_rt6_info(orig_dst);
+       struct ila_lwt *ilwt = ila_lwt_lwtunnel(orig_dst->lwtstate);
+       struct dst_entry *dst;
+       int err = -EINVAL;
+@@ -72,7 +72,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+               memset(&fl6, 0, sizeof(fl6));
+               fl6.flowi6_oif = orig_dst->dev->ifindex;
+               fl6.flowi6_iif = LOOPBACK_IFINDEX;
+-              fl6.daddr = *rt6_nexthop((struct rt6_info *)orig_dst,
++              fl6.daddr = *rt6_nexthop(dst_rt6_info(orig_dst),
+                                        &ip6h->daddr);
+               dst = ip6_route_output(net, NULL, &fl6);
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index d8ce708fcb3c4..ad821d362656f 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -121,7 +121,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+       }
+       rcu_read_lock();
+-      nexthop = rt6_nexthop((struct rt6_info *)dst, daddr);
++      nexthop = rt6_nexthop(dst_rt6_info(dst), daddr);
+       neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
+       if (unlikely(IS_ERR_OR_NULL(neigh))) {
+@@ -611,7 +611,7 @@ int ip6_forward(struct sk_buff *skb)
+                *      send a redirect.
+                */
+-              rt = (struct rt6_info *) dst;
++              rt = dst_rt6_info(dst);
+               if (rt->rt6i_flags & RTF_GATEWAY)
+                       target = &rt->rt6i_gateway;
+               else
+@@ -866,7 +866,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+                int (*output)(struct net *, struct sock *, struct sk_buff *))
+ {
+       struct sk_buff *frag;
+-      struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
++      struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
+       struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
+                               inet6_sk(skb->sk) : NULL;
+       bool mono_delivery_time = skb->mono_delivery_time;
+@@ -1074,7 +1074,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
+               return NULL;
+       }
+-      rt = (struct rt6_info *)dst;
++      rt = dst_rt6_info(dst);
+       /* Yes, checking route validity in not connected
+        * case is not very simple. Take into account,
+        * that we do not support routing by source, TOS,
+@@ -1129,7 +1129,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
+               struct rt6_info *rt;
+               *dst = ip6_route_output(net, sk, fl6);
+-              rt = (*dst)->error ? NULL : (struct rt6_info *)*dst;
++              rt = (*dst)->error ? NULL : dst_rt6_info(*dst);
+               rcu_read_lock();
+               from = rt ? rcu_dereference(rt->from) : NULL;
+@@ -1171,7 +1171,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
+        * dst entry and replace it instead with the
+        * dst entry of the nexthop router
+        */
+-      rt = (struct rt6_info *) *dst;
++      rt = dst_rt6_info(*dst);
+       rcu_read_lock();
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev,
+                                     rt6_nexthop(rt, &fl6->daddr));
+@@ -1437,7 +1437,7 @@ static int __ip6_append_data(struct sock *sk,
+       int offset = 0;
+       bool zc = false;
+       u32 tskey = 0;
+-      struct rt6_info *rt = (struct rt6_info *)cork->dst;
++      struct rt6_info *rt = dst_rt6_info(cork->dst);
+       bool paged, hold_tskey, extra_uref = false;
+       struct ipv6_txoptions *opt = v6_cork->opt;
+       int csummode = CHECKSUM_NONE;
+@@ -1869,7 +1869,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,
+       struct net *net = sock_net(sk);
+       struct ipv6hdr *hdr;
+       struct ipv6_txoptions *opt = v6_cork->opt;
+-      struct rt6_info *rt = (struct rt6_info *)cork->base.dst;
++      struct rt6_info *rt = dst_rt6_info(cork->base.dst);
+       struct flowi6 *fl6 = &cork->fl.u.ip6;
+       unsigned char proto = fl6->flowi6_proto;
+@@ -1941,7 +1941,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,
+ int ip6_send_skb(struct sk_buff *skb)
+ {
+       struct net *net = sock_net(skb->sk);
+-      struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
++      struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
+       int err;
+       rcu_read_lock();
+diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
+index 06f66531628fe..00774cfa011af 100644
+--- a/net/ipv6/ip6mr.c
++++ b/net/ipv6/ip6mr.c
+@@ -2301,7 +2301,7 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
+       int err;
+       struct mr_table *mrt;
+       struct mfc6_cache *cache;
+-      struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
++      struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
+       rcu_read_lock();
+       mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index 85f7798d3e55c..a53d2a6a99f85 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1717,7 +1717,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       if (IS_ERR(dst))
+               return;
+-      rt = (struct rt6_info *) dst;
++      rt = dst_rt6_info(dst);
+       if (rt->rt6i_flags & RTF_GATEWAY) {
+               ND_PRINTK(2, warn,
+diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
+index a5d7d1915ba7e..c9550fddc6b3d 100644
+--- a/net/ipv6/ping.c
++++ b/net/ipv6/ping.c
+@@ -154,7 +154,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+       dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false);
+       if (IS_ERR(dst))
+               return PTR_ERR(dst);
+-      rt = (struct rt6_info *) dst;
++      rt = dst_rt6_info(dst);
+       if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+               fl6.flowi6_oif = np->mcast_oif;
+diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
+index f6f18196ed6e4..7b17f1b2a6128 100644
+--- a/net/ipv6/raw.c
++++ b/net/ipv6/raw.c
+@@ -591,7 +591,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
+       struct ipv6hdr *iph;
+       struct sk_buff *skb;
+       int err;
+-      struct rt6_info *rt = (struct rt6_info *)*dstp;
++      struct rt6_info *rt = dst_rt6_info(*dstp);
+       int hlen = LL_RESERVED_SPACE(rt->dst.dev);
+       int tlen = rt->dst.dev->needed_tailroom;
+@@ -915,7 +915,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+               ipc6.opt = opt;
+               lock_sock(sk);
+               err = ip6_append_data(sk, raw6_getfrag, &rfv,
+-                      len, 0, &ipc6, &fl6, (struct rt6_info *)dst,
++                      len, 0, &ipc6, &fl6, dst_rt6_info(dst),
+                       msg->msg_flags);
+               if (err)
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index f047000e2c55c..ea703c569d4b4 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -227,7 +227,7 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
+                                             struct sk_buff *skb,
+                                             const void *daddr)
+ {
+-      const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
++      const struct rt6_info *rt = dst_rt6_info(dst);
+       return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
+                               dst->dev, skb, daddr);
+@@ -235,8 +235,8 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
+ static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
+ {
++      const struct rt6_info *rt = dst_rt6_info(dst);
+       struct net_device *dev = dst->dev;
+-      struct rt6_info *rt = (struct rt6_info *)dst;
+       daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr);
+       if (!daddr)
+@@ -356,7 +356,7 @@ EXPORT_SYMBOL(ip6_dst_alloc);
+ static void ip6_dst_destroy(struct dst_entry *dst)
+ {
+-      struct rt6_info *rt = (struct rt6_info *)dst;
++      struct rt6_info *rt = dst_rt6_info(dst);
+       struct fib6_info *from;
+       struct inet6_dev *idev;
+@@ -376,7 +376,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)
+ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
+                          int how)
+ {
+-      struct rt6_info *rt = (struct rt6_info *)dst;
++      struct rt6_info *rt = dst_rt6_info(dst);
+       struct inet6_dev *idev = rt->rt6i_idev;
+       struct fib6_info *from;
+@@ -1324,7 +1324,7 @@ struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
+       dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
+       if (dst->error == 0)
+-              return (struct rt6_info *) dst;
++              return dst_rt6_info(dst);
+       dst_release(dst);
+@@ -2681,7 +2681,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net,
+       rcu_read_lock();
+       dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
+-      rt6 = (struct rt6_info *)dst;
++      rt6 = dst_rt6_info(dst);
+       /* For dst cached in uncached_list, refcnt is already taken. */
+       if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) {
+               dst = &net->ipv6.ip6_null_entry->dst;
+@@ -2695,7 +2695,7 @@ EXPORT_SYMBOL_GPL(ip6_route_output_flags);
+ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
+ {
+-      struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
++      struct rt6_info *rt, *ort = dst_rt6_info(dst_orig);
+       struct net_device *loopback_dev = net->loopback_dev;
+       struct dst_entry *new = NULL;
+@@ -2778,7 +2778,7 @@ INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst,
+       struct fib6_info *from;
+       struct rt6_info *rt;
+-      rt = container_of(dst, struct rt6_info, dst);
++      rt = dst_rt6_info(dst);
+       if (rt->sernum)
+               return rt6_is_valid(rt) ? dst : NULL;
+@@ -2807,7 +2807,7 @@ EXPORT_INDIRECT_CALLABLE(ip6_dst_check);
+ static void ip6_negative_advice(struct sock *sk,
+                               struct dst_entry *dst)
+ {
+-      struct rt6_info *rt = (struct rt6_info *) dst;
++      struct rt6_info *rt = dst_rt6_info(dst);
+       if (rt->rt6i_flags & RTF_CACHE) {
+               rcu_read_lock();
+@@ -2830,7 +2830,7 @@ static void ip6_link_failure(struct sk_buff *skb)
+       icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
+-      rt = (struct rt6_info *) skb_dst(skb);
++      rt = dst_rt6_info(skb_dst(skb));
+       if (rt) {
+               rcu_read_lock();
+               if (rt->rt6i_flags & RTF_CACHE) {
+@@ -2886,7 +2886,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
+                                bool confirm_neigh)
+ {
+       const struct in6_addr *daddr, *saddr;
+-      struct rt6_info *rt6 = (struct rt6_info *)dst;
++      struct rt6_info *rt6 = dst_rt6_info(dst);
+       /* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU)
+        * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
+@@ -4214,7 +4214,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
+               }
+       }
+-      rt = (struct rt6_info *) dst;
++      rt = dst_rt6_info(dst);
+       if (rt->rt6i_flags & RTF_REJECT) {
+               net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
+               return;
+@@ -5665,7 +5665,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
+                        int iif, int type, u32 portid, u32 seq,
+                        unsigned int flags)
+ {
+-      struct rt6_info *rt6 = (struct rt6_info *)dst;
++      struct rt6_info *rt6 = dst_rt6_info(dst);
+       struct rt6key *rt6_dst, *rt6_src;
+       u32 *pmetrics, table, rt6_flags;
+       unsigned char nh_flags = 0;
+@@ -6182,7 +6182,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+       }
+-      rt = container_of(dst, struct rt6_info, dst);
++      rt = dst_rt6_info(dst);
+       if (rt->dst.error) {
+               err = rt->dst.error;
+               ip6_rt_put(rt);
+diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
+index a1e31fe596708..3d909982d8187 100644
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -106,11 +106,9 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+       struct dst_entry *dst = skb_dst(skb);
+       if (dst && dst_hold_safe(dst)) {
+-              const struct rt6_info *rt = (const struct rt6_info *)dst;
+-
+               rcu_assign_pointer(sk->sk_rx_dst, dst);
+               sk->sk_rx_dst_ifindex = skb->skb_iif;
+-              sk->sk_rx_dst_cookie = rt6_get_cookie(rt);
++              sk->sk_rx_dst_cookie = rt6_get_cookie(dst_rt6_info(dst));
+       }
+ }
+diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
+index 184fc7a5e4d17..96a46dc473360 100644
+--- a/net/ipv6/udp.c
++++ b/net/ipv6/udp.c
+@@ -925,11 +925,8 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
+ static void udp6_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+ {
+-      if (udp_sk_rx_dst_set(sk, dst)) {
+-              const struct rt6_info *rt = (const struct rt6_info *)dst;
+-
+-              sk->sk_rx_dst_cookie = rt6_get_cookie(rt);
+-      }
++      if (udp_sk_rx_dst_set(sk, dst))
++              sk->sk_rx_dst_cookie = rt6_get_cookie(dst_rt6_info(dst));
+ }
+ /* wrapper for udp_queue_rcv_skb tacking care of csum conversion and
+@@ -1593,7 +1590,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+               skb = ip6_make_skb(sk, getfrag, msg, ulen,
+                                  sizeof(struct udphdr), &ipc6,
+-                                 (struct rt6_info *)dst,
++                                 dst_rt6_info(dst),
+                                  msg->msg_flags, &cork);
+               err = PTR_ERR(skb);
+               if (!IS_ERR_OR_NULL(skb))
+@@ -1620,7 +1617,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+               ipc6.dontfrag = np->dontfrag;
+       up->len += ulen;
+       err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr),
+-                            &ipc6, fl6, (struct rt6_info *)dst,
++                            &ipc6, fl6, dst_rt6_info(dst),
+                             corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
+       if (err)
+               udp_v6_flush_pending_frames(sk);
+diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
+index c945473f6e5fe..0342efd0a0b0a 100644
+--- a/net/ipv6/xfrm6_policy.c
++++ b/net/ipv6/xfrm6_policy.c
+@@ -80,7 +80,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr,
+ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
+                         const struct flowi *fl)
+ {
+-      struct rt6_info *rt = (struct rt6_info *)xdst->route;
++      struct rt6_info *rt = dst_rt6_info(xdst->route);
+       xdst->u.dst.dev = dev;
+       netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
+diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
+index bb92dc8b82f39..e282b91b396c7 100644
+--- a/net/l2tp/l2tp_ip6.c
++++ b/net/l2tp/l2tp_ip6.c
+@@ -633,7 +633,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+       ulen = len + (skb_queue_empty(&sk->sk_write_queue) ? transhdrlen : 0);
+       err = ip6_append_data(sk, ip_generic_getfrag, msg,
+                             ulen, transhdrlen, &ipc6,
+-                            &fl6, (struct rt6_info *)dst,
++                            &fl6, dst_rt6_info(dst),
+                             msg->msg_flags);
+       if (err)
+               ip6_flush_pending_frames(sk);
+diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
+index ef59e25dc4827..8985abcb7a058 100644
+--- a/net/mpls/mpls_iptunnel.c
++++ b/net/mpls/mpls_iptunnel.c
+@@ -92,7 +92,7 @@ static int mpls_xmit(struct sk_buff *skb)
+                       ttl = net->mpls.default_ttl;
+               else
+                       ttl = ipv6_hdr(skb)->hop_limit;
+-              rt6 = (struct rt6_info *)dst;
++              rt6 = dst_rt6_info(dst);
+       } else {
+               goto drop;
+       }
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 9793eb8884373..0fc8cd2d4859e 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -184,7 +184,7 @@ static inline bool crosses_local_route_boundary(int skb_af, struct sk_buff *skb,
+                       (!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
+                       (addr_type & IPV6_ADDR_LOOPBACK);
+               old_rt_is_local = __ip_vs_is_local_route6(
+-                      (struct rt6_info *)skb_dst(skb));
++                      dst_rt6_info(skb_dst(skb)));
+       } else
+ #endif
+       {
+@@ -484,7 +484,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
+       if (dest) {
+               dest_dst = __ip_vs_dst_check(dest);
+               if (likely(dest_dst))
+-                      rt = (struct rt6_info *) dest_dst->dst_cache;
++                      rt = dst_rt6_info(dest_dst->dst_cache);
+               else {
+                       u32 cookie;
+@@ -504,7 +504,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
+                               ip_vs_dest_dst_free(dest_dst);
+                               goto err_unreach;
+                       }
+-                      rt = (struct rt6_info *) dst;
++                      rt = dst_rt6_info(dst);
+                       cookie = rt6_get_cookie(rt);
+                       __ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
+                       spin_unlock_bh(&dest->dst_lock);
+@@ -520,7 +520,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
+                                             rt_mode);
+               if (!dst)
+                       goto err_unreach;
+-              rt = (struct rt6_info *) dst;
++              rt = dst_rt6_info(dst);
+       }
+       local = __ip_vs_is_local_route6(rt);
+@@ -879,7 +879,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                                     IP_VS_RT_MODE_RDR);
+       if (local < 0)
+               goto tx_error;
+-      rt = (struct rt6_info *) skb_dst(skb);
++      rt = dst_rt6_info(skb_dst(skb));
+       /*
+        * Avoid duplicate tuple in reply direction for NAT traffic
+        * to local address when connection is sync-ed
+@@ -1315,7 +1315,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (local)
+               return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1);
+-      rt = (struct rt6_info *) skb_dst(skb);
++      rt = dst_rt6_info(skb_dst(skb));
+       tdev = rt->dst.dev;
+       /*
+@@ -1636,7 +1636,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                                     &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+-      rt = (struct rt6_info *) skb_dst(skb);
++      rt = dst_rt6_info(skb_dst(skb));
+       /*
+        * Avoid duplicate tuple in reply direction for NAT traffic
+        * to local address when connection is sync-ed
+diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
+index 99195cf6b2657..9edc627d94b9e 100644
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -77,12 +77,8 @@ EXPORT_SYMBOL_GPL(flow_offload_alloc);
+ static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)
+ {
+-      const struct rt6_info *rt;
+-
+-      if (flow_tuple->l3proto == NFPROTO_IPV6) {
+-              rt = (const struct rt6_info *)flow_tuple->dst_cache;
+-              return rt6_get_cookie(rt);
+-      }
++      if (flow_tuple->l3proto == NFPROTO_IPV6)
++              return rt6_get_cookie(dst_rt6_info(flow_tuple->dst_cache));
+       return 0;
+ }
+diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
+index 34be2c9bc39d8..523228e969ab4 100644
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -665,7 +665,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
+               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+       if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+-              rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++              rt = dst_rt6_info(tuplehash->tuple.dst_cache);
+               memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+               IP6CB(skb)->iif = skb->dev->ifindex;
+               IP6CB(skb)->flags = IP6SKB_FORWARDED;
+@@ -674,7 +674,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
+       switch (tuplehash->tuple.xmit_type) {
+       case FLOW_OFFLOAD_XMIT_NEIGH:
+-              rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++              rt = dst_rt6_info(tuplehash->tuple.dst_cache);
+               outdev = rt->dst.dev;
+               skb->dev = outdev;
+               nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
+diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c
+index 7d21e16499bfa..eea3ee809c47d 100644
+--- a/net/netfilter/nft_rt.c
++++ b/net/netfilter/nft_rt.c
+@@ -80,7 +80,7 @@ void nft_rt_get_eval(const struct nft_expr *expr,
+               if (nft_pf(pkt) != NFPROTO_IPV6)
+                       goto err;
+-              memcpy(dest, rt6_nexthop((struct rt6_info *)dst,
++              memcpy(dest, rt6_nexthop(dst_rt6_info(dst),
+                                        &ipv6_hdr(skb)->daddr),
+                      sizeof(struct in6_addr));
+               break;
+diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
+index a1cb8ac0408af..be190f5696d88 100644
+--- a/net/sctp/ipv6.c
++++ b/net/sctp/ipv6.c
+@@ -416,7 +416,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
+       if (!IS_ERR_OR_NULL(dst)) {
+               struct rt6_info *rt;
+-              rt = (struct rt6_info *)dst;
++              rt = dst_rt6_info(dst);
+               t->dst_cookie = rt6_get_cookie(rt);
+               pr_debug("rt6_dst:%pI6/%d rt6_src:%pI6\n",
+                        &rt->rt6i_dst.addr, rt->rt6i_dst.plen,
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index dbaef78f43c21..2a47f5b5776e8 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -2526,8 +2526,7 @@ static void xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst,
+                          int nfheader_len)
+ {
+       if (dst->ops->family == AF_INET6) {
+-              struct rt6_info *rt = (struct rt6_info *)dst;
+-              path->path_cookie = rt6_get_cookie(rt);
++              path->path_cookie = rt6_get_cookie(dst_rt6_info(dst));
+               path->u.rt6.rt6i_nfheader_len = nfheader_len;
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
new file mode 100644 (file)
index 0000000..e35a8d4
--- /dev/null
@@ -0,0 +1,335 @@
+From e0442e13a6c31b6c234b61213ee4c237290c796d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:17 +0300
+Subject: ipvs: do not mangle ICMP replies for non-first fragments
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
+
+Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the
+payload for embedded non-first IPv4 fragments. The problem is
+in the very old inverted pp->dont_defrag check which should not
+continue when embedded is a non-first TCP/UDP/SCTP fragment.
+
+Check for embedded non-first fragment is also missing from
+ip_vs_out_icmp_v6(), it is needed before any connection
+lookups that expect ports after the network headers.
+
+Drop the blocking code from ip_vs_in_icmp_v6() which prevents
+ICMPv6 from local clients to use non-MASQ forwarding.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h             | 11 +++---
+ net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++---------------------
+ net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++----
+ 3 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index db5e3832ccd46..344f0082d3041 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1497,8 +1497,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, unsigned int toff,
+-                  unsigned int wlen, unsigned int hooknum,
+-                  struct ip_vs_iphdr *ciph);
++                  unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1512,8 +1511,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, unsigned int toff,
+-                     unsigned int wlen, unsigned int hooknum,
+-                     struct ip_vs_iphdr *ciph);
++                     unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1578,12 +1576,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir, unsigned int toff);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff,
++                  bool has_ports);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph);
++                     bool has_ports, struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 01a87530515d5..27c096b070774 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout, unsigned int toff)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff,
++                  bool has_ports)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+       struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+-          IPPROTO_SCTP == ciph->protocol) {
++      if (has_ports) {
+               __be16 *ports = (void *)ciph + ciph->ihl*4;
+               if (inout)
+@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int inout, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph)
++                     bool has_ports, struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      int protocol;
+       struct icmp6hdr *icmph;
+       struct ipv6hdr *cih;
+       icmph = (struct icmp6hdr *)(skb->data + toff);
+       cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ciph->protocol;
+-
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+               cih->daddr = cp->vaddr.in6;
+@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!ciph->fragoffs &&
+-          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
+-           protocol == IPPROTO_SCTP)) {
++      if (has_ports) {
+               __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       unsigned int ctoff = ciph->len;
++      bool has_ports = false;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       }
+       if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+-          ciph->protocol == IPPROTO_SCTP)
++          ciph->protocol == IPPROTO_SCTP) {
+               ctoff += 2 * sizeof(__u16);
++              has_ports = true;
++      }
+       if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!pp)
+               return NF_ACCEPT;
++      /* Is the embedded protocol header present? */
++      if (unlikely(ciph.fragoffs && !pp->dont_defrag))
++              return NF_ACCEPT;
++
+       /* The embedded headers contain source and dest in reverse order */
+       cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
+                            ipvs, AF_INET6, skb, &ciph);
+@@ -1692,8 +1693,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       pp = pd->pp;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1701,7 +1701,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       offset2 = offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
+-      offset = ciph.len;
+       /* The embedded headers contain source and dest in reverse order.
+        * For IPIP/UDP/GRE tunnel this is error for request, not for reply.
+@@ -1795,11 +1794,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+-          IPPROTO_SCTP == cih->protocol)
+-              offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
+-                                &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1859,8 +1854,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       pp = pd->pp;
+-      /* Cannot handle fragmented embedded protocol */
+-      if (ciph.fragoffs)
++      /* Is the embedded protocol header present? */
++      if (ciph.fragoffs && !pp->dont_defrag)
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+@@ -1884,13 +1879,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               new_cp = true;
+       }
+-      /* VS/TUN, VS/DR and LOCALNODE just let it go */
+-      if ((hooknum == NF_INET_LOCAL_OUT) &&
+-          (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+-              verdict = NF_ACCEPT;
+-              goto out;
+-      }
+-
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+@@ -1906,14 +1894,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      /* Need to mangle contained IPv6 header in ICMPv6 packet */
+-      offset = ciph.len;
+-      if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+-          IPPROTO_SCTP == ciph.protocol)
+-              offset += 2 * sizeof(__u16); /* Also mangle ports */
+-
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
+-                                   &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 06179804b0da6..db2f090e2f8f9 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1512,13 +1512,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+       int local;
+       int rt_mode, was_input;
++      bool has_ports = false;
++      unsigned int wlen;
+       EnterFunction(10);
+@@ -1576,6 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1583,7 +1591,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1603,10 +1611,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
++      bool has_ports = false;
+       struct rt6_info *rt;    /* Route to the other host */
++      unsigned int wlen;
+       int rc;
+       int local;
+       int rt_mode;
+@@ -1666,6 +1675,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1673,7 +1689,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch
new file mode 100644 (file)
index 0000000..98c3437
--- /dev/null
@@ -0,0 +1,624 @@
+From c53e670b99be3aa6818a773f600de70bf4ed972c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:16 +0300
+Subject: ipvs: fix places with wrong packet offsets
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
+
+The offsets we use to packet headers and payloads should be
+based on skb->data. We even already respect non-zero
+network offset in ip_vs_fill_iph_skb() but some places
+do it wrongly and support only zero offset which is expected
+for the IP layer where IPVS has hooks.
+
+Change all places that instead of skb->data use offsets based
+on the network header (skb_network_header, ip_hdr, etc) because
+this doubles the network offset as noted by Sashiko.
+
+For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header
+parsing done by the caller.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   |  15 +--
+ net/netfilter/ipvs/ip_vs_app.c        |   4 +-
+ net/netfilter/ipvs/ip_vs_core.c       | 133 +++++++++++++-------------
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_xmit.c       |  26 ++---
+ 7 files changed, 97 insertions(+), 93 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 22793d64a1295..db5e3832ccd46 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1496,8 +1496,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                  struct ip_vs_protocol *pp, int offset,
+-                  unsigned int hooknum, struct ip_vs_iphdr *iph);
++                  struct ip_vs_protocol *pp, unsigned int toff,
++                  unsigned int wlen, unsigned int hooknum,
++                  struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1510,8 +1511,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                     struct ip_vs_protocol *pp, int offset,
+-                     unsigned int hooknum, struct ip_vs_iphdr *iph);
++                     struct ip_vs_protocol *pp, unsigned int toff,
++                     unsigned int wlen, unsigned int hooknum,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1576,11 +1578,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                     struct ip_vs_conn *cp, int dir);
++                     struct ip_vs_conn *cp, int dir, unsigned int toff,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
+index f9132b359f0c6..0c690a30a85dc 100644
+--- a/net/netfilter/ipvs/ip_vs_app.c
++++ b/net/netfilter/ipvs/ip_vs_app.c
+@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 4ab62ed6e4333..01a87530515d5 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+-      unsigned int icmp_offset = iph->ihl*4;
+-      struct icmphdr *icmph    = (struct icmphdr *)(skb_network_header(skb) +
+-                                                    icmp_offset);
++      struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+       struct iphdr *ciph       = (struct iphdr *)(icmph + 1);
++      unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+       if (inout) {
+               iph->saddr = cp->vaddr.ip;
+@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->checksum = 0;
+-      icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
++      icmph->checksum = ip_vs_checksum_complete(skb, toff);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered outgoing ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered outgoing ICMP");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered incoming ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered incoming ICMP");
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                     struct ip_vs_conn *cp, int inout, unsigned int toff,
++                     struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      unsigned int icmp_offset = 0;
+-      unsigned int offs        = 0; /* header offset*/
+       int protocol;
+       struct icmp6hdr *icmph;
+-      struct ipv6hdr *ciph;
+-      unsigned short fragoffs;
++      struct ipv6hdr *cih;
+-      ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+-      icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+-      offs = icmp_offset + sizeof(struct icmp6hdr);
+-      ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
++      icmph = (struct icmp6hdr *)(skb->data + toff);
++      cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
++      protocol = ciph->protocol;
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+-              ciph->daddr = cp->vaddr.in6;
++              cih->daddr = cp->vaddr.in6;
+       } else {
+               iph->daddr = cp->daddr.in6;
+-              ciph->saddr = cp->daddr.in6;
++              cih->saddr = cp->daddr.in6;
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-                        IPPROTO_SCTP == protocol)) {
+-              __be16 *ports = (void *)(skb_network_header(skb) + offs);
++      if (!ciph->fragoffs &&
++          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
++           protocol == IPPROTO_SCTP)) {
++              __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+                             ntohs(inout ? ports[1] : ports[0]),
+@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
+-                                            skb->len - icmp_offset,
++                                            skb->len - toff,
+                                             IPPROTO_ICMPV6, 0);
+-      skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
++      skb->csum_start = skb_headroom(skb) + toff;
+       skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered outgoing ICMPv6");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered incoming ICMPv6");
+ }
+ #endif
+@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+  */
+ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+-                              __u8 protocol, struct ip_vs_conn *cp,
++                              struct ip_vs_conn *cp,
+                               struct ip_vs_protocol *pp,
+-                              unsigned int offset, unsigned int ihl,
+-                              unsigned int hooknum)
++                              struct ip_vs_iphdr *ciph,
++                              unsigned int toff, unsigned int hooknum)
+ {
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
++      unsigned int ctoff = ciph->len;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
++      if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+               goto out;
+       }
+-      if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-          IPPROTO_SCTP == protocol)
+-              offset += 2 * sizeof(__u16);
+-      if (skb_ensure_writable(skb, offset))
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP)
++              ctoff += 2 * sizeof(__u16);
++      if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+  *    Currently handles error types - unreachable, quench, ttl exceeded.
+  */
+ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+-                        int *related, unsigned int hooknum)
++                        int *related, unsigned int hooknum,
++                        struct ip_vs_iphdr *ipvsh)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = ipvsh->len;
++      offset = ipvsh->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &ipvsh->saddr.ip, &ipvsh->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* Now find the contained IP header */
+       offset += sizeof(_icmph);
+       cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+-      if (cih == NULL)
++      if (!(cih && cih->version == 4 && cih->ihl >= 5))
+               return NF_ACCEPT; /* The packet looks wrong, ignore */
+       pp = ip_vs_proto_get(cih->protocol);
+@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!cp)
+               return NF_ACCEPT;
+-      snet.ip = iph->saddr;
+-      return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+-                                  pp, ciph.len, ihl, hooknum);
++      snet.ip = ipvsh->saddr.ip;
++      return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
++                                  hooknum);
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       struct ip_vs_conn *cp;
+       struct ip_vs_protocol *pp;
+       union nf_inet_addr snet;
+-      unsigned int offset;
+       *related = 1;
+       ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
+@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       snet.in6 = ciph.saddr.in6;
+-      offset = ciph.len;
+-      return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+-                                  pp, offset, ipvsh->len, hooknum);
++      return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
++                                  ipvsh->len, hooknum);
+ }
+ #endif
+@@ -1377,7 +1372,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
+ #endif
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+-                      int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
++                      int verdict = ip_vs_out_icmp(ipvs, skb, &related,
++                                                   hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -1581,9 +1577,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
+  */
+ static int
+ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+-            unsigned int hooknum)
++            unsigned int hooknum, struct ip_vs_iphdr *iph)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1593,7 +1588,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       unsigned int offset, offset2, ihl, verdict;
+       bool tunnel, new_cp = false;
+       union nf_inet_addr *raddr;
+-      char *outer_proto = "IPIP";
++      char *outer_proto __maybe_unused = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
+@@ -1603,17 +1598,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = iph->len;
++      offset = iph->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &iph->saddr.ip, &iph->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1730,7 +1727,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+           !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+-                        &iph->saddr);
++                        &iph->saddr.ip);
+               goto out;
+       }
+@@ -1801,7 +1798,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+           IPPROTO_SCTP == cih->protocol)
+               offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
++                                &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1914,7 +1912,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+           IPPROTO_SCTP == ciph.protocol)
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
++                                   &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1993,7 +1992,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+-                                                  hooknum);
++                                                  hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -2129,6 +2128,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+                  const struct nf_hook_state *state)
+ {
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
++      struct ip_vs_iphdr iphdr;
+       int r;
+       /* ipvs enabled in this netns ? */
+@@ -2138,10 +2138,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+       if (state->pf == NFPROTO_IPV4) {
+               if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
+                       return NF_ACCEPT;
++              ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
+ #ifdef CONFIG_IP_VS_IPV6
+       } else {
+-              struct ip_vs_iphdr iphdr;
+-
+               ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
+               if (iphdr.protocol != IPPROTO_ICMPV6)
+@@ -2151,7 +2150,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+ #endif
+       }
+-      return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
++      return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
+ }
+ static const struct nf_hook_ops ip_vs_ops4[] = {
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index f6f732b7dfa86..3dbd3096e1637 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index bf31127338aa0..1ac9c233537d3 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->source = cp->vport;
+       /* Adjust TCP checksums */
+@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index 40d30649b3048..96ac882df15c1 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->source = cp->vport;
+       /*
+@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 0fc8cd2d4859e..06179804b0da6 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1511,8 +1511,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+  */
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *iph)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+@@ -1526,7 +1527,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, iph);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1544,7 +1545,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+-                                 NULL, iph);
++                                 NULL, ciph);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
+@@ -1576,13 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1601,8 +1602,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ #ifdef CONFIG_IP_VS_IPV6
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *ipvsh)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rt6_info *rt;    /* Route to the other host */
+       int rc;
+@@ -1616,7 +1618,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, ipvsh);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1633,7 +1635,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+-                                    &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
++                                    &cp->daddr.in6, NULL, ciph, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = dst_rt6_info(skb_dst(skb));
+@@ -1665,13 +1667,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.1/ipvs-fix-the-checksum-validations.patch b/queue-6.1/ipvs-fix-the-checksum-validations.patch
new file mode 100644 (file)
index 0000000..f217309
--- /dev/null
@@ -0,0 +1,389 @@
+From 2bdcbfe93c48958393a87764b9b1f310fcc0c1c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:15 +0300
+Subject: ipvs: fix the checksum validations
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
+
+ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6
+packets from clients. In fact, as for TCP/UDP we should
+validate the checksum for ICMP packets only when we
+mangle the packets on MASQ or on reply for tunnel.
+
+Also, Sashiko points out that handle_response_icmp() being
+common for IPv4 and IPv6 is missing the pseudo-header
+calculation while validating ICMPv6 messages from real
+servers which is a problem if checksum is not validated
+by the hardware.
+
+Fix the problems by creating ip_vs_checksum_common_check()
+helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6.
+Rely on the nf_checksum() for validating the ICMP messages
+but use it also for TCP and UDP.
+
+Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
+
+IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum
+validation on LOCAL_OUT (local clients or local real
+servers) and on FORWARD (traffic from servers on LAN).
+Do it only on LOCAL_IN, in case nf_checksum() is not
+called on PRE_ROUTING.
+
+Also, ip_vs_checksum_complete() can be marked static.
+
+Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6")
+Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   | 31 +++++++++++++++--
+ net/netfilter/ipvs/ip_vs_core.c       | 20 +++++++++--
+ net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++----
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  | 44 +++++------------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  | 50 ++++++---------------------
+ 5 files changed, 74 insertions(+), 86 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 35961160ebdec..22793d64a1295 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -24,7 +24,9 @@
+ #include <linux/netfilter.h>          /* for union nf_inet_addr */
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>                       /* for struct ipv6hdr */
++#include <net/route.h>
+ #include <net/ipv6.h>
++#include <net/ip6_fib.h>
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ #include <net/netfilter/nf_conntrack.h>
+ #endif
+@@ -1581,8 +1583,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+-
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+ {
+       __be32 diff[2] = { ~old, new };
+@@ -1608,6 +1608,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
+       return csum_partial(diff, sizeof(diff), oldsum);
+ }
++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
++{
++      /* Checksum unnecessary or already validated? */
++      if (skb_csum_unnecessary(skb))
++              return false;
++      /* LOCAL_OUT ? */
++      if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
++              return false;
++      /* !LOCAL_IN (FORWARD) ? */
++      if (af == AF_INET6) {
++              if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
++                      return false;
++      } else {
++              if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
++                      return false;
++      }
++      return true;
++}
++
++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
++                                             int offset, int proto, int af)
++{
++      if (!ip_vs_checksum_needed(skb, af))
++              return true;
++      return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
++}
++
+ /* Forget current conntrack (unconfirmed) and attach notrack entry */
+ static inline void ip_vs_notrack(struct sk_buff *skb)
+ {
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 4b03857e41d77..4ab62ed6e4333 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+ {
+       return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
+ }
+@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               unsigned int offset, unsigned int ihl,
+                               unsigned int hooknum)
+ {
++      int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+@@ -1725,7 +1726,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
++          !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+                         &iph->saddr);
+@@ -1891,6 +1893,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               goto out;
+       }
++      verdict = NF_DROP;
++
++      /* Ensure the checksum is correct */
++      if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
++          !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
++                                       AF_INET6)) {
++              /* Failed checksum! */
++              IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
++                        &iph->saddr);
++              goto out;
++      }
++
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index c67317be17dfa..f6f732b7dfa86 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -11,7 +11,7 @@
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff);
++              struct ip_vs_iphdr *iph);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff)
++              struct ip_vs_iphdr *iph)
+ {
++      unsigned int sctphoff = iph->len;
+       struct sctphdr *sh;
+       __le32 cmp, val;
++      if (!ip_vs_checksum_needed(skb, af))
++              return 1;
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+       if (val != cmp) {
+               /* CRC failure, dump it. */
+-              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                              "Failed checksum for");
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
+               return 0;
+       }
+       return 1;
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index b382810156b2c..bf31127338aa0 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -30,7 +30,7 @@
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff)
++             struct ip_vs_iphdr *iph)
+ {
+-      switch (skb->ip_summed) {
+-      case CHECKSUM_NONE:
+-              skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+-              fallthrough;
+-      case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-              if (af == AF_INET6) {
+-                      if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                          &ipv6_hdr(skb)->daddr,
+-                                          skb->len - tcphoff,
+-                                          IPPROTO_TCP,
+-                                          skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              } else
+-#endif
+-                      if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                            ip_hdr(skb)->daddr,
+-                                            skb->len - tcphoff,
+-                                            ip_hdr(skb)->protocol,
+-                                            skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              break;
+-      default:
+-              /* No need to checksum. */
+-              break;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+-
+       return 1;
+ }
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index dbd4155bb0752..40d30649b3048 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -26,7 +26,7 @@
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff)
++             struct ip_vs_iphdr *iph)
+ {
+       struct udphdr _udph, *uh;
+-      uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
++      uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+-      if (uh->check != 0) {
+-              switch (skb->ip_summed) {
+-              case CHECKSUM_NONE:
+-                      skb->csum = skb_checksum(skb, udphoff,
+-                                               skb->len - udphoff, 0);
+-                      fallthrough;
+-              case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-                      if (af == AF_INET6) {
+-                              if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                                  &ipv6_hdr(skb)->daddr,
+-                                                  skb->len - udphoff,
+-                                                  IPPROTO_UDP,
+-                                                  skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      } else
+-#endif
+-                              if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                                    ip_hdr(skb)->daddr,
+-                                                    skb->len - udphoff,
+-                                                    ip_hdr(skb)->protocol,
+-                                                    skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      break;
+-              default:
+-                      /* No need to checksum. */
+-                      break;
+-              }
++      if (!uh->check)
++              return 1;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+       return 1;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..3f0dcd5
--- /dev/null
@@ -0,0 +1,64 @@
+From 6bf2c6df208b9fccf96544bf7136f7e45429e817 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index e105349794f23..b9ca9dc9b0c3f 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..9f35308
--- /dev/null
@@ -0,0 +1,63 @@
+From 5937f29f40bd1e2f1f15fe88ef645138aadc9dc5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index b9ca9dc9b0c3f..fd95a0eb7a466 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
new file mode 100644 (file)
index 0000000..d9faf82
--- /dev/null
@@ -0,0 +1,50 @@
+From a47fb4926d7f4ff507423b99d8754adb3b635dac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:04:19 +0900
+Subject: ksmbd: fix use-after-free in __close_file_table_ids()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
+
+A ksmbd_file can remain alive after logical close while another session
+holds a temporary reference obtained through ksmbd_lookup_fd_inode().
+ksmbd_close_fd() currently marks the file closed and drops the idr-owned
+reference, but leaves the pointer published in the closing session's idr
+until the final reference is dropped.
+
+If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final()
+supplies the foreign session's file table to __ksmbd_close_fd(). The object
+is then freed without being removed from its owner's idr, and the owner
+session later dereferences the stale pointer during file-table teardown.
+
+Remove the volatile id from the owner's idr while ksmbd_close_fd() still
+holds that table's lock, and clear volatile_id before dropping
+the idr-owned reference. A later foreign final put then only performs
+physical destruction and cannot remove the object from the wrong table.
+
+Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp")
+Reported-by: Yunseong Kim <yunseong.kim@est.tech>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index e67b055b8eb3b..5b3a55bd700dc 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -409,6 +409,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      idr_remove(ft->idr, id);
++                      fp->volatile_id = KSMBD_NO_FID;
+                       closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+-- 
+2.53.0
+
diff --git a/queue-6.1/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.1/ksmbd-return-success-for-deferred-final-close.patch
new file mode 100644 (file)
index 0000000..9cd62c1
--- /dev/null
@@ -0,0 +1,64 @@
+From fc95f0677f0a5750e3392bbd78e0bc9295f60e8e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jun 2026 19:41:08 +0900
+Subject: ksmbd: return success for deferred final close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ]
+
+ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
+reference. If another in-flight request still holds a reference, the final
+close is deferred until that request drops its reference.
+
+The function currently returns -EINVAL in that deferred-final-close case
+because fp is cleared when the reference count does not reach zero.  That
+turns a valid close into STATUS_FILE_CLOSED.
+
+smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
+closes the same directory handle before receiving the find response.
+The query holds a reference while it builds the response, so close must
+mark the handle closed and return success even though final teardown is
+delayed. Track whether the handle was successfully transitioned to
+FP_CLOSED and return success when only the final close is deferred.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 4911d1e325cd2..e67b055b8eb3b 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -394,6 +394,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+ {
+       struct ksmbd_file       *fp;
+       struct ksmbd_file_table *ft;
++      bool closed = false;
+       if (!has_file_id(id))
+               return 0;
+@@ -408,6 +409,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+               }
+@@ -415,7 +417,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+       write_unlock(&ft->lock);
+       if (!fp)
+-              return -EINVAL;
++              return closed ? 0 : -EINVAL;
+       __put_fd_final(work, fp);
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..5980f59
--- /dev/null
@@ -0,0 +1,40 @@
+From 075661608f82c79d4a9c49cf43d861e6f14e06ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index fd2de35ffb3cf..5fd22bb4f5b60 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
new file mode 100644 (file)
index 0000000..ab4372f
--- /dev/null
@@ -0,0 +1,78 @@
+From 2b3de6306e9168671a469977eb0c68eac43422bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 07:29:01 +0000
+Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
+
+When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry
+under memory pressure or tree size caps, redirect handlers previously fell
+back to sending un-rate-limited ICMP/NDISC Redirect messages.
+
+In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL.
+In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into
+inet_peer_xrlim_allow(), which returned true when peer == NULL.
+
+Because ICMP/NDISC Redirects are not part of the default global rate limit
+mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates
+an un-rate-limited ICMP packet storm.
+
+Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and
+ndisc_send_redirect() when peer is NULL.
+
+Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c      | 2 --
+ net/ipv6/ip6_output.c | 2 +-
+ net/ipv6/ndisc.c      | 2 ++
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 60516c6ae62e0..f9d2d4268c616 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -902,8 +902,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
+       peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
+       if (!peer) {
+               rcu_read_unlock();
+-              icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
+-                        rt_nexthop(rt, ip_hdr(skb)->daddr));
+               return;
+       }
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index ad821d362656f..93e55ab7e0f6d 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -623,7 +623,7 @@ int ip6_forward(struct sk_buff *skb)
+               /* Limit redirects both by destination (here)
+                  and by source (inside ndisc_send_redirect)
+                */
+-              if (inet_peer_xrlim_allow(peer, 1*HZ))
++              if (peer && inet_peer_xrlim_allow(peer, 1*HZ))
+                       ndisc_send_redirect(skb, target);
+               rcu_read_unlock();
+       } else {
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index a53d2a6a99f85..585a9135cf267 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1727,6 +1727,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       rcu_read_lock();
+       peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
++      if (!peer)
++              goto release;
+       ret = inet_peer_xrlim_allow(peer, 1*HZ);
+       rcu_read_unlock();
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..0ceed40
--- /dev/null
@@ -0,0 +1,92 @@
+From 7fc42cd434f53cc61615a2148211f01557ad874f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 3069a7df25d3f..8109a049a74e4 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1483,8 +1483,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->using_mac_select_pcs = using_mac_select_pcs;
+@@ -1508,28 +1508,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->cur_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..c58b458
--- /dev/null
@@ -0,0 +1,48 @@
+From 07febae2ed86e0bea36138a05cd45ad88584f4e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 2801526f75962..342faeb6c8afa 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..96d6713
--- /dev/null
@@ -0,0 +1,50 @@
+From bb1a3b7c01a6cfcb026a7cad494248af422e9b13 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index f6f99712d562e..2801526f75962 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..2c7b7df
--- /dev/null
@@ -0,0 +1,96 @@
+From 071b0458e8569c694fa65a278994bd1983920b6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index dbc614dfe0d56..aafa0c04f917e 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 0af6ede4b92c1..6e8ad849d14c4 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1627,7 +1627,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index f7be30c69b5c8..a1c41defaf22d 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..94f2604
--- /dev/null
@@ -0,0 +1,69 @@
+From 16b83b2d8326665e16b9f81f03cd6f0564d7b99a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index ae3277424b839..2a58a81ed13cf 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -241,9 +241,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -252,15 +250,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..0e1f209
--- /dev/null
@@ -0,0 +1,83 @@
+From 8858755ef9de2bc34fb26103d888deee5a5cab04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 0859b8f767645..61813010cd319 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               vfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..961ec35
--- /dev/null
@@ -0,0 +1,42 @@
+From 63d9716c8eb67fc385f672b5042ecbc5e5086da7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index 49f21c7f5c1fd..187d0a71e64ad 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -842,8 +842,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch b/queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch
new file mode 100644 (file)
index 0000000..75bed41
--- /dev/null
@@ -0,0 +1,41 @@
+From 455c546f2c09cd36fc21118820c90b97ac315785 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Jul 2023 15:45:39 -0400
+Subject: phy: zynqmp: Allow variation in refclk rate
+
+From: Sean Anderson <sean.anderson@seco.com>
+
+[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ]
+
+Due to limited available frequency ratios, the reference clock rate may
+not be exactly the same as the required rate. Allow a small (100 ppm)
+deviation.
+
+Signed-off-by: Sean Anderson <sean.anderson@seco.com>
+Link: https://lore.kernel.org/r/20230711194542.898230-1-sean.anderson@seco.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index cc36fb7616ae4..00ceeca2a395f 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -958,7 +958,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+               rate = clk_get_rate(clk);
+               for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      if (rate == ssc_lookup[i].refclk_rate) {
++                      /* Allow an error of 100 ppm */
++                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+                               gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+                               break;
+                       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..631b285
--- /dev/null
@@ -0,0 +1,76 @@
+From 97b964f5294762ee02e4fbfa9242f3f52531be62 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 61dc831f778dd..517855fea8920 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..cb5b5a8
--- /dev/null
@@ -0,0 +1,56 @@
+From ac05064893ce2ff7729ba5aefd7b33257ddcb67a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 517855fea8920..f1c2006dda602 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1046,6 +1046,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1055,12 +1061,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
new file mode 100644 (file)
index 0000000..ec8444f
--- /dev/null
@@ -0,0 +1,172 @@
+From cfa67e135a4ee1eaf20861665de5b8f52d1b7969 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Apr 2025 08:35:47 +0200
+Subject: phy-zynqmp: Postpone getting clock rate until actually needed
+
+From: Mike Looijmans <mike.looijmans@topic.nl>
+
+[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
+
+At probe time the driver would display the following error and abort:
+  xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
+
+At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't
+initialized the clock yet, so clk_get_rate() likely returns 0 if the clock
+is programmable. So this driver only works if the clock is fixed.
+
+The PHY driver doesn't need to know the clock frequency at probe yet, so
+wait until the associated driver initializes the lane before requesting the
+clock rate setting.
+
+In addition to allowing the driver to be used with programmable clocks,
+this also reduces the driver's runtime memory footprint by removing an
+array of pointers from struct xpsgtr_phy.
+
+Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
+Acked-by: Michal Simek <michal.simek@amd.com>
+Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++----------------
+ 1 file changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 00ceeca2a395f..61dc831f778dd 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -228,7 +228,6 @@ struct xpsgtr_phy {
+  * @siou: siou base address
+  * @gtr_mutex: mutex for locking
+  * @phys: PHY lanes
+- * @refclk_sscs: spread spectrum settings for the reference clocks
+  * @clk: reference clocks
+  * @tx_term_fix: fix for GT issue
+  * @saved_icm_cfg0: stored value of ICM CFG0 register
+@@ -241,7 +240,6 @@ struct xpsgtr_dev {
+       void __iomem *siou;
+       struct mutex gtr_mutex; /* mutex for locking */
+       struct xpsgtr_phy phys[NUM_LANES];
+-      const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
+       struct clk *clk[NUM_LANES];
+       bool tx_term_fix;
+       unsigned int saved_icm_cfg0;
+@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
+       return ret;
+ }
++/* Get the spread spectrum (SSC) settings for the reference clock rate */
++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy)
++{
++      unsigned long rate;
++      struct clk *clk;
++      unsigned int i;
++
++      clk = gtr_phy->dev->clk[gtr_phy->refclk];
++      rate = clk_get_rate(clk);
++
++      for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
++              /* Allow an error of 100 ppm */
++              unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++              if (abs(rate - ssc_lookup[i].refclk_rate) < error)
++                      return &ssc_lookup[i];
++      }
++
++      dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n",
++              rate, gtr_phy->refclk);
++
++      return NULL;
++}
++
+ /* Configure PLL and spread-sprectrum clock. */
+-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+ {
+       const struct xpsgtr_ssc *ssc;
+       u32 step_size;
+-      ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk];
++      ssc = xpsgtr_find_sscs(gtr_phy);
++      if (!ssc)
++              return -EINVAL;
++
+       step_size = ssc->step_size;
+       xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
+@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+       xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB,
+                          STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) |
+                          FORCE_STEP_SIZE | FORCE_STEPS);
++
++      return 0;
+ }
+ /* Configure the lane protocol. */
+@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+        * Configure the PLL, the lane protocol, and perform protocol-specific
+        * initialization.
+        */
+-      xpsgtr_configure_pll(gtr_phy);
++      ret = xpsgtr_configure_pll(gtr_phy);
++      if (ret)
++              goto out;
++
+       xpsgtr_lane_set_protocol(gtr_phy);
+       switch (gtr_phy->protocol) {
+@@ -855,8 +885,7 @@ static struct phy *xpsgtr_xlate(struct device *dev,
+       }
+       refclk = args->args[3];
+-      if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) ||
+-          !gtr_dev->refclk_sscs[refclk]) {
++      if (refclk >= ARRAY_SIZE(gtr_dev->clk)) {
+               dev_err(dev, "Invalid reference clock number %u\n", refclk);
+               return ERR_PTR(-EINVAL);
+       }
+@@ -932,9 +961,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+ {
+       unsigned int refclk;
+-      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
+-              unsigned long rate;
+-              unsigned int i;
++      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) {
+               struct clk *clk;
+               char name[8];
+@@ -950,29 +977,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+                       continue;
+               gtr_dev->clk[refclk] = clk;
+-
+-              /*
+-               * Get the spread spectrum (SSC) settings for the reference
+-               * clock rate.
+-               */
+-              rate = clk_get_rate(clk);
+-
+-              for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      /* Allow an error of 100 ppm */
+-                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
+-
+-                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+-                              gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+-                              break;
+-                      }
+-              }
+-
+-              if (i == ARRAY_SIZE(ssc_lookup)) {
+-                      dev_err(gtr_dev->dev,
+-                              "Invalid rate %lu for reference clock %u\n",
+-                              rate, refclk);
+-                      return -EINVAL;
+-              }
+       }
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
new file mode 100644 (file)
index 0000000..c57fa94
--- /dev/null
@@ -0,0 +1,58 @@
+From 697fe758fd37a93960926189a9002e32ae6401db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:28:44 -0500
+Subject: pinctrl-amd: Don't clear S4 wake bits at probe
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
+
+commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+introduced a regression where Wake-on-LAN no longer works after suspend
+or shutdown on some AMD platforms.
+
+Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI
+PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake
+sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME
+does not use GPIO IRQ infrastructure and relies on firmware configuration.
+
+The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake
+bits on probe again") was to clear spurious wake bits left by firmware
+to prevent unwanted wakeups. However, S4 wake bits are used for
+hardware-level wake sources like WoL that bypass the kernel's IRQ wake
+API.
+
+Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
+- Firmware-configured S4 wake sources (WoL) continue working
+- Kernel maintains control of S3/S0i3 wake policy via set_wake()
+- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
+  amd: Take suspend type into consideration which pins are non-wake")
+
+The trade-off is that firmware-programmed spurious S4 wake bits remain
+set, but this is less problematic than breaking WoL.
+
+Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/pinctrl-amd.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index 2b6d996e393e0..51506492302ef 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
+       u32 pin_reg, mask;
+       int i;
+-      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
+-              BIT(WAKE_CNTRL_OFF_S4);
++      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
+       for (i = 0; i < desc->npins; i++) {
+               int pin = desc->pins[i].number;
+-- 
+2.53.0
+
diff --git a/queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
new file mode 100644 (file)
index 0000000..11f5747
--- /dev/null
@@ -0,0 +1,58 @@
+From c4342884dff8c3f1ebd7a909c1d35c00381b5631 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 15:08:05 +0200
+Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
+
+From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+
+[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
+
+Pins 143 and 151 were not included in the PDC wakeup map. They are
+normally used for PCIe2A and PCIe3a PERST# respectively, so they're
+unlikely to be excercised in practice, but still add them for the sake
+of completeness.
+
+Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver")
+Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+index e96c00686a25b..aeeb40f4aa3da 100644
+--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+@@ -1892,16 +1892,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = {
+       { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 },
+       { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 },
+       { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 },
+-      { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 },
+-      { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 },
+-      { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 },
+-      { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 },
+-      { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 },
+-      { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 },
+-      { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 },
+-      { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 },
+-      { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 },
+-      { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 },
++      { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 },
++      { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 },
++      { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 },
++      { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 },
++      { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 },
++      { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 },
++      { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 },
++      { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 },
++      { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 },
++      { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 },
++      { 225, 196 }, { 227, 197 },
+ };
+ static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
+-- 
+2.53.0
+
diff --git a/queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..6049ce2
--- /dev/null
@@ -0,0 +1,38 @@
+From 9c70098a953b5f3355d66bfe0f37b71e089a86c7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..bf7978e
--- /dev/null
@@ -0,0 +1,38 @@
+From 630f398a572bcdab90d5a72f43c5a9634443880d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..fdfbe7d
--- /dev/null
@@ -0,0 +1,38 @@
+From d7ea3104628ad040818c8948b094f3a2298e5c4d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..f9aca20
--- /dev/null
@@ -0,0 +1,169 @@
+From 44cb067d2d174573cdccfbcfe91c7b94df589dc3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index 4bc950d366073..8148c16c851ea 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -108,7 +108,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -1063,21 +1063,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1114,6 +1099,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1122,9 +1109,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2665,9 +2660,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2697,17 +2696,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..b3e72ac
--- /dev/null
@@ -0,0 +1,118 @@
+From f22be27c65882d29ef66aee7e05e7a02acf49b46 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ce5be43c5fbac..1061bcf7d1315 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index 5289afbb61aa7..e50e01abb0799 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 4444fd82b66df..e717c42b39353 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..b2bba26
--- /dev/null
@@ -0,0 +1,85 @@
+From 575e6e28e23407fd2594a0fc1d6de609e883b534 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index e717c42b39353..323fa5ed5c3ea 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..ca2b8a0
--- /dev/null
@@ -0,0 +1,56 @@
+From 81fdfcf12a08498a0fceb27524a7e6ba6fb5dc6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index ee4e3feedd10b..b858efe3972e9 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..c102f0a
--- /dev/null
@@ -0,0 +1,71 @@
+From a12ae65e91e581e7b929964d30be51e5fcefc12c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index c182aa83f2c93..4d23205129432 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch b/queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch
new file mode 100644 (file)
index 0000000..eae1693
--- /dev/null
@@ -0,0 +1,83 @@
+From 1421c0c238aedf8aecd62359ab5279674e8884db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Mar 2023 19:09:30 +0800
+Subject: scsi: libsas: Abort all in-flight requests when device is gone
+
+From: Jason Yan <yanaijie@huawei.com>
+
+[ Upstream commit 0e4b1791d9b192ac263a03707d876132eb0f8dab ]
+
+When a disk is removed with in-flight I/O, the application needs to wait
+for 30 seconds (depending on the timeout configuration) to hear back from
+the kernel. Xingui tried to fix this issue by aborting the ATA link for
+SATA devices[1], however this approach left the SAS devices unresolved.
+
+Try to fix this issue by aborting all in-flight requests when the device is
+gone. This is implemented by iterating over the tagset.
+
+[1] https://lore.kernel.org/lkml/234e04db-7539-07e4-a6b8-c6b05f78193d@opensource.wdc.com/T/
+
+Cc: Xingui Yang <yangxingui@huawei.com>
+Cc: John Garry <john.g.garry@oracle.com>
+Cc: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Cc: Hannes Reinecke <hare@suse.com>
+Signed-off-by: Jason Yan <yanaijie@huawei.com>
+Link: https://lore.kernel.org/r/20230330110930.175539-1-yanaijie@huawei.com
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libsas/sas_discover.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
+index d5bc1314c3415..49dea3c597778 100644
+--- a/drivers/scsi/libsas/sas_discover.c
++++ b/drivers/scsi/libsas/sas_discover.c
+@@ -360,6 +360,33 @@ static void sas_destruct_ports(struct asd_sas_port *port)
+       }
+ }
++static bool sas_abort_cmd(struct request *req, void *data)
++{
++      struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
++      struct domain_device *dev = data;
++
++      if (dev == cmd_to_domain_dev(cmd))
++              blk_abort_request(req);
++      return true;
++}
++
++static void sas_abort_device_scsi_cmds(struct domain_device *dev)
++{
++      struct sas_ha_struct *sas_ha = dev->port->ha;
++      struct Scsi_Host *shost = sas_ha->core.shost;
++
++      if (dev_is_expander(dev->dev_type))
++              return;
++
++      /*
++       * For removed device with active IOs, the user space applications have
++       * to spend very long time waiting for the timeout. This is not
++       * necessary because a removed device will not return the IOs.
++       * Abort the inflight IOs here so that EH can be quickly kicked in.
++       */
++      blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev);
++}
++
+ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
+ {
+       if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
+@@ -372,6 +399,8 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
+       }
+       if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
++              if (test_bit(SAS_DEV_GONE, &dev->state))
++                      sas_abort_device_scsi_cmds(dev);
+               sas_rphy_unlink(dev->rphy);
+               list_move_tail(&dev->disco_list_node, &port->destroy_list);
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-libsas-delete-struct-scsi_core.patch b/queue-6.1/scsi-libsas-delete-struct-scsi_core.patch
new file mode 100644 (file)
index 0000000..b4f1851
--- /dev/null
@@ -0,0 +1,563 @@
+From cb30320aa74525f6642c05eddfbf1726b32d6fa7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 15 Aug 2023 11:51:50 +0000
+Subject: scsi: libsas: Delete struct scsi_core
+
+From: John Garry <john.g.garry@oracle.com>
+
+[ Upstream commit 1136a0225d0582c4464fa37e3a91ed4b19b8745e ]
+
+Since commit 79855d178557 ("libsas: remove task_collector mode"), struct
+scsi_core only contains a reference to the shost. struct scsi_core is only
+used in sas_ha_struct.core, so delete scsi_core and replace with a
+reference to the shost there.
+
+Signed-off-by: John Garry <john.g.garry@oracle.com>
+Link: https://lore.kernel.org/r/20230815115156.343535-5-john.g.garry@oracle.com
+Reviewed-by: Jason Yan <yanaijie@huawei.com>
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/aic94xx/aic94xx_hwi.c     |  2 +-
+ drivers/scsi/aic94xx/aic94xx_init.c    |  6 +++---
+ drivers/scsi/hisi_sas/hisi_sas_main.c  |  6 +++---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c |  6 +++---
+ drivers/scsi/isci/host.h               |  2 +-
+ drivers/scsi/isci/init.c               |  4 ++--
+ drivers/scsi/libsas/sas_ata.c          |  8 ++++----
+ drivers/scsi/libsas/sas_discover.c     |  8 ++++----
+ drivers/scsi/libsas/sas_expander.c     |  2 +-
+ drivers/scsi/libsas/sas_host_smp.c     |  4 ++--
+ drivers/scsi/libsas/sas_init.c         | 16 ++++++++--------
+ drivers/scsi/libsas/sas_phy.c          |  8 ++++----
+ drivers/scsi/libsas/sas_port.c         |  6 +++---
+ drivers/scsi/libsas/sas_scsi_host.c    | 14 +++++++-------
+ drivers/scsi/mvsas/mv_init.c           |  4 ++--
+ drivers/scsi/pm8001/pm8001_init.c      |  2 +-
+ include/scsi/libsas.h                  |  7 +------
+ 17 files changed, 50 insertions(+), 55 deletions(-)
+
+diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
+index 3dd1101434715..8f515aae0a8dd 100644
+--- a/drivers/scsi/aic94xx/aic94xx_hwi.c
++++ b/drivers/scsi/aic94xx/aic94xx_hwi.c
+@@ -28,7 +28,7 @@ static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
+       if (asd_ha->hw_prof.sas_addr[0])
+               return 0;
+-      return sas_request_addr(asd_ha->sas_ha.core.shost,
++      return sas_request_addr(asd_ha->sas_ha.shost,
+                               asd_ha->hw_prof.sas_addr);
+ }
+diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
+index 1766302053da6..f204714ad7536 100644
+--- a/drivers/scsi/aic94xx/aic94xx_init.c
++++ b/drivers/scsi/aic94xx/aic94xx_init.c
+@@ -688,8 +688,8 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
+       err = sas_unregister_ha(&asd_ha->sas_ha);
+-      sas_remove_host(asd_ha->sas_ha.core.shost);
+-      scsi_host_put(asd_ha->sas_ha.core.shost);
++      sas_remove_host(asd_ha->sas_ha.shost);
++      scsi_host_put(asd_ha->sas_ha.shost);
+       kfree(asd_ha->sas_ha.sas_phy);
+       kfree(asd_ha->sas_ha.sas_port);
+@@ -739,7 +739,7 @@ static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+       asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
+       SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha;
+-      asd_ha->sas_ha.core.shost = shost;
++      asd_ha->sas_ha.shost = shost;
+       shost->transportt = aic94xx_transport_template;
+       shost->max_id = ~0;
+       shost->max_lun = ~0;
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
+index 360f2799f2a13..10ea1d434c48d 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
+@@ -2458,7 +2458,7 @@ int hisi_sas_probe(struct platform_device *pdev,
+       sha->lldd_module = THIS_MODULE;
+       sha->sas_addr = &hisi_hba->sas_addr[0];
+       sha->num_phys = hisi_hba->n_phy;
+-      sha->core.shost = hisi_hba->shost;
++      sha->shost = hisi_hba->shost;
+       for (i = 0; i < hisi_hba->n_phy; i++) {
+               sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy;
+@@ -2500,12 +2500,12 @@ int hisi_sas_remove(struct platform_device *pdev)
+ {
+       struct sas_ha_struct *sha = platform_get_drvdata(pdev);
+       struct hisi_hba *hisi_hba = sha->lldd_ha;
+-      struct Scsi_Host *shost = sha->core.shost;
++      struct Scsi_Host *shost = sha->shost;
+       del_timer_sync(&hisi_hba->timer);
+       sas_unregister_ha(sha);
+-      sas_remove_host(sha->core.shost);
++      sas_remove_host(shost);
+       hisi_sas_free(hisi_hba);
+       scsi_host_put(shost);
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index 20b4d76e07149..a3b408962a861 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -4820,7 +4820,7 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+       sha->sas_phy = arr_phy;
+       sha->sas_port = arr_port;
+-      sha->core.shost = shost;
++      sha->shost = shost;
+       sha->lldd_ha = hisi_hba;
+       shost->transportt = hisi_sas_stt;
+@@ -4921,14 +4921,14 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
+       struct device *dev = &pdev->dev;
+       struct sas_ha_struct *sha = dev_get_drvdata(dev);
+       struct hisi_hba *hisi_hba = sha->lldd_ha;
+-      struct Scsi_Host *shost = sha->core.shost;
++      struct Scsi_Host *shost = sha->shost;
+       pm_runtime_get_noresume(dev);
+       del_timer_sync(&hisi_hba->timer);
+       sas_unregister_ha(sha);
+       flush_workqueue(hisi_hba->wq);
+-      sas_remove_host(sha->core.shost);
++      sas_remove_host(shost);
+       hisi_sas_v3_destroy_irqs(pdev, hisi_hba);
+       hisi_sas_free(hisi_hba);
+diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
+index 6bc3f022630a2..52388374cf315 100644
+--- a/drivers/scsi/isci/host.h
++++ b/drivers/scsi/isci/host.h
+@@ -306,7 +306,7 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
+ static inline struct Scsi_Host *to_shost(struct isci_host *ihost)
+ {
+-      return ihost->sas_ha.core.shost;
++      return ihost->sas_ha.shost;
+ }
+ #define for_each_isci_host(id, ihost, pdev) \
+diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
+index 012cd2dade862..ee8c87dbe5ef3 100644
+--- a/drivers/scsi/isci/init.c
++++ b/drivers/scsi/isci/init.c
+@@ -571,7 +571,7 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
+               goto err_shost;
+       SHOST_TO_SAS_HA(shost) = &ihost->sas_ha;
+-      ihost->sas_ha.core.shost = shost;
++      ihost->sas_ha.shost = shost;
+       shost->transportt = isci_transport_template;
+       shost->max_id = ~0;
+@@ -726,7 +726,7 @@ static int isci_resume(struct device *dev)
+               sas_prep_resume_ha(&ihost->sas_ha);
+               isci_host_init(ihost);
+-              isci_host_start(ihost->sas_ha.core.shost);
++              isci_host_start(ihost->sas_ha.shost);
+               wait_for_start(ihost);
+               sas_resume_ha(&ihost->sas_ha);
+diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
+index 6b045be947b14..1d06daac1d927 100644
+--- a/drivers/scsi/libsas/sas_ata.c
++++ b/drivers/scsi/libsas/sas_ata.c
+@@ -162,7 +162,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
+       struct ata_port *ap = qc->ap;
+       struct domain_device *dev = ap->private_data;
+       struct sas_ha_struct *sas_ha = dev->port->ha;
+-      struct Scsi_Host *host = sas_ha->core.shost;
++      struct Scsi_Host *host = sas_ha->shost;
+       struct sas_internal *i = to_sas_internal(host->transportt);
+       /* TODO: we should try to remove that unlock */
+@@ -236,7 +236,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
+ static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
+ {
+-      return to_sas_internal(dev->port->ha->core.shost->transportt);
++      return to_sas_internal(dev->port->ha->shost->transportt);
+ }
+ static int sas_get_ata_command_set(struct domain_device *dev);
+@@ -576,7 +576,7 @@ static struct ata_port_info sata_port_info = {
+ int sas_ata_init(struct domain_device *found_dev)
+ {
+       struct sas_ha_struct *ha = found_dev->port->ha;
+-      struct Scsi_Host *shost = ha->core.shost;
++      struct Scsi_Host *shost = ha->shost;
+       struct ata_host *ata_host;
+       struct ata_port *ap;
+       int rc;
+@@ -766,7 +766,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
+       struct sas_ha_struct *ha = dev->port->ha;
+       sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
+-      ata_scsi_port_error_handler(ha->core.shost, ap);
++      ata_scsi_port_error_handler(ha->shost, ap);
+       sas_put_device(dev);
+ }
+diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
+index 49dea3c597778..4e8cdd3ae5ab9 100644
+--- a/drivers/scsi/libsas/sas_discover.c
++++ b/drivers/scsi/libsas/sas_discover.c
+@@ -170,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)
+ {
+       int res = 0;
+       struct sas_ha_struct *sas_ha = dev->port->ha;
+-      struct Scsi_Host *shost = sas_ha->core.shost;
++      struct Scsi_Host *shost = sas_ha->shost;
+       struct sas_internal *i = to_sas_internal(shost->transportt);
+       if (!i->dft->lldd_dev_found)
+@@ -192,7 +192,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)
+ void sas_notify_lldd_dev_gone(struct domain_device *dev)
+ {
+       struct sas_ha_struct *sas_ha = dev->port->ha;
+-      struct Scsi_Host *shost = sas_ha->core.shost;
++      struct Scsi_Host *shost = sas_ha->shost;
+       struct sas_internal *i = to_sas_internal(shost->transportt);
+       if (!i->dft->lldd_dev_gone)
+@@ -234,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work)
+       struct domain_device *dev;
+       struct sas_discovery_event *ev = to_sas_discovery_event(work);
+       struct asd_sas_port *port = ev->port;
+-      struct Scsi_Host *shost = port->ha->core.shost;
++      struct Scsi_Host *shost = port->ha->shost;
+       struct sas_internal *si = to_sas_internal(shost->transportt);
+       clear_bit(DISCE_SUSPEND, &port->disc.pending);
+@@ -373,7 +373,7 @@ static bool sas_abort_cmd(struct request *req, void *data)
+ static void sas_abort_device_scsi_cmds(struct domain_device *dev)
+ {
+       struct sas_ha_struct *sas_ha = dev->port->ha;
+-      struct Scsi_Host *shost = sas_ha->core.shost;
++      struct Scsi_Host *shost = sas_ha->shost;
+       if (dev_is_expander(dev->dev_type))
+               return;
+diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
+index ffec7f0e51fcd..03d367c2f0a7a 100644
+--- a/drivers/scsi/libsas/sas_expander.c
++++ b/drivers/scsi/libsas/sas_expander.c
+@@ -37,7 +37,7 @@ static int smp_execute_task_sg(struct domain_device *dev,
+       int res, retry;
+       struct sas_task *task = NULL;
+       struct sas_internal *i =
+-              to_sas_internal(dev->port->ha->core.shost->transportt);
++              to_sas_internal(dev->port->ha->shost->transportt);
+       struct sas_ha_struct *ha = dev->port->ha;
+       pm_runtime_get_sync(ha->dev);
+diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c
+index 32cdc969b736a..2ecb8535634c1 100644
+--- a/drivers/scsi/libsas/sas_host_smp.c
++++ b/drivers/scsi/libsas/sas_host_smp.c
+@@ -114,7 +114,7 @@ static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data,
+                                  u8 reg_type, u8 reg_index, u8 reg_count,
+                                  u8 *req_data)
+ {
+-      struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt);
++      struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt);
+       int written;
+       if (i->dft->lldd_write_gpio == NULL) {
+@@ -182,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
+                           enum sas_linkrate max, u8 *resp_data)
+ {
+       struct sas_internal *i =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       struct sas_phy_linkrates rates;
+       struct asd_sas_phy *asd_phy;
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index e4f77072a58d2..d514cf3f85ed9 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -186,7 +186,7 @@ static int sas_get_linkerrors(struct sas_phy *phy)
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+-                      to_sas_internal(sas_ha->core.shost->transportt);
++                      to_sas_internal(sas_ha->shost->transportt);
+               return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL);
+       }
+@@ -235,7 +235,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+-                      to_sas_internal(sas_ha->core.shost->transportt);
++                      to_sas_internal(sas_ha->shost->transportt);
+               if (!hard_reset && sas_try_ata_reset(asd_phy) == 0)
+                       return 0;
+@@ -269,7 +269,7 @@ int sas_phy_enable(struct sas_phy *phy, int enable)
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+-                      to_sas_internal(sas_ha->core.shost->transportt);
++                      to_sas_internal(sas_ha->shost->transportt);
+               if (enable)
+                       ret = transport_sas_phy_reset(phy, 0);
+@@ -306,7 +306,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+-                      to_sas_internal(sas_ha->core.shost->transportt);
++                      to_sas_internal(sas_ha->shost->transportt);
+               ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
+       } else {
+@@ -342,7 +342,7 @@ int sas_set_phy_speed(struct sas_phy *phy,
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+-                      to_sas_internal(sas_ha->core.shost->transportt);
++                      to_sas_internal(sas_ha->shost->transportt);
+               ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
+                                              rates);
+@@ -441,7 +441,7 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+       /* all phys are back up or timed out, turn on i/o so we can
+        * flush out disks that did not return
+        */
+-      scsi_unblock_requests(ha->core.shost);
++      scsi_unblock_requests(ha->shost);
+       if (drain)
+               sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+@@ -471,7 +471,7 @@ void sas_suspend_ha(struct sas_ha_struct *ha)
+       int i;
+       sas_disable_events(ha);
+-      scsi_block_requests(ha->core.shost);
++      scsi_block_requests(ha->shost);
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_port *port = ha->sas_port[i];
+@@ -644,7 +644,7 @@ struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,
+       struct asd_sas_event *event;
+       struct sas_ha_struct *sas_ha = phy->ha;
+       struct sas_internal *i =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       event = kmem_cache_zalloc(sas_event_cache, gfp_flags);
+       if (!event)
+diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c
+index a0d592d11dfb1..57494ac97076d 100644
+--- a/drivers/scsi/libsas/sas_phy.c
++++ b/drivers/scsi/libsas/sas_phy.c
+@@ -38,7 +38,7 @@ static void sas_phye_oob_error(struct work_struct *work)
+       struct sas_ha_struct *sas_ha = phy->ha;
+       struct asd_sas_port *port = phy->port;
+       struct sas_internal *i =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       sas_deform_port(phy, 1);
+@@ -66,7 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work)
+       struct asd_sas_phy *phy = ev->phy;
+       struct sas_ha_struct *sas_ha = phy->ha;
+       struct sas_internal *i =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       phy->error = 0;
+       i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
+@@ -95,7 +95,7 @@ static void sas_phye_shutdown(struct work_struct *work)
+       struct asd_sas_phy *phy = ev->phy;
+       struct sas_ha_struct *sas_ha = phy->ha;
+       struct sas_internal *i =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       if (phy->enabled) {
+               int ret;
+@@ -131,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
+               spin_lock_init(&phy->sas_prim_lock);
+               phy->frame_rcvd_size = 0;
+-              phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
++              phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i);
+               if (!phy->phy)
+                       return -ENOMEM;
+diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
+index 11599c0e3fc34..60ad1486d15c2 100644
+--- a/drivers/scsi/libsas/sas_port.c
++++ b/drivers/scsi/libsas/sas_port.c
+@@ -28,7 +28,7 @@ static void sas_resume_port(struct asd_sas_phy *phy)
+       struct domain_device *dev, *n;
+       struct asd_sas_port *port = phy->port;
+       struct sas_ha_struct *sas_ha = phy->ha;
+-      struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt);
++      struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt);
+       if (si->dft->lldd_port_formed)
+               si->dft->lldd_port_formed(phy);
+@@ -109,7 +109,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
+       struct asd_sas_port *port = phy->port;
+       struct domain_device *port_dev = NULL;
+       struct sas_internal *si =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       unsigned long flags;
+       if (port) {
+@@ -212,7 +212,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
+       struct sas_ha_struct *sas_ha = phy->ha;
+       struct asd_sas_port *port = phy->port;
+       struct sas_internal *si =
+-              to_sas_internal(sas_ha->core.shost->transportt);
++              to_sas_internal(sas_ha->shost->transportt);
+       struct domain_device *dev;
+       unsigned long flags;
+diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
+index a36fa1c128a84..83ea14ce2330a 100644
+--- a/drivers/scsi/libsas/sas_scsi_host.c
++++ b/drivers/scsi/libsas/sas_scsi_host.c
+@@ -279,7 +279,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
+       unsigned long flags;
+       int i, res;
+       struct sas_internal *si =
+-              to_sas_internal(task->dev->port->ha->core.shost->transportt);
++              to_sas_internal(task->dev->port->ha->shost->transportt);
+       for (i = 0; i < 5; i++) {
+               pr_notice("%s: aborting task 0x%p\n", __func__, task);
+@@ -327,7 +327,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)
+       int res = TMF_RESP_FUNC_FAILED;
+       struct scsi_lun lun;
+       struct sas_internal *i =
+-              to_sas_internal(dev->port->ha->core.shost->transportt);
++              to_sas_internal(dev->port->ha->shost->transportt);
+       int_to_scsilun(cmd->device->lun, &lun);
+@@ -355,7 +355,7 @@ static int sas_recover_I_T(struct domain_device *dev)
+ {
+       int res = TMF_RESP_FUNC_FAILED;
+       struct sas_internal *i =
+-              to_sas_internal(dev->port->ha->core.shost->transportt);
++              to_sas_internal(dev->port->ha->shost->transportt);
+       pr_notice("I_T nexus reset for dev %016llx\n",
+                 SAS_ADDR(dev->sas_addr));
+@@ -410,7 +410,7 @@ static void sas_wait_eh(struct domain_device *dev)
+       spin_unlock_irq(&ha->lock);
+       /* make sure SCSI EH is complete */
+-      if (scsi_host_in_recovery(ha->core.shost)) {
++      if (scsi_host_in_recovery(ha->shost)) {
+               msleep(10);
+               goto retry;
+       }
+@@ -440,7 +440,7 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type,
+                       set_bit(SAS_DEV_EH_PENDING, &dev->state);
+                       set_bit(reset_type, &dev->state);
+                       int_to_scsilun(lun, &dev->ssp_dev.reset_lun);
+-                      scsi_schedule_eh(ha->core.shost);
++                      scsi_schedule_eh(ha->shost);
+               }
+               spin_unlock_irq(&ha->lock);
+@@ -926,7 +926,7 @@ static int sas_execute_internal_abort(struct domain_device *device,
+                                     unsigned int qid, void *data)
+ {
+       struct sas_ha_struct *ha = device->port->ha;
+-      struct sas_internal *i = to_sas_internal(ha->core.shost->transportt);
++      struct sas_internal *i = to_sas_internal(ha->shost->transportt);
+       struct sas_task *task = NULL;
+       int res, retry;
+@@ -1016,7 +1016,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter,
+ {
+       struct sas_task *task;
+       struct sas_internal *i =
+-              to_sas_internal(device->port->ha->core.shost->transportt);
++              to_sas_internal(device->port->ha->shost->transportt);
+       int res, retry;
+       for (retry = 0; retry < TASK_RETRY; retry++) {
+diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
+index b500c343cad75..4182e004d775f 100644
+--- a/drivers/scsi/mvsas/mv_init.c
++++ b/drivers/scsi/mvsas/mv_init.c
+@@ -416,7 +416,7 @@ static int mvs_prep_sas_ha_init(struct Scsi_Host *shost,
+       sha->sas_phy = arr_phy;
+       sha->sas_port = arr_port;
+-      sha->core.shost = shost;
++      sha->shost = shost;
+       sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL);
+       if (!sha->lldd_ha)
+@@ -473,7 +473,7 @@ static void  mvs_post_sas_ha_init(struct Scsi_Host *shost,
+       shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
+       shost->can_queue = can_queue;
+       mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
+-      sha->core.shost = mvi->shost;
++      sha->shost = mvi->shost;
+ }
+ static void mvs_init_sas_add(struct mvs_info *mvi)
+diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
+index 60b477e65a66a..914a30b3dfc2e 100644
+--- a/drivers/scsi/pm8001/pm8001_init.c
++++ b/drivers/scsi/pm8001/pm8001_init.c
+@@ -653,7 +653,7 @@ static void  pm8001_post_sas_ha_init(struct Scsi_Host *shost,
+       sha->lldd_module = THIS_MODULE;
+       sha->sas_addr = &pm8001_ha->sas_addr[0];
+       sha->num_phys = chip_info->n_phy;
+-      sha->core.shost = shost;
++      sha->shost = shost;
+ }
+ /**
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index 9e9dff75a02bc..1be8aa6f53933 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -346,11 +346,6 @@ struct asd_sas_phy {
+       void *lldd_phy;           /* not touched by the sas_class_code */
+ };
+-struct scsi_core {
+-      struct Scsi_Host *shost;
+-
+-};
+-
+ enum sas_ha_state {
+       SAS_HA_REGISTERED,
+       SAS_HA_DRAINING,
+@@ -371,7 +366,7 @@ struct sas_ha_struct {
+       struct mutex disco_mutex;
+-      struct scsi_core core;
++      struct Scsi_Host *shost;
+ /* public: */
+       char *sas_ha_name;
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
new file mode 100644 (file)
index 0000000..dc39774
--- /dev/null
@@ -0,0 +1,165 @@
+From 5a161a846c1b83be31990833cdb08f15af1c94b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 16:11:45 +0800
+Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
+
+From: Xingui Yang <yangxingui@huawei.com>
+
+[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
+
+Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue
+for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
+the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue,
+calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI
+devices and waits for the host to become runtime-active. But the host
+cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the
+drain is blocked on that very handler.
+
+However skipping the drain reintroduces a race: hisi_sas returns from
+resume before all PHY UP work and libsas discovery work finish. The
+controller may then autosuspend while disks are still waking up. The
+disks issue IO to a suspended controller, the IO fails, and the disks
+get disabled.
+
+Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT
+notification to after sas_drain_work(). By then the host resume is about
+to complete, so device removal through device_link no longer blocks on
+the resume and the cycle is broken.
+
+With the deadlock gone, restore sas_resume_ha() (the draining variant)
+in hisi_sas and remove sas_resume_ha_no_sync().
+
+The reorder is safe for the other libsas consumers (isci, pm8001,
+aic94xx, mvsas). During suspend, sas_suspend_devices() calls
+sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to
+NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a
+timed-out phy's disk is immediately rejected by the LLDD before reaching
+hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET),
+and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both
+complete directly via scsi_done() without entering SCSI EH. This is
+identical in both the old and new ordering since lldd_dev_gone runs
+during suspend, before resume. The reorder only affects when the
+PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work()
+vs. asynchronous after resume returns), not whether I/O can reach the
+device. aic94xx and mvsas do not register any PM ops and never reach
+this code path.
+
+Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume")
+Signed-off-by: Xingui Yang <yangxingui@huawei.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------
+ drivers/scsi/libsas/sas_init.c         | 37 +++++++++++++-------------
+ include/scsi/libsas.h                  |  1 -
+ 3 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index a3b408962a861..ccd52fc7d34a2 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -5047,15 +5047,7 @@ static int _resume_v3_hw(struct device *device)
+               return rc;
+       }
+       phys_init_v3_hw(hisi_hba);
+-
+-      /*
+-       * If a directly-attached disk is removed during suspend, a deadlock
+-       * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+-       * hisi_hba->device to be active, which can only happen when resume
+-       * completes. So don't wait for the HA event workqueue to drain upon
+-       * resume.
+-       */
+-      sas_resume_ha_no_sync(sha);
++      sas_resume_ha(sha);
+       clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+       dev_warn(dev, "end of resuming controller\n");
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index d514cf3f85ed9..f1e24534d12fe 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -412,7 +412,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+       }
+ }
+-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
++void sas_resume_ha(struct sas_ha_struct *ha)
+ {
+       const unsigned long tmo = msecs_to_jiffies(25000);
+       int i;
+@@ -428,6 +428,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+                        i, i > 1 ? "s" : "");
+       wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
++
++      /*
++       * All phys are back up or timed out. Turn on I/O and drain
++       * pending work.
++       */
++      scsi_unblock_requests(ha->shost);
++      sas_drain_work(ha);
++
++      /*
++       * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
++       * calls sas_deform_port() -> sas_destruct_devices(), which removes
++       * SCSI devices and, for LLDDs using device_link() PM sync, waits
++       * for the host to be runtime-active. Sending it before the drain
++       * would deadlock: the drain waits for the handler, the handler
++       * waits for host resume, and host resume waits for the drain to
++       * finish.
++       */
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_phy *phy = ha->sas_phy[i];
+@@ -438,12 +455,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               }
+       }
+-      /* all phys are back up or timed out, turn on i/o so we can
+-       * flush out disks that did not return
+-       */
+-      scsi_unblock_requests(ha->shost);
+-      if (drain)
+-              sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+       sas_queue_deferred_work(ha);
+@@ -452,20 +463,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+        */
+       sas_resume_insert_broadcast_ha(ha);
+ }
+-
+-void sas_resume_ha(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, true);
+-}
+ EXPORT_SYMBOL(sas_resume_ha);
+-/* A no-sync variant, which does not call sas_drain_ha(). */
+-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, false);
+-}
+-EXPORT_SYMBOL(sas_resume_ha_no_sync);
+-
+ void sas_suspend_ha(struct sas_ha_struct *ha)
+ {
+       int i;
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index 1be8aa6f53933..7165d3aee1176 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -703,7 +703,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
+ extern int sas_unregister_ha(struct sas_ha_struct *);
+ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
+ extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
+-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
+ extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
+ int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates);
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
new file mode 100644 (file)
index 0000000..595a2ec
--- /dev/null
@@ -0,0 +1,80 @@
+From 5ba734f5ce2bf27a8d13716a478cadb7100a2ccd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:30:10 +0300
+Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
+
+When target_get_sess_cmd() fails during session shutdown because
+percpu_ref_tryget_live() returns false, the command keeps the
+se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier
+without owning a reference. Final release through
+target_release_cmd_kref() then issues an unmatched percpu_ref_put().
+
+Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during
+cmd setup") moved the cmd_cnt assignment ahead of the reference
+acquisition.  Clear se_cmd->cmd_cnt whenever the initial
+target_get_sess_cmd() fails in target_init_cmd() and
+target_submit_tmr(), so release performs exactly one matching put per
+acquired reference.
+
+Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup")
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_transport.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
+index b9a144a59dff3..f4e3ba173dbe3 100644
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -1670,6 +1670,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+                   u32 data_length, int task_attr, int data_dir, int flags)
+ {
+       struct se_portal_group *se_tpg;
++      int ret;
+       se_tpg = se_sess->se_tpg;
+       BUG_ON(!se_tpg);
+@@ -1699,7 +1700,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
+        * kref_put() to happen during fabric packet acknowledgement.
+        */
+-      return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      if (ret)
++              se_cmd->cmd_cnt = NULL;
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(target_init_cmd);
+@@ -1994,8 +1999,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * allocation failure.
+        */
+       ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+-      if (ret < 0)
++      if (ret < 0) {
++              se_cmd->cmd_cnt = NULL;
+               return -ENOMEM;
++      }
+       if (tm_type == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = tag;
+@@ -2003,6 +2010,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+       /* See target_submit_cmd for commentary */
+       ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+       if (ret) {
++              se_cmd->cmd_cnt = NULL;
+               core_tmr_release_req(se_cmd->se_tmr_req);
+               return ret;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..49a5c98
--- /dev/null
@@ -0,0 +1,66 @@
+From 47b376780daae6b805aaa2f7f66610f2fbf77b23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index ab2f35bc294da..d3cc884ccd599 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
index 8124209bec3ced01e759236785dea38771c79b54..f5e3f3595cf30f04e3d23a5e63478c855f316953 100644 (file)
@@ -306,3 +306,73 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
 hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 net-qrtr-ns-limit-the-maximum-server-registration-pe.patch
 net-qrtr-ns-raise-node-count-limit-to-512.patch
+pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
+ata-libahci_platform-support-non-consecutive-port-nu.patch
+ahci-introduce-ahci_ignore_port-helper.patch
+ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+phy-zynqmp-allow-variation-in-refclk-rate.patch
+phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+ipv6-introduce-dst_rt6_info-helper.patch
+ipvs-fix-the-checksum-validations.patch
+ipvs-fix-places-with-wrong-packet-offsets.patch
+ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+scsi-libsas-abort-all-in-flight-requests-when-device.patch
+scsi-libsas-delete-struct-scsi_core.patch
+scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+hwmon-nct6755-add-support-for-nct6799d.patch
+hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch
+hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch
+hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch
+hwmon-nct6775-additional-temp-registers-for-nct6799.patch
+hwmon-nct6775-fix-access-to-temperature-configuratio.patch
+hwmon-nct6775-core-fix-number-of-temperature-registe.patch
+hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
+hwmon-nzxt-smart2-dma-align-output-buffer.patch
+net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
+ksmbd-return-success-for-deferred-final-close.patch
+ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
diff --git a/queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..829eae8
--- /dev/null
@@ -0,0 +1,85 @@
+From 4d90846b856f769eb2adf0051871143d6d45d837 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 49d7726830044..95cf7e69a411d 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -1409,8 +1409,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1522,8 +1524,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1776,8 +1780,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..804e899
--- /dev/null
@@ -0,0 +1,43 @@
+From a3a309d3e4aaa622f57220b9243a2ddf0ed052e5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index 4d9e5c830dbe1..c523ce5aa4958 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..78f8616
--- /dev/null
@@ -0,0 +1,52 @@
+From cb17cce204cc5dff81bd572e26374c6cb2913cb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index c1f964e9991cd..9914390ff31ff 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
new file mode 100644 (file)
index 0000000..ca5e297
--- /dev/null
@@ -0,0 +1,49 @@
+From 10128405b927c7ca4c11361ec0c5e9e59dce26fc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 02:25:20 +0500
+Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ]
+
+In encode_message() the per-transaction lower-bound check compares
+trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer,
+instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr.
+
+Every other length check in this file (encode_message() at the loop
+guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so
+this is an inconsistency. On 64-bit builds the pointer and the struct
+are both 8 bytes, so the check is correct by coincidence and there is
+no behavioural change. On 32-bit builds the pointer is 4 bytes, which
+weakens the minimum-length check below the 8-byte header size.
+
+Use sizeof(*trans_hdr) so the check validates against the actual
+transaction header size on all builds.
+
+Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()")
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accel/qaic/qaic_control.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
+index 8eae30fe14f98..8759b53ba38eb 100644
+--- a/drivers/accel/qaic/qaic_control.c
++++ b/drivers/accel/qaic/qaic_control.c
+@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+                       break;
+               }
+               trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
+-              if (trans_hdr->len < sizeof(trans_hdr) ||
++              if (trans_hdr->len < sizeof(*trans_hdr) ||
+                   size_add(user_len, trans_hdr->len) > user_msg->len) {
+                       ret = -EINVAL;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch
new file mode 100644 (file)
index 0000000..6b90192
--- /dev/null
@@ -0,0 +1,135 @@
+From ef8858a7a9e525f32ce14f8dbe9016d3c8cc94c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jan 2025 14:14:47 +0900
+Subject: ahci: Introduce ahci_ignore_port() helper
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ]
+
+libahci and AHCI drivers may ignore some ports if the port is invalid
+(its ID does not correspond to a valid physical port) or if the user
+explicitly requested the port to be ignored with the mask_port_map
+ahci module parameter. Such port that shall be ignored can be identified
+by checking that the bit corresponding to the port ID is not set in the
+mask_port_map field of struct ahci_host_priv. E.g. code such as:
+"if (!(hpriv->mask_port_map & (1 << portid)))".
+
+Replace all direct use of the mask_port_map field to detect such port
+with the new helper inline function ahci_ignore_port() to make the code
+more readable/easier to understand.
+
+The comment describing the mask_port_map field of struct ahci_host_priv
+is also updated to be more accurate.
+
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Niklas Cassel <cassel@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci.h             | 13 ++++++++++++-
+ drivers/ata/ahci_brcm.c        |  2 +-
+ drivers/ata/ahci_ceva.c        |  4 ++--
+ drivers/ata/libahci_platform.c |  6 +++---
+ 4 files changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index 10a5fe02f0a45..5814245e43ceb 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -329,7 +329,7 @@ struct ahci_port_priv {
+ struct ahci_host_priv {
+       /* Input fields */
+       unsigned int            flags;          /* AHCI_HFLAG_* */
+-      u32                     mask_port_map;  /* mask out particular bits */
++      u32                     mask_port_map;  /* Mask of valid ports */
+       void __iomem *          mmio;           /* bus-independent mem map */
+       u32                     cap;            /* cap to use */
+@@ -380,6 +380,17 @@ struct ahci_host_priv {
+                                                 int port);
+ };
++/*
++ * Return true if a port should be ignored because it is excluded from
++ * the host port map.
++ */
++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv,
++                                  unsigned int portid)
++{
++      return portid >= hpriv->nports ||
++              !(hpriv->mask_port_map & (1 << portid));
++}
++
+ extern int ahci_ignore_sss;
+ extern const struct attribute_group *ahci_shost_groups[];
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index 60505e1e247ab..12bb0c2f914e7 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 6e115da23d3f0..93275b1b48898 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_power_on(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index b68777841f7a5..53b2c7719dc51 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -49,7 +49,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -73,7 +73,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+@@ -94,7 +94,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+-- 
+2.53.0
+
diff --git a/queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..e3d0d64
--- /dev/null
@@ -0,0 +1,61 @@
+From 4acbf5f317d386e793f5d49b47188f1f2567e246 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index 4023b88e7bc13..9253707bccde9 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2388,8 +2388,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..813167a
--- /dev/null
@@ -0,0 +1,61 @@
+From 5a06924b764743850c3fa90b5262eeabd4e030ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index 7e525d49328d2..3b37f4c4235e6 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..ac3503c
--- /dev/null
@@ -0,0 +1,66 @@
+From 854c51871ececb198789159d2f1fda17ed7d0378 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index 388e656ac9743..01619e88a52c9 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
new file mode 100644 (file)
index 0000000..3ab5e06
--- /dev/null
@@ -0,0 +1,76 @@
+From 262563e6066841b74104405bdadc288f265b5bf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 23:55:26 +0530
+Subject: ata: ahci_ceva: fix error paths in
+ ceva_ahci_platform_enable_resources()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
+
+On phy_init() failure the error path fallsthrough to disable_rsts, which
+deasserts the controller reset and then enters disable_phys calling
+phy_power_off() on PHYs that were never powered on. That corrupts the PHY
+power_count and triggers an extra runtime PM put.
+
+Use a separate exit_phys path that unwinds with phy_exit() only and falls
+through to disable_clks while the controller remains in reset.  Reserve
+phy_power_off() for the phy_power_on() failure path only, and skip
+masked-out ports in both unwind loops.
+
+On phy_power_on() failure re-assert the controller reset before disabling
+clocks and regulators, matching the teardown order used by
+ahci_platform_enable_resources() and ahci_platform_disable_resources().
+
+Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_ceva.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 93275b1b48898..b2918ad8e7e05 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+-                      goto disable_rsts;
++                      goto exit_phys;
+       }
+       /* De-assert the controller reset */
+@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       return 0;
+-disable_rsts:
+-      ahci_platform_deassert_rsts(hpriv);
+-
+ disable_phys:
+       while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
++      ahci_platform_assert_rsts(hpriv);
++      goto disable_clks;
++
++exit_phys:
++      while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
++              phy_exit(hpriv->phys[i]);
++      }
+ disable_clks:
+       ahci_platform_disable_clks(hpriv);
+-- 
+2.53.0
+
diff --git a/queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch
new file mode 100644 (file)
index 0000000..ff1cde4
--- /dev/null
@@ -0,0 +1,178 @@
+From c48c3345edb3c1140ed0a9e764f5412674e5ac86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jan 2025 13:13:33 +0100
+Subject: ata: libahci_platform: support non-consecutive port numbers
+
+From: Josua Mayer <josua@solid-run.com>
+
+[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ]
+
+So far ahci_platform relied on number of child nodes in firmware to
+allocate arrays and expected port numbers to start from 0 without holes.
+This number of ports is then set in private structure for use when
+configuring phys and regulators.
+
+Some platforms may not use every port of an ahci controller.
+E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading
+to the following errors during boot:
+[    1.719476] ahci f2540000.sata: invalid port number 1
+[    1.724562] ahci f2540000.sata: No port enabled
+
+Update all accessesors of ahci_host_priv phys and target_pwrs arrays to
+support holes. Access is gated by hpriv->mask_port_map which has a bit
+set for each enabled port.
+
+Update ahci_platform_get_resources to ignore holes in the port numbers
+and enable ports defined in firmware by their reg property only.
+
+When firmware does not define children it is assumed that there is
+exactly one port, using index 0.
+
+Signed-off-by: Josua Mayer <josua@solid-run.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_brcm.c        |  3 +++
+ drivers/ata/ahci_ceva.c        |  6 +++++
+ drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++-----
+ 3 files changed, 43 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index 2f16524c25262..60505e1e247ab 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 11a2c199a7c24..6e115da23d3f0 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_rsts;
+@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_power_on(hpriv->phys[i]);
+               if (rc) {
+                       phy_exit(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index 7a8064520a35b..b68777841f7a5 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -49,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+@@ -70,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -88,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -432,6 +441,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,
+       return 0;
+ }
++static u32 ahci_platform_find_max_port_id(struct device *dev)
++{
++      u32 max_port = 0;
++
++      for_each_child_of_node_scoped(dev->of_node, child) {
++              u32 port;
++
++              if (!of_property_read_u32(child, "reg", &port))
++                      max_port = max(max_port, port);
++      }
++
++      return max_port;
++}
++
+ /**
+  * ahci_platform_get_resources - Get platform resources
+  * @pdev: platform device to get resources for
+@@ -458,6 +481,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+       struct device *dev = &pdev->dev;
+       struct ahci_host_priv *hpriv;
+       u32 mask_port_map = 0;
++      u32 max_port;
+       if (!devres_open_group(dev, NULL, GFP_KERNEL))
+               return ERR_PTR(-ENOMEM);
+@@ -549,15 +573,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+               goto err_out;
+       }
++      /* find maximum port id for allocating structures */
++      max_port = ahci_platform_find_max_port_id(dev);
+       /*
+-       * If no sub-node was found, we still need to set nports to
+-       * one in order to be able to use the
++       * Set nports according to maximum port id. Clamp at
++       * AHCI_MAX_PORTS, warning message for invalid port id
++       * is generated later.
++       * When DT has no sub-nodes max_port is 0, nports is 1,
++       * in order to be able to use the
+        * ahci_platform_[en|dis]able_[phys|regulators] functions.
+        */
+-      if (child_nodes)
+-              hpriv->nports = child_nodes;
+-      else
+-              hpriv->nports = 1;
++      hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1);
+       hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL);
+       if (!hpriv->phys) {
+@@ -625,6 +651,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+                * If no sub-node was found, keep this for device tree
+                * compatibility
+                */
++              hpriv->mask_port_map |= BIT(0);
++
+               rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node);
+               if (rc)
+                       goto err_out;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
new file mode 100644 (file)
index 0000000..8d46017
--- /dev/null
@@ -0,0 +1,44 @@
+From 422ddceb81d0e95e86b7bcf735d87f39f932944b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 15:31:37 -0700
+Subject: ata: sata_mv: accept 1 or 2 resources in platform probe
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
+
+Board files in arch/arm/plat-orion, arch/arm/mach-dove,
+arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the
+"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ).
+Those devices are rejected with -EINVAL, so SATA no longer probes on
+legacy Marvell Orion/Kirkwood-style boards.
+
+Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2
+resources (legacy, IRQ supplied as a second resource) so both probing
+paths work.
+
+Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone")
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/sata_mv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
+index 05c905827dc5c..40fdc422940dd 100644
+--- a/drivers/ata/sata_mv.c
++++ b/drivers/ata/sata_mv.c
+@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
+       /*
+        * Simple resource validation ..
+        */
+-      if (unlikely(pdev->num_resources != 1)) {
++      if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
+               dev_err(&pdev->dev, "invalid number of resources\n");
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch
new file mode 100644 (file)
index 0000000..3a31c2b
--- /dev/null
@@ -0,0 +1,40 @@
+From 6f8809af7f3a2ebcd59dba21ea5632021e3be7b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 01:54:40 -0700
+Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV
+
+From: Zijun Hu <zijun.hu@oss.qualcomm.com>
+
+[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ]
+
+btintel_diagnostics() accesses tlv->val[0] without first validating
+that the diagnostics VSE is long enough to contain that field, so
+may cause reading data beyond the received frame.
+
+Fix by validating the length before access.
+
+Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support")
+Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bluetooth/btintel.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
+index 6e3e4a817e727..cb948423a9bd8 100644
+--- a/drivers/bluetooth/btintel.c
++++ b/drivers/bluetooth/btintel.c
+@@ -3335,6 +3335,9 @@ int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+       struct intel_tlv *tlv = (void *)&skb->data[5];
++      if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
++              goto recv_frame;
++
+       /* The first event is always an event type TLV */
+       if (tlv->type != INTEL_TLV_TYPE_ID)
+               goto recv_frame;
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
new file mode 100644 (file)
index 0000000..905477d
--- /dev/null
@@ -0,0 +1,54 @@
+From d6bc576906b88c8281ddf03cf3ec9d912e81939f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:17 +0300
+Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that.
+
+Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index 93c14e2d66c8a..c5b006d9d974a 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2925,6 +2925,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+       return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
+ }
++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
++{
++      struct hci_conn *conn = data;
++
++      hci_conn_put(conn);
++}
++
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
+@@ -2950,6 +2957,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
++                                  abort_conn_destroy);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
new file mode 100644 (file)
index 0000000..a33f332
--- /dev/null
@@ -0,0 +1,43 @@
+From b46e5385dfa1cf84f94da8d1072c1567aa36032a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:22 +0300
+Subject: Bluetooth: hci_sync: fix hci_conn_del() use in
+ hci_le_create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ]
+
+hci_conn_del() caller must hold hdev->lock, check the conn was not
+concurrently deleted, and usually inform socket the conn is going to be
+deleted.
+
+Use hci_abort_conn_sync() instead of calling hci_conn_del() without
+locks etc.
+
+Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 111b8f3c77296..8c48d459a5415 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6593,7 +6593,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE &&
+                   !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
+-                      hci_conn_del(conn);
++                      conn->state = BT_OPEN;
++                      hci_abort_conn_sync(hdev, conn,
++                                          HCI_ERROR_REJ_LIMITED_RESOURCES);
+                       hci_conn_put(conn);
+                       return -EBUSY;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
new file mode 100644 (file)
index 0000000..bd49bf2
--- /dev/null
@@ -0,0 +1,64 @@
+From c5e57578c6b08d8c9a779685fff2d85c153f3b54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 25 Mar 2026 21:07:45 +0200
+Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if
+ exists
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ]
+
+hci_cmd_sync_run_once() needs to indicate whether a queue item was
+added, so caller can know if callbacks are called, so it can avoid
+leaking resources.
+
+Change the function to return -EEXIST if queue item already exists.
+
+Modify all callsites vs. the changes.  The only callsite is
+hci_abort_conn().
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 4 +++-
+ net/bluetooth/hci_sync.c | 2 +-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index ac98e3f2344e2..93c14e2d66c8a 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2928,6 +2928,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
++      int err;
+       /* If abort_reason has already been set it means the connection is
+        * already being aborted so don't attempt to overwrite it.
+@@ -2949,5 +2950,6 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      return (err == -EEXIST) ? 0 : err;
+ }
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index d2e8543b5a51e..111b8f3c77296 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -825,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                         void *data, hci_cmd_sync_work_destroy_t destroy)
+ {
+       if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
+-              return 0;
++              return -EEXIST;
+       return hci_cmd_sync_run(hdev, func, data, destroy);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
new file mode 100644 (file)
index 0000000..2260403
--- /dev/null
@@ -0,0 +1,80 @@
+From bf0aa0cc8c7feb530fd50e157eec3f0a361bafbd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:23 +0300
+Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in
+ create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ]
+
+hci_conn_get() without already held reference is data race against
+concurrent deletion.
+
+In previous patches, the refcount has been changed to be taken before
+starting the hci_sync task, so remove these extra get() + put() as they
+are not needed.
+
+Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 8c48d459a5415..03bbf4c1d6e5a 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6577,11 +6577,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       bt_dev_dbg(hdev, "conn %p", conn);
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() at done.
+-       */
+-      hci_conn_get(conn);
+-
+       clear_bit(HCI_CONN_SCANNING, &conn->flags);
+       conn->state = BT_CONNECT;
+@@ -6596,7 +6591,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+                       conn->state = BT_OPEN;
+                       hci_abort_conn_sync(hdev, conn,
+                                           HCI_ERROR_REJ_LIMITED_RESOURCES);
+-                      hci_conn_put(conn);
+                       return -EBUSY;
+               }
+@@ -6690,7 +6684,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       /* Re-enable advertising after the connection attempt is finished. */
+       hci_resume_advertising_sync(hdev);
+-      hci_conn_put(conn);
+       return err;
+ }
+@@ -6965,11 +6958,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+       else
+               cp.role_switch = 0x00;
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() below.
+-       */
+-      hci_conn_get(conn);
+-
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+@@ -6981,7 +6969,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+                                      conn->conn_timeout, NULL);
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+-      hci_conn_put(conn);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
new file mode 100644 (file)
index 0000000..3d1e4d4
--- /dev/null
@@ -0,0 +1,188 @@
+From dc376bb410f214396c5ba9c4919a4623527482bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:31 +0300
+Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ]
+
+iso_sock_timeout() takes lock_sock, so sync disabling the timer while
+holding that lock may deadlock.
+
+iso_sock_timeout() may also run concurrently with iso_conn_del(), which
+leads to UAF
+
+        [Task 1]                      [Task hdev->workqueue]
+        iso_sock_timeout              iso_conn_del
+          iso_conn_hold_unless_zero     iso_chan_del
+                           `------------> iso_conn_put
+                                      caller frees hcon
+          iso_conn_put
+            iso_conn_free
+              conn->hcon->iso_data = NULL; /* UAF */
+
+Fix the deadlock by removing the disable from the lock_sock sections.
+Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn
+which may need to be freed in lock_sock section. Convert some of the
+clear_timer to disable_timer.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 60 ++++++++++++++++++++++-----------------------
+ 1 file changed, 29 insertions(+), 31 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index a1c350defcb3a..5b53de4589471 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -30,8 +30,6 @@ struct iso_conn {
+       spinlock_t      lock;
+       struct sock     *sk;
+-      struct delayed_work     timeout_work;
+-
+       struct sk_buff  *rx_skb;
+       __u32           rx_len;
+       __u16           tx_sn;
+@@ -74,6 +72,7 @@ struct iso_pinfo {
+       __u8                    base_len;
+       __u8                    base[BASE_MAX_LENGTH];
+       struct iso_conn         *conn;
++      struct delayed_work     timeout_work;
+ };
+ static struct bt_iso_qos default_qos;
+@@ -109,9 +108,6 @@ static void iso_conn_free(struct kref *ref)
+               hci_conn_drop(conn->hcon);
+       }
+-      /* Ensure no more work items will run since hci_conn has been dropped */
+-      disable_delayed_work_sync(&conn->timeout_work);
+-
+       kfree_skb(conn->rx_skb);
+       kfree(conn);
+@@ -152,48 +148,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
+ static void iso_sock_timeout(struct work_struct *work)
+ {
+-      struct iso_conn *conn = container_of(work, struct iso_conn,
+-                                           timeout_work.work);
+-      struct sock *sk;
+-
+-      conn = iso_conn_hold_unless_zero(conn);
+-      if (!conn)
+-              return;
+-
+-      iso_conn_lock(conn);
+-      sk = iso_sock_hold(conn);
+-      iso_conn_unlock(conn);
+-      iso_conn_put(conn);
+-
+-      if (!sk)
+-              return;
++      struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
++                                          timeout_work.work);
++      struct sock *sk = &pi->bt.sk;
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+       lock_sock(sk);
+-      sk->sk_err = ETIMEDOUT;
+-      sk->sk_state_change(sk);
++      if (!sock_flag(sk, SOCK_ZAPPED)) {
++              sk->sk_err = ETIMEDOUT;
++              sk->sk_state_change(sk);
++      }
+       release_sock(sk);
+-      sock_put(sk);
+ }
+ static void iso_sock_set_timer(struct sock *sk, long timeout)
+ {
++      lockdep_assert(lockdep_sock_is_held(sk));
++
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++
+       if (!iso_pi(sk)->conn)
+               return;
+       BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
+-      schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
++      schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
+ }
+ static void iso_sock_clear_timer(struct sock *sk)
+ {
+-      if (!iso_pi(sk)->conn)
+-              return;
++      BT_DBG("sock %p state %d", sk, sk->sk_state);
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++}
++
++static void iso_sock_disable_timer(struct sock *sk)
++{
++      lockdep_assert(!lockdep_sock_is_held(sk));
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
++      disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
+ }
+ /* ---- ISO connections ---- */
+@@ -218,7 +211,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+       kref_init(&conn->ref);
+       spin_lock_init(&conn->lock);
+-      INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
+       hcon->iso_data = conn;
+       conn->hcon = hcon;
+@@ -283,8 +275,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+               return;
+       }
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
+       iso_sock_kill(sk);
+@@ -766,6 +759,8 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+@@ -864,8 +859,9 @@ static void __iso_sock_close(struct sock *sk)
+ /* Must be called on unlocked socket. */
+ static void iso_sock_close(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+ }
+@@ -934,6 +930,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
+       iso_pi(sk)->qos = default_qos;
+       iso_pi(sk)->sync_handle = -1;
++      INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
++
+       bt_sock_link(&iso_sk_list, sk);
+       return sk;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
new file mode 100644 (file)
index 0000000..a9fef24
--- /dev/null
@@ -0,0 +1,40 @@
+From d47c5fc619ff386de38abc5e94517921f5f9bbf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 17:53:33 +0300
+Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ]
+
+When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is
+necessary, otherwise later iso_conn_free() will UAF.
+
+Fix clearing of iso_data in iso_sock_disconn()
+
+Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on
+iso_sock_release() followed by hci_abort_conn_sync().
+
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 12a01dc6dbde3..1d3e4616ee700 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -811,6 +811,7 @@ static void iso_sock_disconn(struct sock *sk)
+       sk->sk_state = BT_DISCONN;
+       iso_conn_lock(iso_pi(sk)->conn);
+       hci_conn_drop(iso_pi(sk)->conn->hcon);
++      iso_pi(sk)->conn->hcon->iso_data = NULL;
+       iso_pi(sk)->conn->hcon = NULL;
+       iso_conn_unlock(iso_pi(sk)->conn);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch
new file mode 100644 (file)
index 0000000..ab7e531
--- /dev/null
@@ -0,0 +1,108 @@
+From 649540b07cdaa3de1345775782ecd9c926b7870d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:30 +0300
+Subject: Bluetooth: ISO: fix leaking sk after socket release
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ]
+
+iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+sock_flag(sk, SOCK_DEAD) for early return, but this is always true since
+sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket
+always leaks, iso_sock_destruct is never called.
+
+The socket reference also leaks when __iso_sock_close() does not set
+SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after
+zapping.
+
+Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for
+something else, and lock_sock to ensure iso_sock_kill() puts sk only
+after socket release only once. Release and iso_conn_del may run
+concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up
+after zapping.
+
+Remove call to iso_sock_kill() from iso_sock_close(), as it's generally
+no-op there.
+
+Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 06c68e053b423..a1c350defcb3a 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -55,6 +55,7 @@ static void iso_sock_kill(struct sock *sk);
+ enum {
+       BT_SK_BIG_SYNC,
+       BT_SK_PA_SYNC,
++      BT_SK_KILLED,
+ };
+ struct iso_pinfo {
+@@ -286,6 +287,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
++      iso_sock_kill(sk);
+       sock_put(sk);
+ }
+@@ -764,9 +766,13 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      lock_sock(sk);
++
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+-          sock_flag(sk, SOCK_DEAD))
++          test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
++              release_sock(sk);
+               return;
++      }
+       BT_DBG("sk %p state %d", sk, sk->sk_state);
+@@ -780,6 +786,9 @@ static void iso_sock_kill(struct sock *sk)
+       /* Kill poor orphan */
+       bt_sock_unlink(&iso_sk_list, sk);
+       sock_set_flag(sk, SOCK_DEAD);
++      set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
++
++      release_sock(sk);
+       sock_put(sk);
+ }
+@@ -859,7 +868,6 @@ static void iso_sock_close(struct sock *sk)
+       iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+-      iso_sock_kill(sk);
+ }
+ static void iso_sock_init(struct sock *sk, struct sock *parent)
+@@ -1867,8 +1875,16 @@ static int iso_sock_release(struct socket *sock)
+               release_sock(sk);
+       }
++      /* Make sure sk is valid even if iso_conn_del() is concurrent */
++      sock_hold(sk);
++
++      lock_sock(sk);
+       sock_orphan(sk);
++      release_sock(sk);
++
+       iso_sock_kill(sk);
++
++      sock_put(sk);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
new file mode 100644 (file)
index 0000000..c9256fc
--- /dev/null
@@ -0,0 +1,38 @@
+From 654412fcc7691609b382b27bd510dde83c7868f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:27 +0300
+Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ]
+
+In iso.c check_bcast_qos(), missing bcast.timeout is not set to its
+default value, and appears typoed as bcast.sync_timeout.
+
+Fix the typo.
+
+Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 1d3e4616ee700..c54e42ab6947b 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1636,7 +1636,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
+               return false;
+       if (!qos->bcast.timeout)
+-              qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
++              qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
+       if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
new file mode 100644 (file)
index 0000000..0467fc6
--- /dev/null
@@ -0,0 +1,49 @@
+From a2c3af294a2640b163fd89a77365084dbf3a6404 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:28 +0300
+Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ]
+
+iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before
+validating the BIS values, so it's possible to end up with bc_num_bis
+inconsistent.
+
+Assign to iso_pi(sk)->bc_num_bis only after validation.
+
+Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index c54e42ab6947b..06c68e053b423 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1014,15 +1014,15 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
+               goto done;
+       }
+-      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
+-
+-      for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
++      for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
+               if (sa->iso_bc->bc_bis[i] < 0x01 ||
+                   sa->iso_bc->bc_bis[i] > 0x1f) {
+                       err = -EINVAL;
+                       goto done;
+               }
++      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
++
+       memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
+              iso_pi(sk)->bc_num_bis);
+-- 
+2.53.0
+
diff --git a/queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..5b6733f
--- /dev/null
@@ -0,0 +1,60 @@
+From 134062bd43a64988de24fb821be663dce7e7c831 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 56337f164d306..c876f986b27f5 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -4796,6 +4796,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       if (!chan)
+               return -EBADSLT;
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -4841,6 +4845,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
new file mode 100644 (file)
index 0000000..c5a6963
--- /dev/null
@@ -0,0 +1,79 @@
+From 620aa39fa2a26a8505e75294d0039299c7e999e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 4 Jul 2026 17:58:56 +0930
+Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ]
+
+[BUG]
+The following script can lead to unexpected qgroup rescan failure:
+
+  # mkfs.btrfs -f -O quota $dev
+  # mount $dev $mnt
+  # mount -o remount,rescue=ibadroots $mnt
+    ^^^^^ This above command is expected to fail
+
+  # btrfs quota rescan -w $mnt
+    ^^^^^ The above qgroup rescan is not expected to fail
+
+  # btrfs qgroup show $mnt
+  WARNING: qgroup data inconsistent, rescan recommended
+  Qgroupid    Referenced    Exclusive   Path
+  --------    ----------    ---------   ----
+  0/5           16.00KiB     16.00KiB   <toplevel>
+
+The above short script will be converted to a proper fstests case.
+
+[CAUSE]
+Inside btrfs_reconfigure(), if either btrfs_check_options() or
+btrfs_check_features() failed, we will always have
+BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful
+remount.
+
+That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations,
+including:
+
+- Qgroup rescan
+- Auto defrag
+- Space reclaim
+
+[FIX]
+Change the error handling of btrfs_check_options() and
+btrfs_check_features() to goto restore label.
+
+Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context")
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.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>
+---
+ fs/btrfs/super.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index 21419bc33863b..c8ad43f94db0d 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -1509,12 +1509,14 @@ static int btrfs_reconfigure(struct fs_context *fc)
+       sync_filesystem(sb);
+       set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
+-      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags))
+-              return -EINVAL;
++      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) {
++              ret = -EINVAL;
++              goto restore;
++      }
+       ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY));
+       if (ret < 0)
+-              return ret;
++              goto restore;
+       btrfs_ctx_to_info(fs_info, ctx);
+       btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags);
+-- 
+2.53.0
+
diff --git a/queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
new file mode 100644 (file)
index 0000000..ccd9112
--- /dev/null
@@ -0,0 +1,77 @@
+From fa601d17fc8d2cc253ad5a7477c23ca0bce3553a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:40 +0200
+Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction
+ commit
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ]
+
+When writing out metadata extent buffers in a zoned filesystem,
+btree_writepages() holds fs_info->zoned_meta_io_lock across the whole
+writeback loop, including the call to btrfs_check_meta_write_pointer() ->
+check_bg_is_active().
+
+For the tree-log block group, check_bg_is_active() may fail to activate
+the zone and fall back to btrfs_zone_finish_one_bg() to free an active
+zone. That path waits for the running transaction to commit while still
+holding zoned_meta_io_lock, but the committer needs that same lock to
+write out the tree extents, so the two tasks deadlock:
+
+  Task A (kworker, metadata writeback)      Task B (fsstress, transaction commit)
+  ------------------------------------      -------------------------------------
+  wb_workfn()                               btrfs_commit_transaction(T)
+   btree_writepages()                        btrfs_write_and_wait_transaction()
+    btrfs_zoned_meta_io_lock()                btrfs_write_marked_extents()
+    btrfs_check_meta_write_pointer()           btree_writepages()
+     check_bg_is_active() [treelog_bg]          btrfs_zoned_meta_io_lock()
+      btrfs_zone_finish_one_bg()               <blocks on zoned_meta_io_lock,
+       btrfs_zone_finish()                      held by Task A>
+        do_zone_finish()
+         btrfs_inc_block_group_ro()
+          btrfs_wait_for_commit()
+           <blocks waiting for commit
+            of transaction T, done by
+            Task B>
+
+The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock
+around do_zone_finish() for this exact reason. Do the same in the tree-log
+branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire
+it afterwards. The lock only protects fs_info->active_{meta,system}_bg,
+which this branch does not touch, and ctx->zoned_bg keeps a reference to
+the block group across the unlock, so nothing is lost while the lock
+is dropped.
+
+This hang occasionally reproduces with fstests generic/475 on a zoned
+btrfs filesystem.
+
+Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index d9c26f4be6634..c64840b155974 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -2122,7 +2122,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx,
+       if (fs_info->treelog_bg == block_group->start) {
+               if (!btrfs_zone_activate(block_group)) {
+-                      int ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      int ret_fin;
++
++                      btrfs_zoned_meta_io_unlock(fs_info);
++                      ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      btrfs_zoned_meta_io_lock(fs_info);
+                       if (ret_fin != 1 || !btrfs_zone_activate(block_group))
+                               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..7c3948b
--- /dev/null
@@ -0,0 +1,57 @@
+From 4d89225fb4ee31005481671fc696246d8131cbda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index b2622c881aa32..7e3d6afbb24f9 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1885,13 +1885,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
new file mode 100644 (file)
index 0000000..9c9e4fd
--- /dev/null
@@ -0,0 +1,65 @@
+From e1a2c6a57346c8c06923b8dc8509cd107d91b73d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 May 2026 10:15:50 -0400
+Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open()
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ]
+
+The failed_dev_add and failed_dev_name paths drop the file-device
+reference while wq->wq_lock is still held. If put_device(fdev) drops the
+last reference, idxd_file_dev_release() runs synchronously and tries to
+take wq->wq_lock again, deadlocking.
+
+Those paths also fall through into the later ctx cleanup labels even
+though idxd_file_dev_release() owns that cleanup and frees ctx. This can
+make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context.
+
+Move idxd_wq_get() before file-device setup can fail, since the release
+callback always calls idxd_wq_put(). Then unlock wq->wq_lock before
+put_device(fdev) and return directly from the file-device setup failure
+path, leaving ctx cleanup to the release callback.
+
+Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/idxd/cdev.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
+index 8dcd2331bb1ac..14b322ac6e6c6 100644
+--- a/drivers/dma/idxd/cdev.c
++++ b/drivers/dma/idxd/cdev.c
+@@ -293,6 +293,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+       fdev->parent = cdev_dev(idxd_cdev);
+       fdev->bus = &dsa_bus_type;
+       fdev->type = &idxd_cdev_file_type;
++      idxd_wq_get(wq);
+       rc = dev_set_name(fdev, "file%d", ctx->id);
+       if (rc < 0) {
+@@ -306,13 +307,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+               goto failed_dev_add;
+       }
+-      idxd_wq_get(wq);
+       mutex_unlock(&wq->wq_lock);
+       return 0;
+ failed_dev_add:
+ failed_dev_name:
++      mutex_unlock(&wq->wq_lock);
+       put_device(fdev);
++      return rc;
+ failed_ida:
+ failed_set_pasid:
+       if (device_user_pasid_enabled(idxd))
+-- 
+2.53.0
+
diff --git a/queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..4ccc431
--- /dev/null
@@ -0,0 +1,60 @@
+From cefa1bd14be4e30d76167c7fd3bb7ac129653ba0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index c97027379263d..1f1e21d48ad76 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -934,16 +934,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
new file mode 100644 (file)
index 0000000..9daa089
--- /dev/null
@@ -0,0 +1,55 @@
+From 6ae2dec37a9c63d3a8edd417463590c505cfd547 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 17:32:15 +0200
+Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep
+ annotation
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ]
+
+lockdep_hardirq_threaded() is supposed to be used within IRQ core code
+and not within drivers. It is not obvious from within the driver, that
+this is the only interrupt service routing and that it is not shared
+handler.
+
+Replace lockdep_hardirq_threaded() with a lockdep annotation limiting
+threaded context on PREEMPT_RT to __vmbus_isr().
+
+Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/vmbus_drv.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index 83f3215bc5bd5..db1c71832f2a8 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -1320,8 +1320,19 @@ static void vmbus_isr(void)
+       if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
+               vmbus_irqd_wake();
+       } else {
+-              lockdep_hardirq_threaded();
++              static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG);
++
++              /*
++               * vmbus_isr is never force-threaded and always invoked at hard
++               * IRQ level. __vmbus_isr() below can acquire a spinlock_t
++               * which becomes a sleeping lock and must not be acquired in
++               * this context. Therefore on PREEMPT_RT this will be threaded
++               * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets
++               * lockdep know that acquiring a spinlock_t is not an issue.
++               */
++              lock_map_acquire_try(&vmbus_map);
+               __vmbus_isr();
++              lock_map_release(&vmbus_map);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..4da5843
--- /dev/null
@@ -0,0 +1,51 @@
+From afaf46be6995ffa18203a22b5b2dcf836b63ab48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
+index c4c6d0249df56..aa8e6f6ddcd32 100644
+--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
+@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+-- 
+2.53.0
+
diff --git a/queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..840877d
--- /dev/null
@@ -0,0 +1,47 @@
+From 802f89fdfd20e58b4b66a34eb84c8f28a3191e9b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index 720f577929dbf..11eb3751ad27b 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
new file mode 100644 (file)
index 0000000..030f58b
--- /dev/null
@@ -0,0 +1,55 @@
+From bcca6a0d8f2e509af85196c5f0aed86896b16d25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:23:08 +0530
+Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe()
+
+From: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+
+[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ]
+
+The memory allocated for priv->blob.data is not freed in the error paths
+that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as
+well as in the remove function. Fix that by using device managed action
+to free the memory on remove.
+
+Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling")
+Signed-off-by: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
+Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+index 59a8f3a5c4e48..9c526aeadeba5 100644
+--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c
++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val)
+       return 0;
+ }
++static void fops_buf_release(void *data)
++{
++      struct gpio_la_poll_priv *priv = data;
++
++      vfree(priv->blob.data);
++}
++
+ static int fops_buf_size_set(void *data, u64 val)
+ {
+       struct gpio_la_poll_priv *priv = data;
+@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
+               return ret;
+       fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
++      ret = devm_add_action_or_reset(dev, fops_buf_release, priv);
++      if (ret)
++              return ret;
+       priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN);
+       if (IS_ERR(priv->descs))
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..3abd48c
--- /dev/null
@@ -0,0 +1,51 @@
+From 7def045476cbcdccffe6ed6d4b309ca6df7e9479 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 06b19fd382457..a927e0b6d3319 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..7f39f62
--- /dev/null
@@ -0,0 +1,83 @@
+From 6304effb41b2403128474055a83ca1b901ae8ff8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index ef8f411df61be..06b19fd382457 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..5c221b4
--- /dev/null
@@ -0,0 +1,80 @@
+From bb83793a4f35c487fe921041d8fa49b7eb388a83 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 43c0130342622..3a2d408a45be8 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..db37a30
--- /dev/null
@@ -0,0 +1,115 @@
+From dc9f40e665408656450242c83fff4c80a92bcd74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index dbee6926fa055..ef8f411df61be 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..366a71d
--- /dev/null
@@ -0,0 +1,57 @@
+From 7e42adb13766ac36489ac5b17d728329a71128e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 3a2d408a45be8..0fad4bfe53df6 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..0808b98
--- /dev/null
@@ -0,0 +1,62 @@
+From 3cd57f54c689f2a5c26fc723ca6ae56dc7144b09 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 41fa6a0e8b937..f0a45538ded6d 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..442d8e5
--- /dev/null
@@ -0,0 +1,71 @@
+From 803427a90180b4ee8f889e29494c29fb88efe2e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index a927e0b6d3319..41fa6a0e8b937 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..1f7cfce
--- /dev/null
@@ -0,0 +1,111 @@
+From 21c7cb69dd5567b8b8b2940b14283c7d99cea692 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index f0a45538ded6d..43c0130342622 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina226-add-support-for-sy24655.patch b/queue-6.12/hwmon-ina226-add-support-for-sy24655.patch
new file mode 100644 (file)
index 0000000..3129ebc
--- /dev/null
@@ -0,0 +1,326 @@
+From 9fb0ae89106658b3fcceafd5df7aae95866432da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 Nov 2024 10:05:46 -0500
+Subject: hwmon: (ina226) Add support for SY24655
+
+From: Wenliang Yan <wenliang202407@163.com>
+
+[ Upstream commit 52172ad87a22ed6e687ca678da21d3c949bc89a1 ]
+
+SY24655: Support for current and voltage detection as well as
+power calculation.
+
+Signed-off-by: Wenliang Yan <wenliang202407@163.com>
+Message-ID: <20241106150547.2538-1-wenliang202407@163.com>
+[groeck: Changed order of compatible entries;
+ dropped spurious extra return statement in is_visible();
+ fixed code problems]
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 27 +++++++++-
+ drivers/hwmon/Kconfig          |  2 +-
+ drivers/hwmon/ina2xx.c         | 94 ++++++++++++++++++++++++++++++++--
+ 3 files changed, 116 insertions(+), 7 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index 1ce161e6c0bf0..a3860aae444c0 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -63,6 +63,17 @@ Supported chips:
+              https://www.ti.com/
++  * Silergy SY24655
++
++    Prefix: 'sy24655'
++
++    Addresses: I2C 0x40 - 0x4f
++
++    Datasheet: Publicly available at the Silergy website
++
++             https://us1.silergy.com/
++
++
+ Author: Lothar Felten <lothar.felten@gmail.com>
+ Description
+@@ -85,6 +96,11 @@ bus supply voltage.
+ INA260 is a high or low side current and power monitor with integrated shunt
+ resistor.
++The SY24655 is a high- and low-side current shunt and power monitor with an I2C
++interface. The SY24655 supports both shunt drop and supply voltage, with
++programmable calibration value and conversion times. The SY24655 can also
++calculate average power for use in energy conversion.
++
+ The shunt value in micro-ohms can be set via platform data or device tree at
+ compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
+ refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
+@@ -108,8 +124,8 @@ power1_input               Power(uW) measurement channel
+ shunt_resistor                Shunt resistance(uOhm) channel (not for ina260)
+ ======================= ===============================================
+-Additional sysfs entries for ina226, ina230, ina231, and ina260
+----------------------------------------------------------------
++Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
++------------------------------------------------------------------------
+ ======================= ====================================================
+ curr1_lcrit           Critical low current
+@@ -130,6 +146,13 @@ update_interval           data conversion time; affects number of samples used
+                       to average results for shunt and bus voltages.
+ ======================= ====================================================
++Sysfs entries for sy24655 only
++------------------------------
++
++======================= ====================================================
++power1_average                average power from last reading to the present.
++======================= ====================================================
++
+ .. note::
+    - Configure `shunt_resistor` before configure `power1_crit`, because power
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 681722626bc48..195c7b505a853 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2170,7 +2170,7 @@ config SENSORS_INA2XX
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for INA219, INA220, INA226,
+-        INA230, INA231, and INA260 power monitor chips.
++        INA230, INA231, INA260, and SY24655 power monitor chips.
+         The INA2xx driver is configured for the default configuration of
+         the part as described in the datasheet.
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index cecc80a41a974..345fe7db9de94 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -51,12 +51,19 @@
+ #define INA226_ALERT_LIMIT            0x07
+ #define INA226_DIE_ID                 0xFF
+-#define INA2XX_MAX_REGISTERS          8
++/* SY24655 register definitions */
++#define SY24655_EIN                           0x0A
++#define SY24655_ACCUM_CONFIG  0x0D
++#define INA2XX_MAX_REGISTERS          0x0D
+ /* settings - depend on use case */
+ #define INA219_CONFIG_DEFAULT         0x399F  /* PGA=8 */
+ #define INA226_CONFIG_DEFAULT         0x4527  /* averages=16 */
+ #define INA260_CONFIG_DEFAULT         0x6527  /* averages=16 */
++#define SY24655_CONFIG_DEFAULT                0x4527  /* averages=16 */
++
++/* (only for sy24655) */
++#define SY24655_ACCUM_CONFIG_DEFAULT  0x044C  /* continuous mode, clear after read*/
+ /* worst case is 68.10 ms (~14.6Hz, ina219) */
+ #define INA2XX_CONVERSION_RATE                15
+@@ -97,6 +104,7 @@ static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg)
+       case INA2XX_CALIBRATION:
+       case INA226_MASK_ENABLE:
+       case INA226_ALERT_LIMIT:
++      case SY24655_ACCUM_CONFIG:
+               return true;
+       default:
+               return false;
+@@ -127,12 +135,13 @@ static const struct regmap_config ina2xx_regmap_config = {
+       .writeable_reg = ina2xx_writeable_reg,
+ };
+-enum ina2xx_ids { ina219, ina226, ina260 };
++enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
+ struct ina2xx_config {
+       u16 config_default;
+       bool has_alerts;        /* chip supports alerts and limits */
+       bool has_ishunt;        /* chip has internal shunt resistor */
++      bool has_power_average; /* chip has internal shunt resistor */
+       int calibration_value;
+       int shunt_div;
+       int bus_voltage_shift;
+@@ -149,6 +158,7 @@ struct ina2xx_data {
+       long power_lsb_uW;
+       struct mutex config_lock;
+       struct regmap *regmap;
++      struct i2c_client *client;
+ };
+ static const struct ina2xx_config ina2xx_config[] = {
+@@ -161,6 +171,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .power_lsb_factor = 20,
+               .has_alerts = false,
+               .has_ishunt = false,
++              .has_power_average = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+@@ -171,6 +182,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .power_lsb_factor = 25,
+               .has_alerts = true,
+               .has_ishunt = false,
++              .has_power_average = false,
+       },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+@@ -180,6 +192,18 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .power_lsb_factor = 8,
+               .has_alerts = true,
+               .has_ishunt = true,
++              .has_power_average = false,
++      },
++      [sy24655] = {
++              .config_default = SY24655_CONFIG_DEFAULT,
++              .calibration_value = 4096,
++              .shunt_div = 400,
++              .bus_voltage_shift = 0,
++              .bus_voltage_lsb = 1250,
++              .power_lsb_factor = 25,
++              .has_alerts = true,
++              .has_ishunt = false,
++              .has_power_average = true,
+       },
+ };
+@@ -485,6 +509,41 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)
+       return 0;
+ }
++/*
++ * Configuring the READ_EIN (bit 10) of the ACCUM_CONFIG register to 1
++ * can clear accumulator and sample_count after reading the EIN register.
++ * This way, the average power between the last read and the current
++ * read can be obtained. By combining with accurate time data from
++ * outside, the energy consumption during that period can be calculated.
++ */
++static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *val)
++{
++      u8 template[6];
++      int ret;
++      long accumulator_24, sample_count;
++
++      /* 48-bit register read */
++      ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
++      if (ret < 0)
++              return ret;
++      if (ret != 6)
++              return -EIO;
++      accumulator_24 = ((template[3] << 16) |
++                              (template[4] << 8) |
++                              template[5]);
++      sample_count = ((template[0] << 16) |
++                              (template[1] << 8) |
++                              template[2]);
++      if (sample_count <= 0) {
++              *val = 0;
++              return 0;
++      }
++
++      *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++
++      return 0;
++}
++
+ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
+ {
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+@@ -492,6 +551,8 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
+       switch (attr) {
+       case hwmon_power_input:
+               return ina2xx_read_init(dev, INA2XX_POWER, val);
++      case hwmon_power_average:
++              return sy24655_average_power_read(data, SY24655_EIN, val);
+       case hwmon_power_crit:
+               return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK,
+                                              INA2XX_POWER, val);
+@@ -651,6 +712,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+ {
+       const struct ina2xx_data *data = _data;
+       bool has_alerts = data->config->has_alerts;
++      bool has_power_average = data->config->has_power_average;
+       enum ina2xx_ids chip = data->chip;
+       switch (type) {
+@@ -702,6 +764,10 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+                       if (has_alerts)
+                               return 0444;
+                       break;
++              case hwmon_power_average:
++                      if (has_power_average)
++                              return 0444;
++                      break;
+               default:
+                       break;
+               }
+@@ -734,7 +800,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = {
+       HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM |
+                          HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM),
+       HWMON_CHANNEL_INFO(power,
+-                         HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
++                         HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM |
++                         HWMON_P_AVERAGE),
+       NULL
+ };
+@@ -839,6 +906,19 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
+                                  INA226_ALERT_LATCH_ENABLE |
+                                               FIELD_PREP(INA226_ALERT_POLARITY, active_high));
+       }
++      if (data->config->has_power_average) {
++              if (data->chip == sy24655) {
++                      /*
++                       * Initialize the power accumulation method to continuous
++                       * mode and clear the EIN register after each read of the
++                       * EIN register
++                       */
++                      ret = regmap_write(regmap, SY24655_ACCUM_CONFIG,
++                                         SY24655_ACCUM_CONFIG_DEFAULT);
++                      if (ret < 0)
++                              return ret;
++              }
++      }
+       if (data->config->has_ishunt)
+               return 0;
+@@ -868,6 +948,7 @@ static int ina2xx_probe(struct i2c_client *client)
+               return -ENOMEM;
+       /* set the device type */
++      data->client = client;
+       data->config = &ina2xx_config[chip];
+       data->chip = chip;
+       mutex_init(&data->config_lock);
+@@ -906,11 +987,16 @@ static const struct i2c_device_id ina2xx_id[] = {
+       { "ina230", ina226 },
+       { "ina231", ina226 },
+       { "ina260", ina260 },
++      { "sy24655", sy24655 },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
++      {
++              .compatible = "silergy,sy24655",
++              .data = (void *)sy24655
++      },
+       {
+               .compatible = "ti,ina219",
+               .data = (void *)ina219
+@@ -935,7 +1021,7 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
+               .compatible = "ti,ina260",
+               .data = (void *)ina260
+       },
+-      { },
++      { }
+ };
+ MODULE_DEVICE_TABLE(of, ina2xx_of_match);
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch b/queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch
new file mode 100644 (file)
index 0000000..6afc87f
--- /dev/null
@@ -0,0 +1,112 @@
+From efcdd27d43125883ee26a01f0edbd130f3df70ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 27 Aug 2024 12:57:10 -0700
+Subject: hwmon: (ina2xx) Add support for has_alerts configuration flag
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit de0da6ae1908b43b23782d64b4564b5ca3119f7f ]
+
+Add configuration flag indicating if the chip supports alerts and limits
+to prepare for adding INA260 support.
+
+Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index f0fa6d073627f..03a011c9b73da 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -129,6 +129,7 @@ enum ina2xx_ids { ina219, ina226 };
+ struct ina2xx_config {
+       u16 config_default;
++      bool has_alerts;        /* chip supports alerts and limits */
+       int calibration_value;
+       int shunt_div;
+       int bus_voltage_shift;
+@@ -155,6 +156,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .bus_voltage_shift = 3,
+               .bus_voltage_lsb = 4000,
+               .power_lsb_factor = 20,
++              .has_alerts = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+@@ -163,6 +165,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
++              .has_alerts = true,
+       },
+ };
+@@ -624,6 +627,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+                                u32 attr, int channel)
+ {
+       const struct ina2xx_data *data = _data;
++      bool has_alerts = data->config->has_alerts;
+       enum ina2xx_ids chip = data->chip;
+       switch (type) {
+@@ -633,12 +637,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+                       return 0444;
+               case hwmon_in_lcrit:
+               case hwmon_in_crit:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0644;
+                       break;
+               case hwmon_in_lcrit_alarm:
+               case hwmon_in_crit_alarm:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0444;
+                       break;
+               default:
+@@ -651,12 +655,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+                       return 0444;
+               case hwmon_curr_lcrit:
+               case hwmon_curr_crit:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0644;
+                       break;
+               case hwmon_curr_lcrit_alarm:
+               case hwmon_curr_crit_alarm:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0444;
+                       break;
+               default:
+@@ -668,11 +672,11 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+               case hwmon_power_input:
+                       return 0444;
+               case hwmon_power_crit:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0644;
+                       break;
+               case hwmon_power_crit_alarm:
+-                      if (chip == ina226)
++                      if (has_alerts)
+                               return 0444;
+                       break;
+               default:
+@@ -802,7 +806,7 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
+       if (ret < 0)
+               return ret;
+-      if (data->chip == ina226) {
++      if (data->config->has_alerts) {
+               bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high");
+               regmap_update_bits(regmap, INA226_MASK_ENABLE,
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch b/queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch
new file mode 100644 (file)
index 0000000..501d580
--- /dev/null
@@ -0,0 +1,133 @@
+From 3fb13e14f450d96e80e99770c4d894f926a6c413 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 13:20:22 +0200
+Subject: hwmon: (ina2xx) Add support for INA234
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ian Ray <ian.ray@gehealthcare.com>
+
+[ Upstream commit 88a928eebccdc5445d874ddcbf1683b76c9f1431 ]
+
+INA234 is register compatible to INA226 (excepting manufacturer and die
+or device id registers) but has different scaling.
+
+Signed-off-by: Ian Ray <ian.ray@gehealthcare.com>
+Reviewed-by: Bence Csókás <bence98@sch.bme.hu> # v2
+Tested-by: Jens Almer <bagawk@gmail.com>
+Tested-by: Jonas Rebmann <jre@pengutronix.de>
+Link: https://lore.kernel.org/r/20260220112024.97446-4-ian.ray@gehealthcare.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 13 ++++++++++++-
+ drivers/hwmon/Kconfig          |  2 +-
+ drivers/hwmon/ina2xx.c         | 18 ++++++++++++++++++
+ 3 files changed, 31 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index a4ddf4bd2b081..d64e7af46a124 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -74,6 +74,16 @@ Supported chips:
+              https://us1.silergy.com/
++  * Texas Instruments INA234
++
++    Prefix: 'ina234'
++
++    Addresses: I2C 0x40 - 0x43
++
++    Datasheet: Publicly available at the Texas Instruments website
++
++             https://www.ti.com/
++
+ Author: Lothar Felten <lothar.felten@gmail.com>
+ Description
+@@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage.
+ The INA226 is a current shunt and power monitor with an I2C interface.
+ The INA226 monitors both a shunt voltage drop and bus supply voltage.
+-INA230 and INA231 are high or low side current shunt and power monitors
++INA230, INA231, and INA234 are high or low side current shunt and power monitors
+ with an I2C interface. The chips monitor both a shunt voltage drop and
+ bus supply voltage.
+@@ -132,6 +142,7 @@ Additional entries are available for the following chips:
+   * ina226
+   * ina230
+   * ina231
++  * ina234
+   * ina260
+   * sy24655
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 195c7b505a853..848b9cfcb1d87 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2170,7 +2170,7 @@ config SENSORS_INA2XX
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for INA219, INA220, INA226,
+-        INA230, INA231, INA260, and SY24655 power monitor chips.
++        INA230, INA231, INA234, INA260, and SY24655 power monitor chips.
+         The INA2xx driver is configured for the default configuration of
+         the part as described in the datasheet.
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index 97ec0e0218307..be6e214fae21f 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -138,6 +138,7 @@ static const struct regmap_config ina2xx_regmap_config = {
+ enum ina2xx_ids {
+       ina219,
+       ina226,
++      ina234,
+       ina260,
+       sy24655
+ };
+@@ -192,6 +193,18 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_power_average = false,
+               .has_update_interval = true,
+       },
++      [ina234] = {
++              .config_default = INA226_CONFIG_DEFAULT,
++              .calibration_value = 2048,
++              .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .bus_voltage_shift = 4,
++              .bus_voltage_lsb = 25600,
++              .power_lsb_factor = 32,
++              .has_alerts = true,
++              .has_ishunt = false,
++              .has_power_average = false,
++              .has_update_interval = true,
++      },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+               .shunt_div = 400,
+@@ -996,6 +1009,7 @@ static const struct i2c_device_id ina2xx_id[] = {
+       { "ina226", ina226 },
+       { "ina230", ina226 },
+       { "ina231", ina226 },
++      { "ina234", ina234 },
+       { "ina260", ina260 },
+       { "sy24655", sy24655 },
+       { }
+@@ -1027,6 +1041,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
+               .compatible = "ti,ina231",
+               .data = (void *)ina226
+       },
++      {
++              .compatible = "ti,ina234",
++              .data = (void *)ina234
++      },
+       {
+               .compatible = "ti,ina260",
+               .data = (void *)ina260
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch b/queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch
new file mode 100644 (file)
index 0000000..922f331
--- /dev/null
@@ -0,0 +1,234 @@
+From 055a70df0cdec3010cedd2565a6fffd258d04c56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 27 Aug 2024 10:23:10 -0700
+Subject: hwmon: (ina2xx) Add support for INA260
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 70fb84a109c639637f0636281dbdb21ed8ffb000 ]
+
+INA260 is similar to other chips of the series, except it has an internal
+shunt resistor. The calibration register is therefore not present. Also,
+the current register address was changed, though that does not matter for
+the driver since the shunt voltage register (which is now the current
+register) value is already used to read the current.
+
+Cc: Loic Guegan <loic.guegan@mailbox.org>
+Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 23 +++++++++++++++----
+ drivers/hwmon/Kconfig          |  5 ++--
+ drivers/hwmon/ina2xx.c         | 42 ++++++++++++++++++++++++++++++----
+ 3 files changed, 59 insertions(+), 11 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index 7f1939b40f74f..1ce161e6c0bf0 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -53,6 +53,16 @@ Supported chips:
+              https://www.ti.com/
++  * Texas Instruments INA260
++
++    Prefix: 'ina260'
++
++    Addresses: I2C 0x40 - 0x4f
++
++    Datasheet: Publicly available at the Texas Instruments website
++
++             https://www.ti.com/
++
+ Author: Lothar Felten <lothar.felten@gmail.com>
+ Description
+@@ -72,6 +82,9 @@ INA230 and INA231 are high or low side current shunt and power monitors
+ with an I2C interface. The chips monitor both a shunt voltage drop and
+ bus supply voltage.
++INA260 is a high or low side current and power monitor with integrated shunt
++resistor.
++
+ The shunt value in micro-ohms can be set via platform data or device tree at
+ compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
+ refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
+@@ -87,16 +100,16 @@ The actual programmed interval may vary from the desired value.
+ General sysfs entries
+ ---------------------
+-======================= ===============================
++======================= ===============================================
+ in0_input             Shunt voltage(mV) channel
+ in1_input             Bus voltage(mV) channel
+ curr1_input           Current(mA) measurement channel
+ power1_input          Power(uW) measurement channel
+-shunt_resistor                Shunt resistance(uOhm) channel
+-======================= ===============================
++shunt_resistor                Shunt resistance(uOhm) channel (not for ina260)
++======================= ===============================================
+-Sysfs entries for ina226, ina230 and ina231 only
+-------------------------------------------------
++Additional sysfs entries for ina226, ina230, ina231, and ina260
++---------------------------------------------------------------
+ ======================= ====================================================
+ curr1_lcrit           Critical low current
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index acd0f86cb9b38..681722626bc48 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2170,11 +2170,12 @@ config SENSORS_INA2XX
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for INA219, INA220, INA226,
+-        INA230, and INA231 power monitor chips.
++        INA230, INA231, and INA260 power monitor chips.
+         The INA2xx driver is configured for the default configuration of
+         the part as described in the datasheet.
+-        Default value for Rshunt is 10 mOhms.
++        Default value for Rshunt is 10 mOhms except for INA260 which has an
++        internal 2 mOhm shunt resistor.
+         This driver can also be built as a module. If so, the module
+         will be called ina2xx.
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index 03a011c9b73da..cecc80a41a974 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -56,12 +56,14 @@
+ /* settings - depend on use case */
+ #define INA219_CONFIG_DEFAULT         0x399F  /* PGA=8 */
+ #define INA226_CONFIG_DEFAULT         0x4527  /* averages=16 */
++#define INA260_CONFIG_DEFAULT         0x6527  /* averages=16 */
+ /* worst case is 68.10 ms (~14.6Hz, ina219) */
+ #define INA2XX_CONVERSION_RATE                15
+ #define INA2XX_MAX_DELAY              69 /* worst case delay in ms */
+ #define INA2XX_RSHUNT_DEFAULT         10000
++#define INA260_RSHUNT                 2000
+ /* bit mask for reading the averaging setting in the configuration register */
+ #define INA226_AVG_RD_MASK            GENMASK(11, 9)
+@@ -125,11 +127,12 @@ static const struct regmap_config ina2xx_regmap_config = {
+       .writeable_reg = ina2xx_writeable_reg,
+ };
+-enum ina2xx_ids { ina219, ina226 };
++enum ina2xx_ids { ina219, ina226, ina260 };
+ struct ina2xx_config {
+       u16 config_default;
+       bool has_alerts;        /* chip supports alerts and limits */
++      bool has_ishunt;        /* chip has internal shunt resistor */
+       int calibration_value;
+       int shunt_div;
+       int bus_voltage_shift;
+@@ -157,6 +160,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .bus_voltage_lsb = 4000,
+               .power_lsb_factor = 20,
+               .has_alerts = false,
++              .has_ishunt = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+@@ -166,6 +170,16 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
+               .has_alerts = true,
++              .has_ishunt = false,
++      },
++      [ina260] = {
++              .config_default = INA260_CONFIG_DEFAULT,
++              .shunt_div = 400,
++              .bus_voltage_shift = 0,
++              .bus_voltage_lsb = 1250,
++              .power_lsb_factor = 8,
++              .has_alerts = true,
++              .has_ishunt = true,
+       },
+ };
+@@ -257,6 +271,15 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
+       unsigned int regval;
+       int ret, retry;
++      if (data->config->has_ishunt) {
++              /* No calibration needed */
++              ret = regmap_read(regmap, reg, &regval);
++              if (ret < 0)
++                      return ret;
++              *val = ina2xx_get_value(data, reg, regval);
++              return 0;
++      }
++
+       for (retry = 5; retry; retry--) {
+               ret = regmap_read(regmap, reg, &regval);
+               if (ret < 0)
+@@ -686,7 +709,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+-                      if (chip == ina226)
++                      if (chip == ina226 || chip == ina260)
+                               return 0644;
+                       break;
+               default:
+@@ -795,7 +818,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
+       u32 shunt;
+       int ret;
+-      if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0)
++      if (data->config->has_ishunt)
++              shunt = INA260_RSHUNT;
++      else if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0)
+               shunt = INA2XX_RSHUNT_DEFAULT;
+       ret = ina2xx_set_shunt(data, shunt);
+@@ -815,6 +840,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
+                                               FIELD_PREP(INA226_ALERT_POLARITY, active_high));
+       }
++      if (data->config->has_ishunt)
++              return 0;
++
+       /*
+        * Calibration register is set to the best value, which eliminates
+        * truncation errors on calculating current register in hardware.
+@@ -860,7 +888,8 @@ static int ina2xx_probe(struct i2c_client *client)
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+                                                        data, &ina2xx_chip_info,
+-                                                       ina2xx_groups);
++                                                       data->config->has_ishunt ?
++                                                              NULL : ina2xx_groups);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+@@ -876,6 +905,7 @@ static const struct i2c_device_id ina2xx_id[] = {
+       { "ina226", ina226 },
+       { "ina230", ina226 },
+       { "ina231", ina226 },
++      { "ina260", ina260 },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+@@ -901,6 +931,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
+               .compatible = "ti,ina231",
+               .data = (void *)ina226
+       },
++      {
++              .compatible = "ti,ina260",
++              .data = (void *)ina260
++      },
+       { },
+ };
+ MODULE_DEVICE_TABLE(of, ina2xx_of_match);
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch
new file mode 100644 (file)
index 0000000..4e53ca4
--- /dev/null
@@ -0,0 +1,156 @@
+From 226e9dd2665d0eb346202f427f3c2a7e5ae815da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 10 Jun 2026 07:46:16 -0700
+Subject: hwmon: (ina2xx) Fix various overflow issues
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ]
+
+Sashiko reports several integer overflow problems in the ina2xx driver
+caused by unbounded multiplications and inadequate types for intermediate
+calculations.
+
+Specifically:
+- In ina2xx_get_value(), the return type is changed from int to long.
+  Intermediate calculations for current are now performed using 64-bit
+  types to prevent 32-bit integer overflow before the division by 1000.
+- When calculating power in ina2xx_get_value() and
+  sy24655_average_power_read(), interim values are cast to u64 and clamped
+  to LONG_MAX. This prevents overflow when regval or accumulator_24 is
+  multiplied by power_lsb_uW.
+- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t().
+  This safely avoids integer overflows when scaling user-provided values
+  for shunt voltage, bus voltage, power, and current limits.
+
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index d671d546b2054..c504bb43a2ca3 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -31,6 +31,7 @@
+ #include <linux/i2c.h>
+ #include <linux/init.h>
+ #include <linux/kernel.h>
++#include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+@@ -283,30 +284,34 @@ static u16 ina226_interval_to_reg(long interval)
+       return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits);
+ }
+-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+-                          unsigned int regval)
++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg,
++                           unsigned int regval)
+ {
+-      int val;
++      s64 val64;
++      long val;
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+               /* signed register */
+-              val = (s16)regval >> data->config->shunt_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
++              val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift,
++                                      data->config->shunt_div);
+               break;
+       case INA2XX_BUS_VOLTAGE:
+-              val = (regval >> data->config->bus_voltage_shift) *
+-                data->config->bus_voltage_lsb;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) *
++                                      data->config->bus_voltage_lsb, 1000);
+               break;
+       case INA2XX_POWER:
+-              val = regval * data->power_lsb_uW;
++              val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX);
+               break;
+       case INA2XX_CURRENT:
+               /* signed register, result in mA */
+-              val = ((s16)regval >> data->config->current_shift) *
++              val64 = (s64)((s16)regval >> data->config->current_shift) *
+                 data->current_lsb_uA;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              if (val64 < 0)
++                      val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000);
++              else
++                      val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000);
++              val = clamp_val(val64, LONG_MIN, LONG_MAX);
+               break;
+       case INA2XX_CALIBRATION:
+               val = regval;
+@@ -395,27 +400,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
+  */
+ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+ {
++      long limit;
++
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+-              val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
+-              val *= data->config->shunt_div;
+-              val <<= data->config->shunt_voltage_shift;
+-              return clamp_val(val, 0, SHRT_MAX);
++              val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div));
++              return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift,
++                           SHRT_MAX);
+       case INA2XX_BUS_VOLTAGE:
+-              val = clamp_val(val, 0, 200000);
+-              val = (val * 1000) << data->config->bus_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, 130000);
++              return min_t(long,
++                           DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift,
++                                             data->config->bus_voltage_lsb),
++                           USHRT_MAX);
+       case INA2XX_POWER:
+-              val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW);
+-              val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, LONG_MAX - data->power_lsb_uW);
++              return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX);
+       case INA2XX_CURRENT:
+-              val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
++              limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000;
++              val = min_t(long, val, limit);
+               /* signed register, result in mA */
+               val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
+-              val <<= data->config->current_shift;
+-              return clamp_val(val, SHRT_MIN, SHRT_MAX);
++              limit = SHRT_MAX >> data->config->current_shift;
++              return (u16)(min_t(long, val, limit) << data->config->current_shift);
+       default:
+               /* programmer goofed */
+               WARN_ON_ONCE(1);
+@@ -560,6 +567,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+       u8 template[6];
+       int ret;
+       long accumulator_24, sample_count;
++      u64 val64;
+       /* 48-bit register read */
+       ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
+@@ -578,7 +586,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+               return 0;
+       }
+-      *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      *val = min_t(u64, val64, LONG_MAX);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch b/queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch
new file mode 100644 (file)
index 0000000..0a1eca1
--- /dev/null
@@ -0,0 +1,133 @@
+From 9d2d906f9d043d546ebdef2e4ca5fed76482a3ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 13:20:21 +0200
+Subject: hwmon: (ina2xx) Make it easier to add more devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ian Ray <ian.ray@gehealthcare.com>
+
+[ Upstream commit f6e14b5bcabf4ee97a2d535c3c2d7e72c8da4c15 ]
+
+* Make sysfs entries documentation easier to maintain.
+* Use multi-line enum.
+* Correct "has_power_average" comment.
+
+Create a new "has_update_interval" member for chips which support
+averaging.
+
+Signed-off-by: Ian Ray <ian.ray@gehealthcare.com>
+Reviewed-by: Bence Csókás <bence98@sch.bme.hu> # v2
+Tested-by: Jens Almer <bagawk@gmail.com>
+Link: https://lore.kernel.org/r/20260220112024.97446-3-ian.ray@gehealthcare.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 12 ++++++++++--
+ drivers/hwmon/ina2xx.c         | 18 ++++++++++++++----
+ 2 files changed, 24 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index a3860aae444c0..a4ddf4bd2b081 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -124,8 +124,16 @@ power1_input              Power(uW) measurement channel
+ shunt_resistor                Shunt resistance(uOhm) channel (not for ina260)
+ ======================= ===============================================
+-Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
+-------------------------------------------------------------------------
++Additional sysfs entries
++------------------------
++
++Additional entries are available for the following chips:
++
++  * ina226
++  * ina230
++  * ina231
++  * ina260
++  * sy24655
+ ======================= ====================================================
+ curr1_lcrit           Critical low current
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index 345fe7db9de94..97ec0e0218307 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -135,13 +135,19 @@ static const struct regmap_config ina2xx_regmap_config = {
+       .writeable_reg = ina2xx_writeable_reg,
+ };
+-enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
++enum ina2xx_ids {
++      ina219,
++      ina226,
++      ina260,
++      sy24655
++};
+ struct ina2xx_config {
+       u16 config_default;
+       bool has_alerts;        /* chip supports alerts and limits */
+       bool has_ishunt;        /* chip has internal shunt resistor */
+-      bool has_power_average; /* chip has internal shunt resistor */
++      bool has_power_average; /* chip supports average power */
++      bool has_update_interval;
+       int calibration_value;
+       int shunt_div;
+       int bus_voltage_shift;
+@@ -172,6 +178,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = false,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .has_update_interval = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+@@ -183,6 +190,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .has_update_interval = true,
+       },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+@@ -193,6 +201,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = true,
+               .has_power_average = false,
++              .has_update_interval = true,
+       },
+       [sy24655] = {
+               .config_default = SY24655_CONFIG_DEFAULT,
+@@ -204,6 +213,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = true,
++              .has_update_interval = false,
+       },
+ };
+@@ -713,7 +723,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+       const struct ina2xx_data *data = _data;
+       bool has_alerts = data->config->has_alerts;
+       bool has_power_average = data->config->has_power_average;
+-      enum ina2xx_ids chip = data->chip;
++      bool has_update_interval = data->config->has_update_interval;
+       switch (type) {
+       case hwmon_in:
+@@ -775,7 +785,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+-                      if (chip == ina226 || chip == ina260)
++                      if (has_update_interval)
+                               return 0644;
+                       break;
+               default:
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch b/queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch
new file mode 100644 (file)
index 0000000..26e9258
--- /dev/null
@@ -0,0 +1,163 @@
+From 3f968fafb2850c79db10b056447970d401a4b4dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Mar 2026 12:07:02 +0100
+Subject: hwmon: (ina2xx) Shift INA234 shunt and current registers
+
+From: Jonas Rebmann <jre@pengutronix.de>
+
+[ Upstream commit eeca1114d1e2cc0eacaebb80f3f2afbaebfc60be ]
+
+The INA219 has the lowest three bits of the bus voltage register
+zero-reserved, the bus_voltage_shift ina2xx_config field was introduced
+to accommodate for that.
+
+The INA234 has four bits of the bus voltage, of the shunt voltage, and
+of the current registers zero-reserved but the latter two were
+implemented by choosing a 16x higher shunt_div instead of a separate
+field specifying a bit shift.
+
+This is possible because shunt voltage and current are divided by
+shunt_div, hence a 16x higher shunt_div results in a 16x smaller LSB for
+both the shunt voltage and the current register, perfectly accounting
+for the missing bit shift.
+
+For consistency and correctness, account for the reserved bits via
+shunt_voltage_shift and current_shift configuration fields as already
+done for voltage registers and use the conversion constants given in the
+INA234 datasheet.
+
+Signed-off-by: Jonas Rebmann <jre@pengutronix.de>
+Link: https://lore.kernel.org/r/20260303-ina234-shift-v1-2-318c33ac4480@pengutronix.de
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index be6e214fae21f..d671d546b2054 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -151,9 +151,11 @@ struct ina2xx_config {
+       bool has_update_interval;
+       int calibration_value;
+       int shunt_div;
++      int shunt_voltage_shift;
+       int bus_voltage_shift;
+       int bus_voltage_lsb;    /* uV */
+       int power_lsb_factor;
++      int current_shift;
+ };
+ struct ina2xx_data {
+@@ -173,59 +175,69 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .config_default = INA219_CONFIG_DEFAULT,
+               .calibration_value = 4096,
+               .shunt_div = 100,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 3,
+               .bus_voltage_lsb = 4000,
+               .power_lsb_factor = 20,
+               .has_alerts = false,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+               .calibration_value = 2048,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = true,
+       },
+       [ina234] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+               .calibration_value = 2048,
+-              .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .shunt_voltage_shift = 4,
+               .bus_voltage_shift = 4,
+               .bus_voltage_lsb = 25600,
+               .power_lsb_factor = 32,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 4,
+               .has_update_interval = true,
+       },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 8,
+               .has_alerts = true,
+               .has_ishunt = true,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = true,
+       },
+       [sy24655] = {
+               .config_default = SY24655_CONFIG_DEFAULT,
+               .calibration_value = 4096,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = true,
++              .current_shift = 0,
+               .has_update_interval = false,
+       },
+ };
+@@ -279,7 +291,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+               /* signed register */
+-              val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
++              val = (s16)regval >> data->config->shunt_voltage_shift;
++              val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
+               break;
+       case INA2XX_BUS_VOLTAGE:
+               val = (regval >> data->config->bus_voltage_shift) *
+@@ -291,7 +304,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+               break;
+       case INA2XX_CURRENT:
+               /* signed register, result in mA */
+-              val = (s16)regval * data->current_lsb_uA;
++              val = ((s16)regval >> data->config->current_shift) *
++                data->current_lsb_uA;
+               val = DIV_ROUND_CLOSEST(val, 1000);
+               break;
+       case INA2XX_CALIBRATION:
+@@ -385,6 +399,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+       case INA2XX_SHUNT_VOLTAGE:
+               val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
+               val *= data->config->shunt_div;
++              val <<= data->config->shunt_voltage_shift;
+               return clamp_val(val, 0, SHRT_MAX);
+       case INA2XX_BUS_VOLTAGE:
+               val = clamp_val(val, 0, 200000);
+@@ -399,6 +414,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+               val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
+               /* signed register, result in mA */
+               val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
++              val <<= data->config->current_shift;
+               return clamp_val(val, SHRT_MIN, SHRT_MAX);
+       default:
+               /* programmer goofed */
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
new file mode 100644 (file)
index 0000000..6d5450f
--- /dev/null
@@ -0,0 +1,54 @@
+From e9211fde6e941ad0a19805b93ef9f0bf815f82b8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 15:27:28 -0700
+Subject: hwmon: (lm90) Only report alarms if driver is ready
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
+
+Userspace can read sysfs attributes before driver registration is complete,
+immediately after devm_hwmon_device_register_with_info() has been called.
+At that time, data->hwmon_dev is not yet initialized. This can trigger
+a NULL pointer access since lm90_update_device() and with it
+lm90_update_alarms_locked() will be called. This call schedules
+report_work and lm90_report_alarms(), which passes the still-NULL
+data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer
+dereference.
+
+Fix the problem by only scheduling the report and alert workers
+data->hwmon_dev is set.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/lm90.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
+index 511d95a0efb36..45d5a2427dcb1 100644
+--- a/drivers/hwmon/lm90.c
++++ b/drivers/hwmon/lm90.c
+@@ -1149,7 +1149,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+               check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+                       (data->config & 0x80);
+-              if (force || check_enable)
++              if (data->hwmon_dev && (force || check_enable))
+                       schedule_work(&data->report_work);
+               /*
+@@ -1157,7 +1157,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+                * alarms are all clear, and alerts are currently disabled.
+                * Otherwise (re)schedule worker if needed.
+                */
+-              if (check_enable) {
++              if (check_enable && data->hwmon_dev) {
+                       if (!(data->current_alarms & data->alert_alarms)) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               lm90_update_confreg(data, data->config & ~0x80);
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
new file mode 100644 (file)
index 0000000..b44e92a
--- /dev/null
@@ -0,0 +1,39 @@
+From fb8048d2ea8b827805d2fcc4a07e8d7229086ef1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 12:27:15 -0800
+Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ]
+
+Coverity reports an out-of-bounds access when reading the minimum alarm
+voltage for the VGPIO channel. Add the missing return statement to fix
+the problem.
+
+Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282")
+Cc: Nuno Sa <nuno.sa@analog.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ltc4282.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
+index d98c57918ce33..e1c0d02b564b7 100644
+--- a/drivers/hwmon/ltc4282.c
++++ b/drivers/hwmon/ltc4282.c
+@@ -382,8 +382,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
+                                                  channel, val);
+       case hwmon_in_min_alarm:
+               if (channel == LTC4282_CHAN_VGPIO)
+-                      ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
+-                                         LTC4282_GPIO_ALARM_L_MASK, val);
++                      return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
++                                                LTC4282_GPIO_ALARM_L_MASK, val);
+               return ltc4282_vdd_source_read_alm(st,
+                                                  LTC4282_VSOURCE_ALARM_L_MASK,
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch
new file mode 100644 (file)
index 0000000..37a1574
--- /dev/null
@@ -0,0 +1,90 @@
+From 602b41a909121226b0a7dd11597a5c375a4c800c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:14:36 -0700
+Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
+
+Unlike NCT6106, NCT6116 only has three temperature registers, and with
+it only three temperature source and temperature source configuration
+registers. The register addresses match those of NCT6106 and can be
+re-used.
+
+The code used a separate array to list the temperature source registers
+for NCT6116, but used the size of the NCT6106 register array to set
+the number of registers. The NCT6106 register array provides six addresses,
+while the temperature source register array for NCT6116 only provides three
+addresses. This causes a KASAN report.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775]
+Read of size 2 at addr ffffffffc19561a6 by task modprobe/954
+...
+Call Trace:
+ dump_stack+0x7d/0xa7
+ print_address_description.constprop.0+0x1c/0x220
+ ? __kasan_kmalloc.constprop.0+0xc9/0xd0
+ ? __kmalloc_node_track_caller+0x194/0x5b0
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+...
+
+Fix the problem by hard-coding the number of temperature and temperature
+configuration registers to three for NCT6116. Drop the unnecessary
+NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
+
+Reported-by: Florian Bezdeka <florian.bezdeka@siemens.com>
+Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 79bc67ffb9986..506d57025c3bd 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+ static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+ static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+ static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+-      0xb0, 0xb1, 0xb2 };
+ static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+       0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                 = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+               data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+               data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+-              data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
++              data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
+-              num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
++              num_reg_temp = 3;
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
+               num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+-              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
++              num_reg_temp_config = 3;
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..a2309fa
--- /dev/null
@@ -0,0 +1,74 @@
+From 14d8e3a4e9a76e7ee6f06d2cfd4121f5626f4706 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 506d57025c3bd..ce2cf1f229004 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch
new file mode 100644 (file)
index 0000000..6c3ac15
--- /dev/null
@@ -0,0 +1,53 @@
+From 0501320f32578722239a07e67369590ed2c3ff49 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 09:54:23 -0700
+Subject: hwmon: (nzxt-smart2) DMA-align output buffer
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
+
+Sashiko reports:
+
+When send_output_report() calls hid_hw_output_report(), the underlying USB
+HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
+
+When the DMA mapping flushes or invalidates the cacheline, it will corrupt
+the adjacent variables (mutex, update_interval) that were modified
+concurrently by the CPU. This causes memory corruption due to cacheline
+sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA
+API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings
+for this violation.
+
+Any operation that triggers send_output_report() (like setting a fan speed
+or updating the interval) causes the USB DMA mapping. On systems with
+non-coherent caches, this structural bug causes immediate and deterministic
+memory corruption.
+
+Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.")
+Cc: Aleksandr Mezin <mezin.alexander@gmail.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nzxt-smart2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
+index 6f8febda4277c..66c5886f411a6 100644
+--- a/drivers/hwmon/nzxt-smart2.c
++++ b/drivers/hwmon/nzxt-smart2.c
+@@ -203,7 +203,7 @@ struct drvdata {
+        */
+       struct mutex mutex;
+       long update_interval;
+-      u8 output_buffer[OUTPUT_REPORT_SIZE];
++      u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ };
+ static long scale_pwm_value(long val, long orig_max, long new_max)
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..7f471f0
--- /dev/null
@@ -0,0 +1,39 @@
+From 8858dde4f59016376548bf60728a01cfcf232f8b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index a431cce336034..e08e30f288130 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -508,7 +508,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+-- 
+2.53.0
+
diff --git a/queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch
new file mode 100644 (file)
index 0000000..bfd8bee
--- /dev/null
@@ -0,0 +1,76 @@
+From e523910a26393a5ab5d75cf8ebd7e40fd70252aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 09:34:46 -0700
+Subject: hwmon: (sht3x) Fix unaligned accesses
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ]
+
+Sashiko reports:
+
+In sht3x_update_client(), the 16-bit temperature and humidity values are
+extracted from a stack-allocated byte array using be16_to_cpup(). The
+pointers passed to this function are calculated as buf and buf + 3. Since
+the difference between the two pointers is an odd number of bytes, at
+least one of them is guaranteed to be at an unaligned offset.
+
+This will trigger an alignment fault on strict-alignment architectures
+such as ARMv5 or SPARC, resulting in a kernel panic.
+
+Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(),
+and put_unaligned_be16() instead of cpu_to_be16().
+
+Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/sht3x.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
+index 94466e28dc56f..1f63c07cfb085 100644
+--- a/drivers/hwmon/sht3x.c
++++ b/drivers/hwmon/sht3x.c
+@@ -21,6 +21,7 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/jiffies.h>
++#include <linux/unaligned.h>
+ /* commands (high repeatability mode) */
+ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 };
+@@ -279,9 +280,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev)
+               if (ret)
+                       goto out;
+-              val = be16_to_cpup((__be16 *)buf);
++              val = get_unaligned_be16(buf);
+               data->temperature = sht3x_extract_temperature(val);
+-              val = be16_to_cpup((__be16 *)(buf + 3));
++              val = get_unaligned_be16(buf + 3);
+               data->humidity = sht3x_extract_humidity(val);
+               data->last_update = jiffies;
+       }
+@@ -339,7 +340,7 @@ static int limits_update(struct sht3x_data *data)
+               if (ret)
+                       return ret;
+-              raw = be16_to_cpup((__be16 *)buffer);
++              raw = get_unaligned_be16(buffer);
+               temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
+               humidity = sht3x_extract_humidity(raw & 0xfe00);
+               data->temperature_limits[index] = temperature;
+@@ -392,7 +393,7 @@ static size_t limit_write(struct device *dev,
+       raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
+       raw |= ((humidity * 42950) >> 16) & 0xfe00;
+-      *((__be16 *)position) = cpu_to_be16(raw);
++      put_unaligned_be16(raw, position);
+       position += SHT3X_WORD_LEN;
+       *position = crc8(sht3x_crc8_table,
+                        position - SHT3X_WORD_LEN,
+-- 
+2.53.0
+
diff --git a/queue-6.12/idpf-adjust-txq-ring-count-minimum.patch b/queue-6.12/idpf-adjust-txq-ring-count-minimum.patch
new file mode 100644 (file)
index 0000000..f4f4966
--- /dev/null
@@ -0,0 +1,72 @@
+From 414320955368289fd160f8d78e3f62a6c497babc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Jun 2026 16:56:19 -0700
+Subject: idpf: adjust TxQ ring count minimum
+
+From: Joshua Hay <joshua.a.hay@intel.com>
+
+[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ]
+
+Set the TxQ ring count minimum to 128 descriptors. Any lower than this,
+and the queue will stall and trigger Tx timeouts in flow based
+scheduling mode. This is because next_to_clean might never be updated.
+
+In flow based scheduling mode, next_to_clean is only updated after a
+descriptor completion is processed, i.e. after the RE bit is set in the
+last descriptor of a Tx packet. This will never happen with a ring size
+of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value
+of last_re is initialized/set to, the calculated gap will be at most 63
+and never trigger the RE bit.
+
+Even a ring size of 96 does not solve this. Because of how infrequent
+next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED
+will be much smaller on average. This increases the chance the queue
+will be stopped because a multi-descriptor packet, e.g. a large LSO
+packet, does not see enough resources on the ring. In this case, the
+queue will trigger the stop logic. The queue permanently stalls because
+there is no chance for a descriptor completion to update next_to_clean
+since it is dependent on a packet being sent.
+
+Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool")
+Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +----
+ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +-
+ 2 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+index 82a927265b9cf..afb922e8704d0 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+@@ -2507,10 +2507,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
+               tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
+               tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
+-              /* Set the RE bit to periodically "clean" the descriptor ring.
+-               * MIN_GAP is set to MIN_RING size to ensure it will be set at
+-               * least once each time around the ring.
+-               */
++              /* Set the RE bit periodically to "clean" the descriptor ring */
+               if (idpf_tx_splitq_need_re(tx_q)) {
+                       tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE;
+                       tx_q->txq_grp->num_completions_pending++;
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+index a34c791c46088..2d9960dd51ad5 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+@@ -19,7 +19,7 @@
+ /* Mailbox Queue */
+ #define IDPF_MAX_MBXQ                         1
+-#define IDPF_MIN_TXQ_DESC                     64
++#define IDPF_MIN_TXQ_DESC                     128
+ #define IDPF_MIN_RXQ_DESC                     64
+ #define IDPF_MIN_TXQ_COMPLQ_DESC              256
+ #define IDPF_MAX_QIDS                         256
+-- 
+2.53.0
+
diff --git a/queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
new file mode 100644 (file)
index 0000000..32b2d55
--- /dev/null
@@ -0,0 +1,41 @@
+From 44c5cfb9678052340d09a3955baeb004408e7fac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 01:03:32 -0400
+Subject: idpf: Fix mailbox IRQ name leak on request failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ]
+
+idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling
+request_irq(). On success, the name is released later through
+kfree(free_irq()), but request_irq() failure returns without freeing it.
+
+Free the allocated name on the request_irq() failure path.
+
+Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+index 4973135c346a2..c2101f7f6e1bf 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+@@ -134,7 +134,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "IRQ request for mailbox failed, error: %d\n", err);
+-
++              kfree(name);
+               return err;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
new file mode 100644 (file)
index 0000000..78c6e23
--- /dev/null
@@ -0,0 +1,335 @@
+From 8439b7f8a9a0140b8345812c4a27fe9a744e59aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:17 +0300
+Subject: ipvs: do not mangle ICMP replies for non-first fragments
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
+
+Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the
+payload for embedded non-first IPv4 fragments. The problem is
+in the very old inverted pp->dont_defrag check which should not
+continue when embedded is a non-first TCP/UDP/SCTP fragment.
+
+Check for embedded non-first fragment is also missing from
+ip_vs_out_icmp_v6(), it is needed before any connection
+lookups that expect ports after the network headers.
+
+Drop the blocking code from ip_vs_in_icmp_v6() which prevents
+ICMPv6 from local clients to use non-MASQ forwarding.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h             | 11 +++---
+ net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++---------------------
+ net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++----
+ 3 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index a44979db80134..6935ec09af24d 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1627,8 +1627,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, unsigned int toff,
+-                  unsigned int wlen, unsigned int hooknum,
+-                  struct ip_vs_iphdr *ciph);
++                  unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1642,8 +1641,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, unsigned int toff,
+-                     unsigned int wlen, unsigned int hooknum,
+-                     struct ip_vs_iphdr *ciph);
++                     unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1708,12 +1706,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir, unsigned int toff);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff,
++                  bool has_ports);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph);
++                     bool has_ports, struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 784c00ec01ef7..6207a91e93f3b 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout, unsigned int toff)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff,
++                  bool has_ports)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+       struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+-          IPPROTO_SCTP == ciph->protocol) {
++      if (has_ports) {
+               __be16 *ports = (void *)ciph + ciph->ihl*4;
+               if (inout)
+@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int inout, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph)
++                     bool has_ports, struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      int protocol;
+       struct icmp6hdr *icmph;
+       struct ipv6hdr *cih;
+       icmph = (struct icmp6hdr *)(skb->data + toff);
+       cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ciph->protocol;
+-
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+               cih->daddr = cp->vaddr.in6;
+@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!ciph->fragoffs &&
+-          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
+-           protocol == IPPROTO_SCTP)) {
++      if (has_ports) {
+               __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       unsigned int ctoff = ciph->len;
++      bool has_ports = false;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       }
+       if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+-          ciph->protocol == IPPROTO_SCTP)
++          ciph->protocol == IPPROTO_SCTP) {
+               ctoff += 2 * sizeof(__u16);
++              has_ports = true;
++      }
+       if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!pp)
+               return NF_ACCEPT;
++      /* Is the embedded protocol header present? */
++      if (unlikely(ciph.fragoffs && !pp->dont_defrag))
++              return NF_ACCEPT;
++
+       /* The embedded headers contain source and dest in reverse order */
+       cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
+                            ipvs, AF_INET6, skb, &ciph);
+@@ -1687,8 +1688,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       pp = pd->pp;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1696,7 +1696,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       offset2 = offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
+-      offset = ciph.len;
+       /* The embedded headers contain source and dest in reverse order.
+        * For IPIP/UDP/GRE tunnel this is error for request, not for reply.
+@@ -1790,11 +1789,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+-          IPPROTO_SCTP == cih->protocol)
+-              offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
+-                                &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1854,8 +1849,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       pp = pd->pp;
+-      /* Cannot handle fragmented embedded protocol */
+-      if (ciph.fragoffs)
++      /* Is the embedded protocol header present? */
++      if (ciph.fragoffs && !pp->dont_defrag)
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+@@ -1879,13 +1874,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               new_cp = true;
+       }
+-      /* VS/TUN, VS/DR and LOCALNODE just let it go */
+-      if ((hooknum == NF_INET_LOCAL_OUT) &&
+-          (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+-              verdict = NF_ACCEPT;
+-              goto out;
+-      }
+-
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+@@ -1901,14 +1889,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      /* Need to mangle contained IPv6 header in ICMPv6 packet */
+-      offset = ciph.len;
+-      if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+-          IPPROTO_SCTP == ciph.protocol)
+-              offset += 2 * sizeof(__u16); /* Also mangle ports */
+-
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
+-                                   &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 05e6164c45039..b85420a3a14ce 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1500,13 +1500,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+       int local;
+       int rt_mode, was_input;
++      bool has_ports = false;
++      unsigned int wlen;
+       /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
+          forwarded directly here, because there is no need to
+@@ -1562,6 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1569,7 +1577,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1586,10 +1594,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
++      bool has_ports = false;
+       struct rt6_info *rt;    /* Route to the other host */
++      unsigned int wlen;
+       int rc;
+       int local;
+       int rt_mode;
+@@ -1647,6 +1656,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1654,7 +1670,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch
new file mode 100644 (file)
index 0000000..6390487
--- /dev/null
@@ -0,0 +1,624 @@
+From b03fe65a4291e243bb661439174eb0aae4b2756f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:16 +0300
+Subject: ipvs: fix places with wrong packet offsets
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
+
+The offsets we use to packet headers and payloads should be
+based on skb->data. We even already respect non-zero
+network offset in ip_vs_fill_iph_skb() but some places
+do it wrongly and support only zero offset which is expected
+for the IP layer where IPVS has hooks.
+
+Change all places that instead of skb->data use offsets based
+on the network header (skb_network_header, ip_hdr, etc) because
+this doubles the network offset as noted by Sashiko.
+
+For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header
+parsing done by the caller.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   |  15 +--
+ net/netfilter/ipvs/ip_vs_app.c        |   4 +-
+ net/netfilter/ipvs/ip_vs_core.c       | 133 +++++++++++++-------------
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_xmit.c       |  26 ++---
+ 7 files changed, 97 insertions(+), 93 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index aef042039cb00..a44979db80134 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1626,8 +1626,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                  struct ip_vs_protocol *pp, int offset,
+-                  unsigned int hooknum, struct ip_vs_iphdr *iph);
++                  struct ip_vs_protocol *pp, unsigned int toff,
++                  unsigned int wlen, unsigned int hooknum,
++                  struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1640,8 +1641,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                     struct ip_vs_protocol *pp, int offset,
+-                     unsigned int hooknum, struct ip_vs_iphdr *iph);
++                     struct ip_vs_protocol *pp, unsigned int toff,
++                     unsigned int wlen, unsigned int hooknum,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1706,11 +1708,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                     struct ip_vs_conn *cp, int dir);
++                     struct ip_vs_conn *cp, int dir, unsigned int toff,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
+index f9132b359f0c6..0c690a30a85dc 100644
+--- a/net/netfilter/ipvs/ip_vs_app.c
++++ b/net/netfilter/ipvs/ip_vs_app.c
+@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 306a8227d300e..784c00ec01ef7 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+-      unsigned int icmp_offset = iph->ihl*4;
+-      struct icmphdr *icmph    = (struct icmphdr *)(skb_network_header(skb) +
+-                                                    icmp_offset);
++      struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+       struct iphdr *ciph       = (struct iphdr *)(icmph + 1);
++      unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+       if (inout) {
+               iph->saddr = cp->vaddr.ip;
+@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->checksum = 0;
+-      icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
++      icmph->checksum = ip_vs_checksum_complete(skb, toff);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered outgoing ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered outgoing ICMP");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered incoming ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered incoming ICMP");
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                     struct ip_vs_conn *cp, int inout, unsigned int toff,
++                     struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      unsigned int icmp_offset = 0;
+-      unsigned int offs        = 0; /* header offset*/
+       int protocol;
+       struct icmp6hdr *icmph;
+-      struct ipv6hdr *ciph;
+-      unsigned short fragoffs;
++      struct ipv6hdr *cih;
+-      ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+-      icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+-      offs = icmp_offset + sizeof(struct icmp6hdr);
+-      ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
++      icmph = (struct icmp6hdr *)(skb->data + toff);
++      cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
++      protocol = ciph->protocol;
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+-              ciph->daddr = cp->vaddr.in6;
++              cih->daddr = cp->vaddr.in6;
+       } else {
+               iph->daddr = cp->daddr.in6;
+-              ciph->saddr = cp->daddr.in6;
++              cih->saddr = cp->daddr.in6;
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-                        IPPROTO_SCTP == protocol)) {
+-              __be16 *ports = (void *)(skb_network_header(skb) + offs);
++      if (!ciph->fragoffs &&
++          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
++           protocol == IPPROTO_SCTP)) {
++              __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+                             ntohs(inout ? ports[1] : ports[0]),
+@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
+-                                            skb->len - icmp_offset,
++                                            skb->len - toff,
+                                             IPPROTO_ICMPV6, 0);
+-      skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
++      skb->csum_start = skb_headroom(skb) + toff;
+       skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered outgoing ICMPv6");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered incoming ICMPv6");
+ }
+ #endif
+@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+  */
+ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+-                              __u8 protocol, struct ip_vs_conn *cp,
++                              struct ip_vs_conn *cp,
+                               struct ip_vs_protocol *pp,
+-                              unsigned int offset, unsigned int ihl,
+-                              unsigned int hooknum)
++                              struct ip_vs_iphdr *ciph,
++                              unsigned int toff, unsigned int hooknum)
+ {
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
++      unsigned int ctoff = ciph->len;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
++      if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+               goto out;
+       }
+-      if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-          IPPROTO_SCTP == protocol)
+-              offset += 2 * sizeof(__u16);
+-      if (skb_ensure_writable(skb, offset))
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP)
++              ctoff += 2 * sizeof(__u16);
++      if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+  *    Currently handles error types - unreachable, quench, ttl exceeded.
+  */
+ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+-                        int *related, unsigned int hooknum)
++                        int *related, unsigned int hooknum,
++                        struct ip_vs_iphdr *ipvsh)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = ipvsh->len;
++      offset = ipvsh->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &ipvsh->saddr.ip, &ipvsh->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* Now find the contained IP header */
+       offset += sizeof(_icmph);
+       cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+-      if (cih == NULL)
++      if (!(cih && cih->version == 4 && cih->ihl >= 5))
+               return NF_ACCEPT; /* The packet looks wrong, ignore */
+       pp = ip_vs_proto_get(cih->protocol);
+@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!cp)
+               return NF_ACCEPT;
+-      snet.ip = iph->saddr;
+-      return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+-                                  pp, ciph.len, ihl, hooknum);
++      snet.ip = ipvsh->saddr.ip;
++      return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
++                                  hooknum);
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       struct ip_vs_conn *cp;
+       struct ip_vs_protocol *pp;
+       union nf_inet_addr snet;
+-      unsigned int offset;
+       *related = 1;
+       ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
+@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       snet.in6 = ciph.saddr.in6;
+-      offset = ciph.len;
+-      return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+-                                  pp, offset, ipvsh->len, hooknum);
++      return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
++                                  ipvsh->len, hooknum);
+ }
+ #endif
+@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
+ #endif
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+-                      int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
++                      int verdict = ip_vs_out_icmp(ipvs, skb, &related,
++                                                   hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -1576,9 +1572,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
+  */
+ static int
+ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+-            unsigned int hooknum)
++            unsigned int hooknum, struct ip_vs_iphdr *iph)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1588,7 +1583,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       unsigned int offset, offset2, ihl, verdict;
+       bool tunnel, new_cp = false;
+       union nf_inet_addr *raddr;
+-      char *outer_proto = "IPIP";
++      char *outer_proto __maybe_unused = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
+@@ -1598,17 +1593,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = iph->len;
++      offset = iph->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &iph->saddr.ip, &iph->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1725,7 +1722,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+           !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+-                        &iph->saddr);
++                        &iph->saddr.ip);
+               goto out;
+       }
+@@ -1796,7 +1793,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+           IPPROTO_SCTP == cih->protocol)
+               offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
++                                &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1909,7 +1907,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+           IPPROTO_SCTP == ciph.protocol)
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
++                                   &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1988,7 +1987,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+-                                                  hooknum);
++                                                  hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -2124,6 +2123,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+                  const struct nf_hook_state *state)
+ {
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
++      struct ip_vs_iphdr iphdr;
+       int r;
+       /* ipvs enabled in this netns ? */
+@@ -2133,10 +2133,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+       if (state->pf == NFPROTO_IPV4) {
+               if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
+                       return NF_ACCEPT;
++              ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
+ #ifdef CONFIG_IP_VS_IPV6
+       } else {
+-              struct ip_vs_iphdr iphdr;
+-
+               ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
+               if (iphdr.protocol != IPPROTO_ICMPV6)
+@@ -2146,7 +2145,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+ #endif
+       }
+-      return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
++      return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
+ }
+ static const struct nf_hook_ops ip_vs_ops4[] = {
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index f6f732b7dfa86..3dbd3096e1637 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index bf31127338aa0..1ac9c233537d3 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->source = cp->vport;
+       /* Adjust TCP checksums */
+@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index 40d30649b3048..96ac882df15c1 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->source = cp->vport;
+       /*
+@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index fa1b03c3201f1..05e6164c45039 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1499,8 +1499,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+  */
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *iph)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+@@ -1512,7 +1513,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, iph);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1530,7 +1531,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+-                                 NULL, iph);
++                                 NULL, ciph);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
+@@ -1562,13 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1584,8 +1585,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ #ifdef CONFIG_IP_VS_IPV6
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *ipvsh)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rt6_info *rt;    /* Route to the other host */
+       int rc;
+@@ -1597,7 +1599,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, ipvsh);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1614,7 +1616,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+-                                    &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
++                                    &cp->daddr.in6, NULL, ciph, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = dst_rt6_info(skb_dst(skb));
+@@ -1646,13 +1648,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ipvs-fix-the-checksum-validations.patch b/queue-6.12/ipvs-fix-the-checksum-validations.patch
new file mode 100644 (file)
index 0000000..7c03fbf
--- /dev/null
@@ -0,0 +1,389 @@
+From e99237f82626252ac5667bb2361b842346ce77b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:15 +0300
+Subject: ipvs: fix the checksum validations
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
+
+ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6
+packets from clients. In fact, as for TCP/UDP we should
+validate the checksum for ICMP packets only when we
+mangle the packets on MASQ or on reply for tunnel.
+
+Also, Sashiko points out that handle_response_icmp() being
+common for IPv4 and IPv6 is missing the pseudo-header
+calculation while validating ICMPv6 messages from real
+servers which is a problem if checksum is not validated
+by the hardware.
+
+Fix the problems by creating ip_vs_checksum_common_check()
+helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6.
+Rely on the nf_checksum() for validating the ICMP messages
+but use it also for TCP and UDP.
+
+Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
+
+IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum
+validation on LOCAL_OUT (local clients or local real
+servers) and on FORWARD (traffic from servers on LAN).
+Do it only on LOCAL_IN, in case nf_checksum() is not
+called on PRE_ROUTING.
+
+Also, ip_vs_checksum_complete() can be marked static.
+
+Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6")
+Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   | 31 +++++++++++++++--
+ net/netfilter/ipvs/ip_vs_core.c       | 20 +++++++++--
+ net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++----
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  | 44 +++++------------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  | 50 ++++++---------------------
+ 5 files changed, 74 insertions(+), 86 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 08b90d33acdc6..aef042039cb00 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -24,7 +24,9 @@
+ #include <linux/netfilter.h>          /* for union nf_inet_addr */
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>                       /* for struct ipv6hdr */
++#include <net/route.h>
+ #include <net/ipv6.h>
++#include <net/ip6_fib.h>
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ #include <net/netfilter/nf_conntrack.h>
+ #endif
+@@ -1711,8 +1713,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+-
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+ {
+       __be32 diff[2] = { ~old, new };
+@@ -1738,6 +1738,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
+       return csum_partial(diff, sizeof(diff), oldsum);
+ }
++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
++{
++      /* Checksum unnecessary or already validated? */
++      if (skb_csum_unnecessary(skb))
++              return false;
++      /* LOCAL_OUT ? */
++      if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
++              return false;
++      /* !LOCAL_IN (FORWARD) ? */
++      if (af == AF_INET6) {
++              if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
++                      return false;
++      } else {
++              if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
++                      return false;
++      }
++      return true;
++}
++
++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
++                                             int offset, int proto, int af)
++{
++      if (!ip_vs_checksum_needed(skb, af))
++              return true;
++      return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
++}
++
+ /* Forget current conntrack (unconfirmed) and attach notrack entry */
+ static inline void ip_vs_notrack(struct sk_buff *skb)
+ {
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 90619453cb6f0..306a8227d300e 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+ {
+       return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
+ }
+@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               unsigned int offset, unsigned int ihl,
+                               unsigned int hooknum)
+ {
++      int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+@@ -1720,7 +1721,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
++          !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+                         &iph->saddr);
+@@ -1886,6 +1888,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               goto out;
+       }
++      verdict = NF_DROP;
++
++      /* Ensure the checksum is correct */
++      if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
++          !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
++                                       AF_INET6)) {
++              /* Failed checksum! */
++              IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
++                        &iph->saddr);
++              goto out;
++      }
++
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index c67317be17dfa..f6f732b7dfa86 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -11,7 +11,7 @@
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff);
++              struct ip_vs_iphdr *iph);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff)
++              struct ip_vs_iphdr *iph)
+ {
++      unsigned int sctphoff = iph->len;
+       struct sctphdr *sh;
+       __le32 cmp, val;
++      if (!ip_vs_checksum_needed(skb, af))
++              return 1;
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+       if (val != cmp) {
+               /* CRC failure, dump it. */
+-              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                              "Failed checksum for");
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
+               return 0;
+       }
+       return 1;
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index b382810156b2c..bf31127338aa0 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -30,7 +30,7 @@
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff)
++             struct ip_vs_iphdr *iph)
+ {
+-      switch (skb->ip_summed) {
+-      case CHECKSUM_NONE:
+-              skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+-              fallthrough;
+-      case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-              if (af == AF_INET6) {
+-                      if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                          &ipv6_hdr(skb)->daddr,
+-                                          skb->len - tcphoff,
+-                                          IPPROTO_TCP,
+-                                          skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              } else
+-#endif
+-                      if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                            ip_hdr(skb)->daddr,
+-                                            skb->len - tcphoff,
+-                                            ip_hdr(skb)->protocol,
+-                                            skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              break;
+-      default:
+-              /* No need to checksum. */
+-              break;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+-
+       return 1;
+ }
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index dbd4155bb0752..40d30649b3048 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -26,7 +26,7 @@
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff)
++             struct ip_vs_iphdr *iph)
+ {
+       struct udphdr _udph, *uh;
+-      uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
++      uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+-      if (uh->check != 0) {
+-              switch (skb->ip_summed) {
+-              case CHECKSUM_NONE:
+-                      skb->csum = skb_checksum(skb, udphoff,
+-                                               skb->len - udphoff, 0);
+-                      fallthrough;
+-              case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-                      if (af == AF_INET6) {
+-                              if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                                  &ipv6_hdr(skb)->daddr,
+-                                                  skb->len - udphoff,
+-                                                  IPPROTO_UDP,
+-                                                  skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      } else
+-#endif
+-                              if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                                    ip_hdr(skb)->daddr,
+-                                                    skb->len - udphoff,
+-                                                    ip_hdr(skb)->protocol,
+-                                                    skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      break;
+-              default:
+-                      /* No need to checksum. */
+-                      break;
+-              }
++      if (!uh->check)
++              return 1;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+       return 1;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..42b096f
--- /dev/null
@@ -0,0 +1,64 @@
+From 86de0810a334b7f3ddc8ad8ae261367c91ed8242 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index e105349794f23..b9ca9dc9b0c3f 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..20e6a17
--- /dev/null
@@ -0,0 +1,63 @@
+From e0e353acd4b00a4225cce078294db8f65d62bbf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index b9ca9dc9b0c3f..fd95a0eb7a466 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
new file mode 100644 (file)
index 0000000..7bda3e5
--- /dev/null
@@ -0,0 +1,96 @@
+From eb51779d0f9000cee36481b510c16961e1f78c65 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:22:30 +0300
+Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return
+ type
+
+From: Fabrice Derepas <fabrice.derepas@canonical.com>
+
+[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ]
+
+Two correctness and type-hygiene issues exist in the DCP trusted keys
+implementation.
+
+First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob
+without checking if it exceeds MAX_KEY_SIZE.  If a crafted blob provides a
+payload_len larger than 128, the subsequent do_aead_crypto() call writes
+past the end of the p->key array into the adjacent p->blob buffer within
+the same struct trusted_key_payload -- the caller's own input, not
+unrelated kernel memory.  While not exploitable, this violates strict array
+bounds and triggers static analyzers.  Fix this by adding a validation
+check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the
+length, matching the checks already done in trusted_core.c.
+
+Second, calc_blob_len() calculates a sum in size_t that truncates to
+unsigned int on 64-bit platforms.  Because the DCP hardware is only present
+on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally
+equivalent in production, making this truncation harmless in practice.
+Nevertheless, updating the return type to size_t (and subsequently updating
+'blen' in the seal/unseal paths) resolves type-narrowing warnings and
+improves overall code hygiene.
+
+Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys")
+Signed-off-by: Fabrice Derepas <fabrice.derepas@canonical.com>
+Reviewed-by: David Gstir <david@sigma-star.at>
+Reviewed-by: Richard Weinberger <richard@nod.at>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c
+index 7b6eb655df0cb..c078adebe190e 100644
+--- a/security/keys/trusted-keys/trusted_dcp.c
++++ b/security/keys/trusted-keys/trusted_dcp.c
+@@ -69,7 +69,7 @@ static bool skip_zk_test;
+ module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
+ MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
+-static unsigned int calc_blob_len(unsigned int payload_len)
++static size_t calc_blob_len(unsigned int payload_len)
+ {
+       return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
+ }
+@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key)
+ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key;
+       blen = calc_blob_len(p->key_len);
+@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key = NULL;
+       if (b->fmt_version != DCP_BLOB_VERSION) {
+@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+       }
+       p->key_len = le32_to_cpu(b->payload_len);
++      if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) {
++              ret = -EINVAL;
++              goto out;
++      }
++
+       blen = calc_blob_len(p->key_len);
+       if (blen != p->blob_len) {
+-              pr_err("DCP blob has bad length: %i != %i\n", blen,
++              pr_err("DCP blob has bad length: %zu != %u\n", blen,
+                      p->blob_len);
+               ret = -EINVAL;
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
new file mode 100644 (file)
index 0000000..bcbee47
--- /dev/null
@@ -0,0 +1,50 @@
+From a3d326cbb0897e38c4a8e87b4e15fd5bdc1d3ce2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:04:19 +0900
+Subject: ksmbd: fix use-after-free in __close_file_table_ids()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
+
+A ksmbd_file can remain alive after logical close while another session
+holds a temporary reference obtained through ksmbd_lookup_fd_inode().
+ksmbd_close_fd() currently marks the file closed and drops the idr-owned
+reference, but leaves the pointer published in the closing session's idr
+until the final reference is dropped.
+
+If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final()
+supplies the foreign session's file table to __ksmbd_close_fd(). The object
+is then freed without being removed from its owner's idr, and the owner
+session later dereferences the stale pointer during file-table teardown.
+
+Remove the volatile id from the owner's idr while ksmbd_close_fd() still
+holds that table's lock, and clear volatile_id before dropping
+the idr-owned reference. A later foreign final put then only performs
+physical destruction and cannot remove the object from the wrong table.
+
+Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp")
+Reported-by: Yunseong Kim <yunseong.kim@est.tech>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 0d3341927b483..51e37e89d1aa5 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      idr_remove(ft->idr, id);
++                      fp->volatile_id = KSMBD_NO_FID;
+                       closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.12/ksmbd-return-success-for-deferred-final-close.patch
new file mode 100644 (file)
index 0000000..d9ceec7
--- /dev/null
@@ -0,0 +1,64 @@
+From 6b210ed24a4bf09d9f448ca02e135991891c8227 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jun 2026 19:41:08 +0900
+Subject: ksmbd: return success for deferred final close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ]
+
+ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
+reference. If another in-flight request still holds a reference, the final
+close is deferred until that request drops its reference.
+
+The function currently returns -EINVAL in that deferred-final-close case
+because fp is cleared when the reference count does not reach zero.  That
+turns a valid close into STATUS_FILE_CLOSED.
+
+smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
+closes the same directory handle before receiving the find response.
+The query holds a reference while it builds the response, so close must
+mark the handle closed and return success even though final teardown is
+delayed. Track whether the handle was successfully transitioned to
+FP_CLOSED and return success when only the final close is deferred.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 5b6d8bb8edb27..0d3341927b483 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+ {
+       struct ksmbd_file       *fp;
+       struct ksmbd_file_table *ft;
++      bool closed = false;
+       if (!has_file_id(id))
+               return 0;
+@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+               }
+@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+       write_unlock(&ft->lock);
+       if (!fp)
+-              return -EINVAL;
++              return closed ? 0 : -EINVAL;
+       __put_fd_final(work, fp);
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..19358b9
--- /dev/null
@@ -0,0 +1,40 @@
+From 2cc5cfa6ad5193f19e0c8c4976b9835a8571eb1d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index fd2de35ffb3cf..5fd22bb4f5b60 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
new file mode 100644 (file)
index 0000000..d7d3f65
--- /dev/null
@@ -0,0 +1,78 @@
+From dfed353957f7e41f1943bc9434eeaf2aa1a26244 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 07:29:01 +0000
+Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
+
+When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry
+under memory pressure or tree size caps, redirect handlers previously fell
+back to sending un-rate-limited ICMP/NDISC Redirect messages.
+
+In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL.
+In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into
+inet_peer_xrlim_allow(), which returned true when peer == NULL.
+
+Because ICMP/NDISC Redirects are not part of the default global rate limit
+mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates
+an un-rate-limited ICMP packet storm.
+
+Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and
+ndisc_send_redirect() when peer is NULL.
+
+Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c      | 2 --
+ net/ipv6/ip6_output.c | 2 +-
+ net/ipv6/ndisc.c      | 2 ++
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 4dce0de6ab898..2b8c29a29c4ae 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -890,8 +890,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
+       peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
+       if (!peer) {
+               rcu_read_unlock();
+-              icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
+-                        rt_nexthop(rt, ip_hdr(skb)->daddr));
+               return;
+       }
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 50b41be5d38d0..5d17982cb9946 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -627,7 +627,7 @@ int ip6_forward(struct sk_buff *skb)
+               /* Limit redirects both by destination (here)
+                  and by source (inside ndisc_send_redirect)
+                */
+-              if (inet_peer_xrlim_allow(peer, 1*HZ))
++              if (peer && inet_peer_xrlim_allow(peer, 1*HZ))
+                       ndisc_send_redirect(skb, target);
+               rcu_read_unlock();
+       } else {
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index 73e2e457e30be..1a759519a90f9 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1739,6 +1739,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       rcu_read_lock();
+       peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
++      if (!peer)
++              goto release;
+       ret = inet_peer_xrlim_allow(peer, 1*HZ);
+       rcu_read_unlock();
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
new file mode 100644 (file)
index 0000000..89df8fb
--- /dev/null
@@ -0,0 +1,53 @@
+From a4ded6efb318cc70c212d43718073b5d3cb7ad68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:14 +0100
+Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ]
+
+bus->read() returns a negative errno on failure, but
+mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT
+into 0xff92, and returns success. The garbage word is then consumed as
+register data, and read-modify-write cycles write it back to the
+switch. Check both reads and propagate their errors.
+
+The same defect existed in mt7530_mii_read() since the driver was
+introduced and moved into the regmap backend unchanged.
+
+Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530-mdio.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c
+index 51df42ccdbe62..e19b46449ffe6 100644
+--- a/drivers/net/dsa/mt7530-mdio.c
++++ b/drivers/net/dsa/mt7530-mdio.c
+@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
+       if (ret < 0)
+               return ret;
+-      lo = bus->read(bus, priv->mdiodev->addr, r);
+-      hi = bus->read(bus, priv->mdiodev->addr, 0x10);
++      ret = bus->read(bus, priv->mdiodev->addr, r);
++      if (ret < 0)
++              return ret;
++      lo = ret;
++
++      ret = bus->read(bus, priv->mdiodev->addr, 0x10);
++      if (ret < 0)
++              return ret;
++      hi = ret;
+       *val = (hi << 16) | (lo & 0xffff);
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
new file mode 100644 (file)
index 0000000..ec0c902
--- /dev/null
@@ -0,0 +1,191 @@
+From de36809ff405c07fa438f892e09c8073df37d37e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:29 +0100
+Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ]
+
+The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through
+a helper which returns 0 when the underlying read fails, so a failed
+bus transaction clears MT7531_PHY_ACS_ST and the access carries on,
+returning garbage PHY register data to phylib.
+
+Poll using regmap_read_poll_timeout(), which stops on read errors and
+propagates them. These functions hold the MDIO bus lock across the
+whole sequence, so the unlocked regmap accesses remain correct. Remove
+the now-unused _mt7530_unlocked_read().
+
+Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 58 ++++++++++++++--------------------------
+ 1 file changed, 20 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index 21437ce57b64f..b13e905833c3f 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -219,12 +219,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+       mt7530_mutex_unlock(priv);
+ }
+-static u32
+-_mt7530_unlocked_read(struct mt7530_dummy_poll *p)
+-{
+-      return mt7530_mii_read(p->priv, p->reg);
+-}
+-
+ static u32
+ _mt7530_read(struct mt7530_dummy_poll *p)
+ {
+@@ -577,16 +571,13 @@ static int
+ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+                       int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 reg, val;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -596,8 +587,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -607,8 +598,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad);
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -625,16 +616,13 @@ static int
+ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+                        int regnum, u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 val, reg;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -644,8 +632,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -655,8 +643,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | data;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -671,16 +659,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+ static int
+ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 val;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -691,8 +676,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+       mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -709,16 +694,13 @@ static int
+ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+                        u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 reg;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -729,8 +711,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch b/queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch
new file mode 100644 (file)
index 0000000..c03be12
--- /dev/null
@@ -0,0 +1,128 @@
+From 010927541f2e61737e68ea7420488d3936dc16c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Jun 2025 15:21:22 +0200
+Subject: net: ethernet: mtk_eth_soc: add consts for irq index
+
+From: Frank Wunderlich <frank-w@public-files.de>
+
+[ Upstream commit 4981901009923c8889a635a928d55b8f7f8b31ec ]
+
+Use consts instead of fixed integers for accessing IRQ array.
+
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/20250619132125.78368-3-linux@fw-web.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e095f249e220 ("net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 ++++++++++-----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |  7 ++++++-
+ 2 files changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 4305779459efd..c18d4a143aac9 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3215,9 +3215,9 @@ static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth)
+       int i;
+       /* future SoCs beginning with MT7988 should use named IRQs in dts */
+-      eth->irq[1] = platform_get_irq_byname(pdev, "fe1");
+-      eth->irq[2] = platform_get_irq_byname(pdev, "fe2");
+-      if (eth->irq[1] >= 0 && eth->irq[2] >= 0)
++      eth->irq[MTK_FE_IRQ_TX] = platform_get_irq_byname(pdev, "fe1");
++      eth->irq[MTK_FE_IRQ_RX] = platform_get_irq_byname(pdev, "fe2");
++      if (eth->irq[MTK_FE_IRQ_TX] >= 0 && eth->irq[MTK_FE_IRQ_RX] >= 0)
+               return 0;
+       /* legacy way:
+@@ -3226,9 +3226,9 @@ static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth)
+        * On SoCs with non-shared IRQs the first entry is not used,
+        * the second is for TX, and the third is for RX.
+        */
+-      for (i = 0; i < 3; i++) {
++      for (i = 0; i < MTK_FE_IRQ_NUM; i++) {
+               if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
+-                      eth->irq[i] = eth->irq[0];
++                      eth->irq[i] = eth->irq[MTK_FE_IRQ_SHARED];
+               else
+                       eth->irq[i] = platform_get_irq(pdev, i);
+@@ -3294,7 +3294,7 @@ static void mtk_poll_controller(struct net_device *dev)
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+-      mtk_handle_irq_rx(eth->irq[2], dev);
++      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev);
+       mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+ }
+@@ -4787,7 +4787,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+       eth->netdev[id]->features |= eth->soc->hw_features;
+       eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
+-      eth->netdev[id]->irq = eth->irq[0];
++      eth->netdev[id]->irq = eth->irq[MTK_FE_IRQ_SHARED];
+       eth->netdev[id]->dev.of_node = np;
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+@@ -5064,17 +5064,17 @@ static int mtk_probe(struct platform_device *pdev)
+       }
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) {
+-              err = devm_request_irq(eth->dev, eth->irq[0],
++              err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_SHARED],
+                                      mtk_handle_irq, 0,
+                                      dev_name(eth->dev), eth);
+       } else {
+-              err = devm_request_irq(eth->dev, eth->irq[1],
++              err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_TX],
+                                      mtk_handle_irq_tx, 0,
+                                      dev_name(eth->dev), eth);
+               if (err)
+                       goto err_free_dev;
+-              err = devm_request_irq(eth->dev, eth->irq[2],
++              err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_RX],
+                                      mtk_handle_irq_rx, 0,
+                                      dev_name(eth->dev), eth);
+       }
+@@ -5120,7 +5120,7 @@ static int mtk_probe(struct platform_device *pdev)
+               } else
+                       netif_info(eth, probe, eth->netdev[i],
+                                  "mediatek frame engine at 0x%08lx, irq %d\n",
+-                                 eth->netdev[i]->base_addr, eth->irq[0]);
++                                 eth->netdev[i]->base_addr, eth->irq[MTK_FE_IRQ_SHARED]);
+       }
+       /* we run 2 devices on the same DMA ring so we need a dummy device
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 0570623e569d5..76d60b6a23b2b 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -611,6 +611,11 @@
+ #define MTK_MAC_FSM(x)                (0x1010C + ((x) * 0x100))
++#define MTK_FE_IRQ_SHARED     0
++#define MTK_FE_IRQ_TX         1
++#define MTK_FE_IRQ_RX         2
++#define MTK_FE_IRQ_NUM                (MTK_FE_IRQ_RX + 1)
++
+ struct mtk_rx_dma {
+       unsigned int rxd1;
+       unsigned int rxd2;
+@@ -1248,7 +1253,7 @@ struct mtk_eth {
+       struct net_device               *dummy_dev;
+       struct net_device               *netdev[MTK_MAX_DEVS];
+       struct mtk_mac                  *mac[MTK_MAX_DEVS];
+-      int                             irq[3];
++      int                             irq[MTK_FE_IRQ_NUM];
+       u32                             msg_enable;
+       unsigned long                   sysclk;
+       struct regmap                   *ethsys;
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
new file mode 100644 (file)
index 0000000..d5990d9
--- /dev/null
@@ -0,0 +1,40 @@
+From 1281cc3f7c0ce7c5d2a641d26aed314d3e8abd6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 13:57:35 +0800
+Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in
+ poll_controller
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ]
+
+mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq
+cookie), but mtk_poll_controller incorrectly passed the net_device *.
+Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled
+would then crash.
+
+Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index c18d4a143aac9..0d3346baa84e7 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3294,7 +3294,7 @@ static void mtk_poll_controller(struct net_device *dev)
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+-      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev);
++      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth);
+       mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch b/queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch
new file mode 100644 (file)
index 0000000..5311526
--- /dev/null
@@ -0,0 +1,92 @@
+From 94ae289b86b49c92289d214af6f13945c9573071 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Jun 2025 15:21:21 +0200
+Subject: net: ethernet: mtk_eth_soc: support named IRQs
+
+From: Frank Wunderlich <frank-w@public-files.de>
+
+[ Upstream commit ee85b483fefbbebe1e6e61c53169f864bbdd8f13 ]
+
+Add named interrupts and keep index based fallback for existing
+devicetrees.
+
+Currently only rx and tx IRQs are defined to be used with mt7988, but
+later extended with RSS/LRO support.
+
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20250619132125.78368-2-linux@fw-web.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e095f249e220 ("net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 46 ++++++++++++++++-----
+ 1 file changed, 35 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index ebf5432cb328d..4305779459efd 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3210,6 +3210,37 @@ static void mtk_tx_timeout(struct net_device *dev, unsigned int txqueue)
+       schedule_work(&eth->pending_work);
+ }
++static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth)
++{
++      int i;
++
++      /* future SoCs beginning with MT7988 should use named IRQs in dts */
++      eth->irq[1] = platform_get_irq_byname(pdev, "fe1");
++      eth->irq[2] = platform_get_irq_byname(pdev, "fe2");
++      if (eth->irq[1] >= 0 && eth->irq[2] >= 0)
++              return 0;
++
++      /* legacy way:
++       * On MTK_SHARED_INT SoCs (MT7621 + MT7628) the first IRQ is taken
++       * from devicetree and used for both RX and TX - it is shared.
++       * On SoCs with non-shared IRQs the first entry is not used,
++       * the second is for TX, and the third is for RX.
++       */
++      for (i = 0; i < 3; i++) {
++              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
++                      eth->irq[i] = eth->irq[0];
++              else
++                      eth->irq[i] = platform_get_irq(pdev, i);
++
++              if (eth->irq[i] < 0) {
++                      dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
++                      return -ENXIO;
++              }
++      }
++
++      return 0;
++}
++
+ static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
+ {
+       struct mtk_eth *eth = _eth;
+@@ -4986,17 +5017,10 @@ static int mtk_probe(struct platform_device *pdev)
+               }
+       }
+-      for (i = 0; i < 3; i++) {
+-              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
+-                      eth->irq[i] = eth->irq[0];
+-              else
+-                      eth->irq[i] = platform_get_irq(pdev, i);
+-              if (eth->irq[i] < 0) {
+-                      dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
+-                      err = -ENXIO;
+-                      goto err_wed_exit;
+-              }
+-      }
++      err = mtk_get_irqs(pdev, eth);
++      if (err)
++              goto err_wed_exit;
++
+       for (i = 0; i < ARRAY_SIZE(eth->clks); i++) {
+               eth->clks[i] = devm_clk_get(eth->dev,
+                                           mtk_clks_source_name[i]);
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
new file mode 100644 (file)
index 0000000..584c6dd
--- /dev/null
@@ -0,0 +1,48 @@
+From 8fd1171e50f3aa1c766a4550bc84c8900d40ef16 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:46:57 +0800
+Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets
+
+From: Jiawen Wu <jiawenwu@trustnetic.com>
+
+[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ]
+
+When TX VLAN hardware offload is disabled, VLAN tags are embedded in
+the packet payload (software VLAN). Previously, the driver failed to
+set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission.
+
+This missing flag caused the txgbe FDIR ATR logic to fall through to the
+default hash calculation path. This resulted in asymmetric hash values
+for Tx and Rx flows, preventing return packets from being steered to the
+same queue as the transmit packets.
+
+Fix this by detecting software VLANs via eth_type_vlan(skb->protocol)
+and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects
+the correct hashing algorithm to maintain Tx/Rx queue symmetry.
+
+Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support")
+Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+index 4c203f4afd689..3666302e3401b 100644
+--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+@@ -1481,6 +1481,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
+       if (skb_vlan_tag_present(skb)) {
+               tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT;
+               tx_flags |= WX_TX_FLAGS_HW_VLAN;
++      } else if (eth_type_vlan(skb->protocol)) {
++              tx_flags |= WX_TX_FLAGS_SW_VLAN;
+       }
+       /* record initial flags and protocol */
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..a2fc862
--- /dev/null
@@ -0,0 +1,92 @@
+From a98569dec6e8514076ea3bfb6a6d262091d3ef4a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index b78dfcbec936c..e6f3963739ad6 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1728,8 +1728,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->using_mac_select_pcs = using_mac_select_pcs;
+@@ -1753,28 +1753,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->cur_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..7c5be1a
--- /dev/null
@@ -0,0 +1,48 @@
+From 1be983093a4dbc670959463006db37ce11d773ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index a4f457fa570d9..4afa2b2c67b47 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1079,7 +1079,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1188,6 +1190,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..20c5ebc
--- /dev/null
@@ -0,0 +1,50 @@
+From 37c5d45f45d538c6e8e7549d527c7c40cc383f2b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index d2ba283f6123f..a4f457fa570d9 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -598,14 +598,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
new file mode 100644 (file)
index 0000000..dd544d4
--- /dev/null
@@ -0,0 +1,150 @@
+From 5c0b63ef8637f1a69c80dfc5285ab874c146d1fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 09:11:37 +0000
+Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ]
+
+syzbot reported a memory leak [1] in the UDP tunnel NIC offload code.
+
+When device registration fails (e.g. in register_netdevice()), netdev core
+unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued
+during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister()
+returns early:
+
+       if (utn->work_pending)
+               return;
+
+Because failed registrations do not enter netdev_wait_allrefs_any(), no
+subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the
+struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked
+permanently.
+
+Fix this by removing the early return. Instead, synchronously cancel any
+pending work with cancel_delayed_work_sync() before freeing @utn.
+
+To be able to call cancel_delayed_work_sync() while holding RTNL (the work also
+needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL
+is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work())
+to prevent high CPU contention while waiting for RTNL lock.
+
+The utn->work_pending bookkeeping is no longer needed and is removed, as
+the workqueue core already tracks the pending/running state of the work.
+
+[1]
+BUG: memory leak
+unreferenced object 0xffff888127d5f840 (size 96):
+  comm "syz-executor", pid 5806, jiffies 4294942188
+  backtrace (crc 99fdb6c8):
+    __kmalloc_noprof+0x3bf/0x550
+    udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline]
+    udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline]
+    udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931
+    notifier_call_chain+0x59/0x160 kernel/notifier.c:85
+    call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250
+    register_netdevice+0xc10/0xeb0 net/core/dev.c:11478
+
+Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure")
+Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++---------------
+ 1 file changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
+index b13e8f7092f46..4360d159ee8a7 100644
+--- a/net/ipv4/udp_tunnel_nic.c
++++ b/net/ipv4/udp_tunnel_nic.c
+@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry {
+  * @lock:     protects all fields
+  * @need_sync:        at least one port start changed
+  * @need_replay: space was freed, we need a replay of all ports
+- * @work_pending: @work is currently scheduled
+  * @n_tables: number of tables under @entries
+  * @missed:   bitmap of tables which overflown
+  * @entries:  table of tables of ports currently offloaded
+  */
+ struct udp_tunnel_nic {
+-      struct work_struct work;
++      struct delayed_work work;
+       struct net_device *dev;
+@@ -46,7 +45,6 @@ struct udp_tunnel_nic {
+       u8 need_sync:1;
+       u8 need_replay:1;
+-      u8 work_pending:1;
+       unsigned int n_tables;
+       unsigned long missed;
+@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void
+ udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ {
+-      if (!utn->need_sync || utn->work_pending)
++      if (!utn->need_sync)
+               return;
+-      queue_work(udp_tunnel_nic_workqueue, &utn->work);
+-      utn->work_pending = 1;
++      queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0);
+ }
+ static bool
+@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void udp_tunnel_nic_device_sync_work(struct work_struct *work)
+ {
+       struct udp_tunnel_nic *utn =
+-              container_of(work, struct udp_tunnel_nic, work);
++              container_of(work, struct udp_tunnel_nic, work.work);
+-      rtnl_lock();
++      /* We cannot block on RTNL here, otherwise we would deadlock with
++       * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync()
++       * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended.
++       */
++      if (!rtnl_trylock()) {
++              queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1);
++              return;
++      }
+       mutex_lock(&utn->lock);
+-
+-      utn->work_pending = 0;
+       __udp_tunnel_nic_device_sync(utn->dev, utn);
+       if (utn->need_replay)
+@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info,
+       if (!utn)
+               return NULL;
+       utn->n_tables = n_tables;
+-      INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
++      INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
+       mutex_init(&utn->lock);
+       for (i = 0; i < n_tables; i++) {
+@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
+       udp_tunnel_nic_flush(dev, utn);
+       udp_tunnel_nic_unlock(dev);
+-      /* Wait for the work to be done using the state, netdev core will
+-       * retry unregister until we give up our reference on this device.
++      /* Make sure no work is running or queued before freeing @utn.
++       * The work handler uses rtnl_trylock(), so it will not deadlock
++       * against the RTNL we are holding here.
+        */
+-      if (utn->work_pending)
+-              return;
++      cancel_delayed_work_sync(&utn->work);
+       udp_tunnel_nic_free(utn);
+ release_dev:
+-- 
+2.53.0
+
diff --git a/queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..3816e08
--- /dev/null
@@ -0,0 +1,96 @@
+From 55bff37447197c397f6b4157deb88aed732e3cfc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index dbc614dfe0d56..aafa0c04f917e 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 852c0b74b8a77..4f975b83c84f6 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index f7be30c69b5c8..a1c41defaf22d 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
new file mode 100644 (file)
index 0000000..9e81d49
--- /dev/null
@@ -0,0 +1,205 @@
+From d4f35e73fd5e45276ef1d5957e83359fd582b7b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 10:13:37 +0200
+Subject: netfilter: nf_tables: make nft_object rhltable per table
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ]
+
+The nft_object rhltable is global, this allows for accessing objects
+that are being dismangled from lookup path by other existing netns.
+Given the nft_obj_destroy() releases the object inmediately, this might
+lead to use-after-free of these objects that are being released.
+Make the existing rhltable per table to address this issue to deal with
+with the nft_rcv_nl_event() path too.
+
+Update nft_obj_lookup() to take the table as non-const, otherwise,
+compiler complains when passing the objname_ht to rhltable_lookup().
+
+Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable")
+Suggested-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h |  4 +++-
+ net/netfilter/nf_tables_api.c     | 34 +++++++++++++++----------------
+ 2 files changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 032b649658be9..f4b59915a49c5 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1274,6 +1274,7 @@ static inline void nft_use_inc_restore(u32 *use)
+  *    @sets: sets in the table
+  *    @objects: stateful objects in the table
+  *    @flowtables: flow tables in the table
++ *    @objname_ht: hashtable for objects lookup by name
+  *    @hgenerator: handle generator state
+  *    @handle: table handle
+  *    @use: number of chain references to this table
+@@ -1293,6 +1294,7 @@ struct nft_table {
+       struct list_head                sets;
+       struct list_head                objects;
+       struct list_head                flowtables;
++      struct rhltable                 objname_ht;
+       u64                             hgenerator;
+       u64                             handle;
+       u32                             use;
+@@ -1380,7 +1382,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)
+ #define nft_expr_obj(expr)    *((struct nft_object **)nft_expr_priv(expr))
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask);
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 4cf565686a51f..03dd9d1db12fc 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -41,8 +41,6 @@ enum {
+       NFT_VALIDATE_DO,
+ };
+-static struct rhltable nft_objname_ht;
+-
+ static u32 nft_chain_hash(const void *data, u32 len, u32 seed);
+ static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed);
+ static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *);
+@@ -1457,6 +1455,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err)
+               goto err_chain_ht;
++      err = rhltable_init(&table->objname_ht, &nft_objname_ht_params);
++      if (err < 0)
++              goto err_obj_ht;
++
+       INIT_LIST_HEAD(&table->chains);
+       INIT_LIST_HEAD(&table->sets);
+       INIT_LIST_HEAD(&table->objects);
+@@ -1475,6 +1477,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       list_add_tail_rcu(&table->list, &nft_net->tables);
+       return 0;
+ err_trans:
++      rhltable_destroy(&table->objname_ht);
++err_obj_ht:
+       rhltable_destroy(&table->chains_ht);
+ err_chain_ht:
+       kfree(table->udata);
+@@ -1641,6 +1645,7 @@ static void nf_tables_table_destroy(struct nft_table *table)
+               return;
+       rhltable_destroy(&table->chains_ht);
++      rhltable_destroy(&table->objname_ht);
+       kfree(table->name);
+       kfree(table->udata);
+       kfree(table);
+@@ -7578,7 +7583,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type)
+ EXPORT_SYMBOL_GPL(nft_unregister_obj);
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask)
+ {
+@@ -7594,7 +7599,7 @@ struct nft_object *nft_obj_lookup(const struct net *net,
+                    !lockdep_commit_lock_is_held(net));
+       rcu_read_lock();
+-      list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params);
++      list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params);
+       if (!list)
+               goto out;
+@@ -7873,7 +7878,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err < 0)
+               goto err_trans;
+-      err = rhltable_insert(&nft_objname_ht, &obj->rhlhead,
++      err = rhltable_insert(&table->objname_ht, &obj->rhlhead,
+                             nft_objname_ht_params);
+       if (err < 0)
+               goto err_obj_ht;
+@@ -8052,8 +8057,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       u8 family = info->nfmsg->nfgen_family;
+-      const struct nft_table *table;
+       struct net *net = info->net;
++      struct nft_table *table;
+       struct nft_object *obj;
+       struct sk_buff *skb2;
+       u32 objtype;
+@@ -9782,9 +9787,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
+               nf_tables_commit_chain_free_rules_old(g0);
+ }
+-static void nft_obj_del(struct nft_object *obj)
++static void nft_obj_del(struct nft_table *table, struct nft_object *obj)
+ {
+-      rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params);
++      rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);
+       list_del_rcu(&obj->list);
+ }
+@@ -10480,7 +10485,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
+                       break;
+               case NFT_MSG_DELOBJ:
+               case NFT_MSG_DESTROYOBJ:
+-                      nft_obj_del(nft_trans_obj(trans));
++                      nft_obj_del(table, nft_trans_obj(trans));
+                       nf_tables_obj_notify(&ctx, nft_trans_obj(trans),
+                                            trans->msg_type);
+                       break;
+@@ -10780,7 +10785,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
+                               nft_trans_destroy(trans);
+                       } else {
+                               nft_use_dec_restore(&table->use);
+-                              nft_obj_del(nft_trans_obj(trans));
++                              nft_obj_del(table, nft_trans_obj(trans));
+                       }
+                       break;
+               case NFT_MSG_DELOBJ:
+@@ -11447,7 +11452,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
+               nft_set_destroy(&ctx, set);
+       }
+       list_for_each_entry_safe(obj, ne, &table->objects, list) {
+-              nft_obj_del(obj);
++              nft_obj_del(table, obj);
+               nft_use_dec(&table->use);
+               nft_obj_destroy(&ctx, obj);
+       }
+@@ -11629,10 +11634,6 @@ static int __init nf_tables_module_init(void)
+       if (err < 0)
+               goto err_netdev_notifier;
+-      err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params);
+-      if (err < 0)
+-              goto err_rht_objname;
+-
+       err = nft_offload_init();
+       if (err < 0)
+               goto err_offload;
+@@ -11655,8 +11656,6 @@ static int __init nf_tables_module_init(void)
+ err_netlink_notifier:
+       nft_offload_exit();
+ err_offload:
+-      rhltable_destroy(&nft_objname_ht);
+-err_rht_objname:
+       unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
+ err_netdev_notifier:
+       nf_tables_core_module_exit();
+@@ -11678,7 +11677,6 @@ static void __exit nf_tables_module_exit(void)
+       unregister_pernet_subsys(&nf_tables_net_ops);
+       cancel_work_sync(&trans_gc_work);
+       rcu_barrier();
+-      rhltable_destroy(&nft_objname_ht);
+       nf_tables_core_module_exit();
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..0d7553c
--- /dev/null
@@ -0,0 +1,69 @@
+From 69e893dcc766e8eed72794bee079601f3a9f913c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index 36c31ad2d64c1..15feb49e0b663 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -253,9 +253,7 @@ static int nft_payload_dump(struct sk_buff *skb,
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -264,15 +262,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..7a91747
--- /dev/null
@@ -0,0 +1,83 @@
+From 760266bf78d62908739895c61356665a0b35ce8a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 0859b8f767645..61813010cd319 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               vfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..ea28691
--- /dev/null
@@ -0,0 +1,42 @@
+From ef058dddb0af9d5d84b4d35cd72c761b3669e250 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index c314139e0d781..d03399585d01d 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -838,8 +838,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch b/queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch
new file mode 100644 (file)
index 0000000..c82a46d
--- /dev/null
@@ -0,0 +1,172 @@
+From 1db32f155c82eedaa9404e509fb07e9ec05eaa96 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 8 Oct 2024 15:06:24 -0700
+Subject: of: reserved_mem: Add code to dynamically allocate reserved_mem array
+
+From: Oreoluwa Babatunde <quic_obabatun@quicinc.com>
+
+[ Upstream commit 00c9a452a235c61f099504783badd9a7675ff5a5 ]
+
+The reserved_mem array is statically allocated with a size of
+MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem
+regions exceeds this size, there will not be enough space to store
+all the data.
+
+Hence, extend the use of the static array by introducing a
+dynamically allocated array based on the number of reserved memory
+regions specified in the DT.
+
+On architectures such as arm64, memblock allocated memory is not
+writable until after the page tables have been setup. Hence, the
+dynamic allocation of the reserved_mem array will need to be done only
+after the page tables have been setup.
+
+As a result, a temporary static array is still needed in the initial
+stages to store the information of the dynamically-placed reserved
+memory regions because the start address is selected only at run-time
+and is not stored anywhere else.
+It is not possible to wait until the reserved_mem array is allocated
+because this is done after the page tables are setup and the reserved
+memory regions need to be initialized before then.
+
+After the reserved_mem array is allocated, all entries from the static
+array is copied over to the new array, and the rest of the information
+for the statically-placed reserved memory regions are read in from the
+DT and stored in the new array as well.
+
+Once the init process is completed, the temporary static array is
+released back to the system because it is no longer needed. This is
+achieved by marking it as __initdata.
+
+Signed-off-by: Oreoluwa Babatunde <quic_obabatun@quicinc.com>
+Link: https://lore.kernel.org/r/20241008220624.551309-3-quic_obabatun@quicinc.com
+Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
+Stable-dep-of: db3dbdfea1b8 ("of: reserved_mem: prevent OOB when too many dynamic regions are defined")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/of/of_reserved_mem.c | 63 +++++++++++++++++++++++++++++++++---
+ 1 file changed, 59 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+index 7b5d6562fe4a0..02d3c3284f886 100644
+--- a/drivers/of/of_reserved_mem.c
++++ b/drivers/of/of_reserved_mem.c
+@@ -28,7 +28,9 @@
+ #include "of_private.h"
+-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
++static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata;
++static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
++static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+ static int reserved_mem_count;
+ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
+@@ -57,6 +59,50 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
+       return err;
+ }
++/*
++ * alloc_reserved_mem_array() - allocate memory for the reserved_mem
++ * array using memblock
++ *
++ * This function is used to allocate memory for the reserved_mem
++ * array according to the total number of reserved memory regions
++ * defined in the DT.
++ * After the new array is allocated, the information stored in
++ * the initial static array is copied over to this new array and
++ * the new array is used from this point on.
++ */
++static void __init alloc_reserved_mem_array(void)
++{
++      struct reserved_mem *new_array;
++      size_t alloc_size, copy_size, memset_size;
++
++      alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
++      if (alloc_size == SIZE_MAX) {
++              pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW);
++              return;
++      }
++
++      new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
++      if (!new_array) {
++              pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM);
++              return;
++      }
++
++      copy_size = array_size(reserved_mem_count, sizeof(*new_array));
++      if (copy_size == SIZE_MAX) {
++              memblock_free(new_array, alloc_size);
++              total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
++              pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW);
++              return;
++      }
++
++      memset_size = alloc_size - copy_size;
++
++      memcpy(new_array, reserved_mem, copy_size);
++      memset(new_array + reserved_mem_count, 0, memset_size);
++
++      reserved_mem = new_array;
++}
++
+ static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem);
+ /*
+  * fdt_reserved_mem_save_node() - save fdt node for second pass initialization
+@@ -66,7 +112,7 @@ static void __init fdt_reserved_mem_save_node(unsigned long node, const char *un
+ {
+       struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
+-      if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
++      if (reserved_mem_count == total_reserved_mem_cnt) {
+               pr_err("not enough space for all defined regions.\n");
+               return;
+       }
+@@ -199,6 +245,9 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
+               return;
+       }
++      /* Attempt dynamic allocation of a new reserved_mem array */
++      alloc_reserved_mem_array();
++
+       if (__reserved_mem_check_root(node)) {
+               pr_err("Reserved memory: unsupported node format, ignoring\n");
+               return;
+@@ -243,7 +292,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
+ int __init fdt_scan_reserved_mem(void)
+ {
+       int node, child;
+-      int dynamic_nodes_cnt = 0;
++      int dynamic_nodes_cnt = 0, count = 0;
+       int dynamic_nodes[MAX_RESERVED_REGIONS];
+       const void *fdt = initial_boot_params;
+@@ -266,6 +315,8 @@ int __init fdt_scan_reserved_mem(void)
+               uname = fdt_get_name(fdt, child, NULL);
+               err = __reserved_mem_reserve_reg(child, uname);
++              if (!err)
++                      count++;
+               /*
+                * Save the nodes for the dynamically-placed regions
+                * into an array which will be used for allocation right
+@@ -280,11 +331,15 @@ int __init fdt_scan_reserved_mem(void)
+       }
+       for (int i = 0; i < dynamic_nodes_cnt; i++) {
+               const char *uname;
++              int err;
+               child = dynamic_nodes[i];
+               uname = fdt_get_name(fdt, child, NULL);
+-              __reserved_mem_alloc_size(child, uname);
++              err = __reserved_mem_alloc_size(child, uname);
++              if (!err)
++                      count++;
+       }
++      total_reserved_mem_cnt = count;
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
new file mode 100644 (file)
index 0000000..d3d6989
--- /dev/null
@@ -0,0 +1,66 @@
+From 976f97a7ca25112f6377d28751d9d73d23f2d3e8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 14 Jun 2026 22:38:06 +0900
+Subject: of: reserved_mem: prevent OOB when too many dynamic regions are
+ defined
+
+From: Sang-Heon Jeon <ekffu200098@gmail.com>
+
+[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ]
+
+On boot, fdt_scan_reserved_mem() saves each dynamically-placed
+/reserved-memory subnode into a local array of size
+MAX_RESERVED_REGIONS.
+
+If the device tree defines more than MAX_RESERVED_REGIONS
+dynamically-placed regions, fdt_scan_reserved_mem() writes past the
+end of the local array.
+
+Add a bounds check that logs an error and skips the excess regions,
+restoring the original behavior.
+
+Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed")
+Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
+Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com
+Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/of/of_reserved_mem.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+index 02d3c3284f886..9f4f0306b834f 100644
+--- a/drivers/of/of_reserved_mem.c
++++ b/drivers/of/of_reserved_mem.c
+@@ -317,6 +317,7 @@ int __init fdt_scan_reserved_mem(void)
+               err = __reserved_mem_reserve_reg(child, uname);
+               if (!err)
+                       count++;
++
+               /*
+                * Save the nodes for the dynamically-placed regions
+                * into an array which will be used for allocation right
+@@ -324,10 +325,17 @@ int __init fdt_scan_reserved_mem(void)
+                * or marked as no-map. This is done to avoid dynamically
+                * allocating from one of the statically-placed regions.
+                */
+-              if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
+-                      dynamic_nodes[dynamic_nodes_cnt] = child;
+-                      dynamic_nodes_cnt++;
++              if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL))
++                      continue;
++
++              if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) {
++                      pr_err("too many defined dynamic regions, skip '%s'\n",
++                             uname);
++                      continue;
+               }
++
++              dynamic_nodes[dynamic_nodes_cnt] = child;
++              dynamic_nodes_cnt++;
+       }
+       for (int i = 0; i < dynamic_nodes_cnt; i++) {
+               const char *uname;
+-- 
+2.53.0
+
diff --git a/queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..afe4fb6
--- /dev/null
@@ -0,0 +1,76 @@
+From 0f9e0e34ef1da4fe32c09ac9f068d0b34b8ce4b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 6fcbdff760b32..5039b0e89cba4 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..2f62e86
--- /dev/null
@@ -0,0 +1,56 @@
+From 612585a8e6c42e5d9f56c9afc5e5a6696dc78811 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 5039b0e89cba4..1ec5722ac427c 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
new file mode 100644 (file)
index 0000000..d6ac9b0
--- /dev/null
@@ -0,0 +1,172 @@
+From 198948f768a687cbbf9fed22a3327a9a19e48a55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Apr 2025 08:35:47 +0200
+Subject: phy-zynqmp: Postpone getting clock rate until actually needed
+
+From: Mike Looijmans <mike.looijmans@topic.nl>
+
+[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
+
+At probe time the driver would display the following error and abort:
+  xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
+
+At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't
+initialized the clock yet, so clk_get_rate() likely returns 0 if the clock
+is programmable. So this driver only works if the clock is fixed.
+
+The PHY driver doesn't need to know the clock frequency at probe yet, so
+wait until the associated driver initializes the lane before requesting the
+clock rate setting.
+
+In addition to allowing the driver to be used with programmable clocks,
+this also reduces the driver's runtime memory footprint by removing an
+array of pointers from struct xpsgtr_phy.
+
+Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
+Acked-by: Michal Simek <michal.simek@amd.com>
+Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++----------------
+ 1 file changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index e6579002f1146..6fcbdff760b32 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -222,7 +222,6 @@ struct xpsgtr_phy {
+  * @siou: siou base address
+  * @gtr_mutex: mutex for locking
+  * @phys: PHY lanes
+- * @refclk_sscs: spread spectrum settings for the reference clocks
+  * @clk: reference clocks
+  * @tx_term_fix: fix for GT issue
+  * @saved_icm_cfg0: stored value of ICM CFG0 register
+@@ -235,7 +234,6 @@ struct xpsgtr_dev {
+       void __iomem *siou;
+       struct mutex gtr_mutex; /* mutex for locking */
+       struct xpsgtr_phy phys[NUM_LANES];
+-      const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
+       struct clk *clk[NUM_LANES];
+       bool tx_term_fix;
+       unsigned int saved_icm_cfg0;
+@@ -398,13 +396,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
+       return ret;
+ }
++/* Get the spread spectrum (SSC) settings for the reference clock rate */
++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy)
++{
++      unsigned long rate;
++      struct clk *clk;
++      unsigned int i;
++
++      clk = gtr_phy->dev->clk[gtr_phy->refclk];
++      rate = clk_get_rate(clk);
++
++      for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
++              /* Allow an error of 100 ppm */
++              unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++              if (abs(rate - ssc_lookup[i].refclk_rate) < error)
++                      return &ssc_lookup[i];
++      }
++
++      dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n",
++              rate, gtr_phy->refclk);
++
++      return NULL;
++}
++
+ /* Configure PLL and spread-sprectrum clock. */
+-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+ {
+       const struct xpsgtr_ssc *ssc;
+       u32 step_size;
+-      ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk];
++      ssc = xpsgtr_find_sscs(gtr_phy);
++      if (!ssc)
++              return -EINVAL;
++
+       step_size = ssc->step_size;
+       xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
+@@ -446,6 +471,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+       xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB,
+                          STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) |
+                          FORCE_STEP_SIZE | FORCE_STEPS);
++
++      return 0;
+ }
+ /* Configure the lane protocol. */
+@@ -658,7 +685,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+        * Configure the PLL, the lane protocol, and perform protocol-specific
+        * initialization.
+        */
+-      xpsgtr_configure_pll(gtr_phy);
++      ret = xpsgtr_configure_pll(gtr_phy);
++      if (ret)
++              goto out;
++
+       xpsgtr_lane_set_protocol(gtr_phy);
+       switch (gtr_phy->protocol) {
+@@ -823,8 +853,7 @@ static struct phy *xpsgtr_xlate(struct device *dev,
+       }
+       refclk = args->args[3];
+-      if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) ||
+-          !gtr_dev->refclk_sscs[refclk]) {
++      if (refclk >= ARRAY_SIZE(gtr_dev->clk)) {
+               dev_err(dev, "Invalid reference clock number %u\n", refclk);
+               return ERR_PTR(-EINVAL);
+       }
+@@ -928,9 +957,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+ {
+       unsigned int refclk;
+-      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
+-              unsigned long rate;
+-              unsigned int i;
++      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) {
+               struct clk *clk;
+               char name[8];
+@@ -946,29 +973,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+                       continue;
+               gtr_dev->clk[refclk] = clk;
+-
+-              /*
+-               * Get the spread spectrum (SSC) settings for the reference
+-               * clock rate.
+-               */
+-              rate = clk_get_rate(clk);
+-
+-              for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      /* Allow an error of 100 ppm */
+-                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
+-
+-                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+-                              gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+-                              break;
+-                      }
+-              }
+-
+-              if (i == ARRAY_SIZE(ssc_lookup)) {
+-                      dev_err(gtr_dev->dev,
+-                              "Invalid rate %lu for reference clock %u\n",
+-                              rate, refclk);
+-                      return -EINVAL;
+-              }
+       }
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
new file mode 100644 (file)
index 0000000..34b3d23
--- /dev/null
@@ -0,0 +1,58 @@
+From adf1864247d1f95271535b2668b5878a1b4e71f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:28:44 -0500
+Subject: pinctrl-amd: Don't clear S4 wake bits at probe
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
+
+commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+introduced a regression where Wake-on-LAN no longer works after suspend
+or shutdown on some AMD platforms.
+
+Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI
+PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake
+sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME
+does not use GPIO IRQ infrastructure and relies on firmware configuration.
+
+The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake
+bits on probe again") was to clear spurious wake bits left by firmware
+to prevent unwanted wakeups. However, S4 wake bits are used for
+hardware-level wake sources like WoL that bypass the kernel's IRQ wake
+API.
+
+Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
+- Firmware-configured S4 wake sources (WoL) continue working
+- Kernel maintains control of S3/S0i3 wake policy via set_wake()
+- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
+  amd: Take suspend type into consideration which pins are non-wake")
+
+The trade-off is that firmware-programmed spurious S4 wake bits remain
+set, but this is less problematic than breaking WoL.
+
+Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/pinctrl-amd.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index debf36ce57857..65006eceaf399 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
+       u32 pin_reg, mask;
+       int i;
+-      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
+-              BIT(WAKE_CNTRL_OFF_S4);
++      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
+       for (i = 0; i < desc->npins; i++) {
+               int pin = desc->pins[i].number;
+-- 
+2.53.0
+
diff --git a/queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
new file mode 100644 (file)
index 0000000..d44d3ad
--- /dev/null
@@ -0,0 +1,58 @@
+From 693f0f227a1aac6f3bae30da8e3ea7871e9faa82 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 15:08:05 +0200
+Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
+
+From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+
+[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
+
+Pins 143 and 151 were not included in the PDC wakeup map. They are
+normally used for PCIe2A and PCIe3a PERST# respectively, so they're
+unlikely to be excercised in practice, but still add them for the sake
+of completeness.
+
+Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver")
+Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+index 4b1c49697698d..67945ce867fa1 100644
+--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = {
+       { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 },
+       { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 },
+       { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 },
+-      { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 },
+-      { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 },
+-      { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 },
+-      { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 },
+-      { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 },
+-      { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 },
+-      { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 },
+-      { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 },
+-      { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 },
+-      { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 },
++      { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 },
++      { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 },
++      { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 },
++      { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 },
++      { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 },
++      { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 },
++      { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 },
++      { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 },
++      { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 },
++      { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 },
++      { 225, 196 }, { 227, 197 },
+ };
+ static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
+-- 
+2.53.0
+
diff --git a/queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
new file mode 100644 (file)
index 0000000..1225d50
--- /dev/null
@@ -0,0 +1,70 @@
+From 2d9245bc8e7d31a26100bda1096511c7177c2490 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 17:24:53 +0530
+Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable
+
+From: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+
+[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ]
+
+GPIO interrupts that are wakeup capable need to be forwarded to wakeup
+capable parent irqchip. This is done via writing to it's wakeup_enable bit.
+
+Currently the bit is set only for PDC irqchip by checking skip_wake_irqs.
+skip_wake_irqs is set to differentiate between parent irqchips MPM and
+PDC. It is set when the parent irqchip is PDC to inform pinctrl about
+skipping the IRQ setting up at TLMM.
+
+However, the functionality to forward GPIO interrupts during SoC low
+power mode is needed regardless of which parent irqchip it is.
+Without the functionality it is impossible for MPM irqchip to detect the
+GPIO interrupt during SoC low power mode since for MPM irqchip the
+skip_wake_irqs is always false.
+
+Remove skip_wake_irqs condition when setting wakeup enable bit to allow
+forwarding GPIO interrupts for SoCs using MPM irqchip too.
+
+Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits")
+Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+Reviewed-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
+index 27eb585bf42df..994f46c4c3b6b 100644
+--- a/drivers/pinctrl/qcom/pinctrl-msm.c
++++ b/drivers/pinctrl/qcom/pinctrl-msm.c
+@@ -1247,12 +1247,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d)
+       /*
+        * If the wakeup_enable bit is present and marked as available for the
+        * requested GPIO, it should be enabled when the GPIO is marked as
+-       * wake irq in order to allow the interrupt event to be transfered to
+-       * the PDC HW.
++       * wake irq in order to allow the interrupt event to be transferred to
++       * the PDC/MPM HW.
+        * While the name implies only the wakeup event, it's also required for
+        * the interrupt event.
+        */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+@@ -1280,7 +1280,7 @@ static void msm_gpio_irq_relres(struct irq_data *d)
+       unsigned long flags;
+       /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+-- 
+2.53.0
+
diff --git a/queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..b13d00d
--- /dev/null
@@ -0,0 +1,38 @@
+From 100b184d93359398d2ca9684086d6c40b18f09fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..a36620d
--- /dev/null
@@ -0,0 +1,38 @@
+From ca7a601e5a6d6de7f80b47f6b53717136ecf605a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..ff71404
--- /dev/null
@@ -0,0 +1,38 @@
+From 8de7e4472ca96b99b2253a5d8d73d3b0baf46eb0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..4e99d93
--- /dev/null
@@ -0,0 +1,169 @@
+From b1a47e1e2ceef35feb092684adc110b04cd023bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index b5d744d2586f7..59a80f7193723 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..098cb46
--- /dev/null
@@ -0,0 +1,118 @@
+From b07c1b0529437fd76d0dac6262b5d4c454eea574 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ce5be43c5fbac..1061bcf7d1315 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index 5289afbb61aa7..e50e01abb0799 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 06a2d8d48bbac..87e6ab0e93b71 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -342,9 +342,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..3746175
--- /dev/null
@@ -0,0 +1,85 @@
+From c8c24b98870d434162cdc9ec3f16d3517ad1755e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 87e6ab0e93b71..1980a197034ba 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -331,23 +331,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
new file mode 100644 (file)
index 0000000..369ae4c
--- /dev/null
@@ -0,0 +1,63 @@
+From cc3ad3e21450c97cc3273d01c564d5ac19966831 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 03:21:32 +0200
+Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove
+
+From: Karl Mehltretter <kmehltretter@gmail.com>
+
+[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ]
+
+remove_pud_mapping() and remove_p4d_mapping() obtain a child table base
+with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for
+addr.
+
+RISC-V folds page-table levels at runtime. When a level is folded, its
+offset helper returns the parent entry itself, but the index can still be
+nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39
+folds both P4D and PUD, so memory hot-remove can descend into unrelated
+memory and pass an invalid page to __free_pages(). This can trigger:
+
+  kernel BUG at include/linux/mm.h:1810!
+  VM_BUG_ON_PAGE(page_ref_count(page) == 0)
+  arch_remove_memory+0x1e/0x5c
+  try_remove_memory+0x15e/0x200
+  remove_memory+0x24/0x3c
+
+Only add the index when the corresponding page-table level is enabled,
+matching p4d_offset() and pud_offset().
+
+Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
+Assisted-by: Claude:claude-fable-5
+Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
+Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/mm/init.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index 4594f77a06ee3..e52f7dabb370c 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -1726,7 +1726,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = pud_addr_end(addr, end);
+-              pudp = pud_base + pud_index(addr);
++              pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base;
+               pud = pudp_get(pudp);
+               if (!pud_present(pud))
+                       continue;
+@@ -1757,7 +1757,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = p4d_addr_end(addr, end);
+-              p4dp = p4d_base + p4d_index(addr);
++              p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base;
+               p4d = p4dp_get(p4dp);
+               if (!p4d_present(p4d))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
new file mode 100644 (file)
index 0000000..3730d7e
--- /dev/null
@@ -0,0 +1,47 @@
+From 031806e8efdce8dc46b7960fd9cf119c358776a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 10:38:36 +0800
+Subject: rtase: fix double free of multi-frag skb on DMA map failure
+
+From: Yun Lu <luyun@kylinos.cn>
+
+[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ]
+
+In rtase_start_xmit(), when the head buffer DMA mapping fails after
+rtase_xmit_frags() has mapped all fragments, the error path clears
+the fragment descriptors with rtase_tx_clear_range(), which frees
+the skb through the last-frag slot and accounts tx_dropped. Control
+then falls through to the common error label, which frees the same
+skb a second time and counts it again.
+
+Return right after clearing the fragments when the skb owns frags;
+the no-frag case still drops through and frees the head skb once.
+
+Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function")
+Signed-off-by: Yun Lu <luyun@kylinos.cn>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Reviewed-by: Justin Lai <justinlai0215@realtek.com>
+Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+index a565d5fb6b85c..afae19cc99314 100644
+--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+@@ -1609,6 +1609,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+ err_dma_1:
+       ring->skbuff[entry] = NULL;
+       rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
++      if (frags)
++              /* the frags were cleared above, along with the skb */
++              return NETDEV_TX_OK;
+ err_dma_0:
+       tp->stats.tx_dropped++;
+-- 
+2.53.0
+
diff --git a/queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
new file mode 100644 (file)
index 0000000..233f00f
--- /dev/null
@@ -0,0 +1,46 @@
+From b2f48d7f8ae232fd1c0fe52975bf18b0807bb5b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 14:58:33 +0200
+Subject: sched/deadline: Use revised wakeup rule only for running dl_server
+
+From: Gabriele Monaco <gmonaco@redhat.com>
+
+[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ]
+
+Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for
+dl_server") applies the revised wakeup rule to any server, as a result
+servers that are not running (dl_defer_running == 0) and start with a
+deadline overflow get enqueued and can boost tasks as if they were
+running, invalidating the defer rule and the documented state model.
+
+Apply the revised wakeup rule only for deferrable servers that are
+marked as running.
+
+Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server")
+Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Acked-by: Juri Lelli <juri.lelli@redhat.com>
+Tested-by: Andrea Righi <arighi@nvidia.com>
+Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/sched/deadline.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
+index cb8eff0ebd228..f5c64d1a019d5 100644
+--- a/kernel/sched/deadline.c
++++ b/kernel/sched/deadline.c
+@@ -1079,7 +1079,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se)
+       if (dl_time_before(dl_se->deadline, rq_clock(rq)) ||
+           dl_entity_overflow(dl_se, rq_clock(rq))) {
+-              if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) &&
++              if (unlikely((!dl_is_implicit(dl_se) ||
++                            (dl_se->dl_defer && dl_se->dl_defer_running)) &&
+                            !dl_time_before(dl_se->deadline, rq_clock(rq)) &&
+                            !is_dl_boosted(dl_se))) {
+                       update_dl_revised_wakeup(dl_se, rq);
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..d2d3c85
--- /dev/null
@@ -0,0 +1,56 @@
+From 004182c7ca3985b5f820a5be6f1c50c3f2e787ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 29af3722ea220..0f169f243475f 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..d1a0197
--- /dev/null
@@ -0,0 +1,71 @@
+From ac067b3373db984e47ccc4e54974bdff16b2d148 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index c182aa83f2c93..4d23205129432 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
new file mode 100644 (file)
index 0000000..621895d
--- /dev/null
@@ -0,0 +1,165 @@
+From 9b990e6b9cd199725b6f82198729aa0754c6b31e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 16:11:45 +0800
+Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
+
+From: Xingui Yang <yangxingui@huawei.com>
+
+[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
+
+Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue
+for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
+the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue,
+calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI
+devices and waits for the host to become runtime-active. But the host
+cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the
+drain is blocked on that very handler.
+
+However skipping the drain reintroduces a race: hisi_sas returns from
+resume before all PHY UP work and libsas discovery work finish. The
+controller may then autosuspend while disks are still waking up. The
+disks issue IO to a suspended controller, the IO fails, and the disks
+get disabled.
+
+Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT
+notification to after sas_drain_work(). By then the host resume is about
+to complete, so device removal through device_link no longer blocks on
+the resume and the cycle is broken.
+
+With the deadlock gone, restore sas_resume_ha() (the draining variant)
+in hisi_sas and remove sas_resume_ha_no_sync().
+
+The reorder is safe for the other libsas consumers (isci, pm8001,
+aic94xx, mvsas). During suspend, sas_suspend_devices() calls
+sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to
+NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a
+timed-out phy's disk is immediately rejected by the LLDD before reaching
+hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET),
+and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both
+complete directly via scsi_done() without entering SCSI EH. This is
+identical in both the old and new ordering since lldd_dev_gone runs
+during suspend, before resume. The reorder only affects when the
+PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work()
+vs. asynchronous after resume returns), not whether I/O can reach the
+device. aic94xx and mvsas do not register any PM ops and never reach
+this code path.
+
+Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume")
+Signed-off-by: Xingui Yang <yangxingui@huawei.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------
+ drivers/scsi/libsas/sas_init.c         | 37 +++++++++++++-------------
+ include/scsi/libsas.h                  |  1 -
+ 3 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index e958b588d078f..b7dd4efca0c7f 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -5219,15 +5219,7 @@ static int _resume_v3_hw(struct device *device)
+               return rc;
+       }
+       phys_init_v3_hw(hisi_hba);
+-
+-      /*
+-       * If a directly-attached disk is removed during suspend, a deadlock
+-       * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+-       * hisi_hba->device to be active, which can only happen when resume
+-       * completes. So don't wait for the HA event workqueue to drain upon
+-       * resume.
+-       */
+-      sas_resume_ha_no_sync(sha);
++      sas_resume_ha(sha);
+       clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+       dev_warn(dev, "end of resuming controller\n");
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index 8566bb1208a05..ac157ab6a3011 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+       }
+ }
+-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
++void sas_resume_ha(struct sas_ha_struct *ha)
+ {
+       const unsigned long tmo = msecs_to_jiffies(25000);
+       int i;
+@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+                        i, i > 1 ? "s" : "");
+       wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
++
++      /*
++       * All phys are back up or timed out. Turn on I/O and drain
++       * pending work.
++       */
++      scsi_unblock_requests(ha->shost);
++      sas_drain_work(ha);
++
++      /*
++       * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
++       * calls sas_deform_port() -> sas_destruct_devices(), which removes
++       * SCSI devices and, for LLDDs using device_link() PM sync, waits
++       * for the host to be runtime-active. Sending it before the drain
++       * would deadlock: the drain waits for the handler, the handler
++       * waits for host resume, and host resume waits for the drain to
++       * finish.
++       */
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_phy *phy = ha->sas_phy[i];
+@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               }
+       }
+-      /* all phys are back up or timed out, turn on i/o so we can
+-       * flush out disks that did not return
+-       */
+-      scsi_unblock_requests(ha->shost);
+-      if (drain)
+-              sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+       sas_queue_deferred_work(ha);
+@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+        */
+       sas_resume_insert_broadcast_ha(ha);
+ }
+-
+-void sas_resume_ha(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, true);
+-}
+ EXPORT_SYMBOL(sas_resume_ha);
+-/* A no-sync variant, which does not call sas_drain_ha(). */
+-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, false);
+-}
+-EXPORT_SYMBOL(sas_resume_ha_no_sync);
+-
+ void sas_suspend_ha(struct sas_ha_struct *ha)
+ {
+       int i;
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index 1324068dd950f..2e3fcae62e27e 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -676,7 +676,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
+ extern int sas_unregister_ha(struct sas_ha_struct *);
+ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
+ extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
+-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
+ extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
+ int sas_phy_reset(struct sas_phy *phy, int hard_reset);
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
new file mode 100644 (file)
index 0000000..fff978c
--- /dev/null
@@ -0,0 +1,80 @@
+From 33688352daae6c90f41e537458d7a2cabe2f0dab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:30:10 +0300
+Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
+
+When target_get_sess_cmd() fails during session shutdown because
+percpu_ref_tryget_live() returns false, the command keeps the
+se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier
+without owning a reference. Final release through
+target_release_cmd_kref() then issues an unmatched percpu_ref_put().
+
+Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during
+cmd setup") moved the cmd_cnt assignment ahead of the reference
+acquisition.  Clear se_cmd->cmd_cnt whenever the initial
+target_get_sess_cmd() fails in target_init_cmd() and
+target_submit_tmr(), so release performs exactly one matching put per
+acquired reference.
+
+Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup")
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_transport.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
+index cf9834f958c9c..3dc49f6c7832e 100644
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -1691,6 +1691,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+                   u32 data_length, int task_attr, int data_dir, int flags)
+ {
+       struct se_portal_group *se_tpg;
++      int ret;
+       se_tpg = se_sess->se_tpg;
+       BUG_ON(!se_tpg);
+@@ -1720,7 +1721,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
+        * kref_put() to happen during fabric packet acknowledgement.
+        */
+-      return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      if (ret)
++              se_cmd->cmd_cnt = NULL;
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(target_init_cmd);
+@@ -1996,8 +2001,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * allocation failure.
+        */
+       ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+-      if (ret < 0)
++      if (ret < 0) {
++              se_cmd->cmd_cnt = NULL;
+               return -ENOMEM;
++      }
+       if (tm_type == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = tag;
+@@ -2005,6 +2012,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+       /* See target_submit_cmd for commentary */
+       ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+       if (ret) {
++              se_cmd->cmd_cnt = NULL;
+               core_tmr_release_req(se_cmd->se_tmr_req);
+               return ret;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
new file mode 100644 (file)
index 0000000..253161f
--- /dev/null
@@ -0,0 +1,54 @@
+From 3ab9e4cee8b8671f58da2137d6e74525c04cd9a0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:58:50 +0800
+Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE
+
+From: TanZheng <tanzheng@kylinos.cn>
+
+[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ]
+
+In the iblock_execute_pr_out() function, PRO_PREEMPT,
+PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability
+checks through ops->pr_clear. The error check allows unimplemented hooks
+to pass through the gate, resulting dereferencing a NULL function
+pointer.
+
+Check whether the hooks that need to be called are supported.
+
+Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock")
+Signed-off-by: TanZheng <tanzheng@kylinos.cn>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_iblock.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
+index c8dc92a7d63e6..0a232abee8f58 100644
+--- a/drivers/target/target_core_iblock.c
++++ b/drivers/target/target_core_iblock.c
+@@ -891,7 +891,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+               break;
+       case PRO_PREEMPT:
+       case PRO_PREEMPT_AND_ABORT:
+-              if (!ops->pr_clear) {
++              if (!ops->pr_preempt) {
+                       pr_err("block_device does not support pr_preempt.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+@@ -901,8 +901,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+                                     sa == PRO_PREEMPT_AND_ABORT);
+               break;
+       case PRO_RELEASE:
+-              if (!ops->pr_clear) {
+-                      pr_err("block_device does not support pr_pclear.\n");
++              if (!ops->pr_release) {
++                      pr_err("block_device does not support pr_release.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
new file mode 100644 (file)
index 0000000..863e67d
--- /dev/null
@@ -0,0 +1,70 @@
+From eef9d12d9caa5c8800203920ead61cbafe19e69f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 01:27:26 +0800
+Subject: scsi: ufs: core: Cancel RTC work in active-active suspend
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ]
+
+UFS RTC support schedules ufs_rtc_update_work to periodically update the
+device RTC. The work can issue query commands and access the UFS host
+controller.
+
+A previous change moved the RTC work cancellation before the PRE_CHANGE
+vendor suspend callback to close a race in the common suspend path.
+However, the active-active path jumps directly to vops_suspend after
+flushing exception handling work and therefore bypasses the
+cancellation.
+
+If the RTC work runs while the vendor suspend callback is gating or
+otherwise changing hardware state, it can access the controller during
+suspend and trigger an SError.
+
+Cancel the RTC work before entering the vendor suspend callback in the
+active-active path. Since this path now cancels the work, move the RTC
+work scheduling outside the device and link state restoration block in
+the resume path. This restarts RTC updates after an active-active
+suspend and resume cycle.
+
+Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend")
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 205427f6c4651..98bf5bc6f383e 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -9761,6 +9761,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+                       req_link_state == UIC_LINK_ACTIVE_STATE) {
+               ufshcd_disable_auto_bkops(hba);
+               flush_work(&hba->eeh_work);
++              cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
+               goto vops_suspend;
+       }
+@@ -9970,10 +9971,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+               if (ret)
+                       goto set_old_link_state;
+               ufshcd_set_timestamp_attr(hba);
+-              schedule_delayed_work(&hba->ufs_rtc_update_work,
+-                                    msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+       }
++      schedule_delayed_work(&hba->ufs_rtc_update_work,
++                            msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
++
+       if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+               ufshcd_enable_auto_bkops(hba);
+       else
+-- 
+2.53.0
+
diff --git a/queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..ab80c3c
--- /dev/null
@@ -0,0 +1,66 @@
+From 27db9be62504ea2a3ef85d6be276161d43ba0ed8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index ab2f35bc294da..d3cc884ccd599 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
index 85365a67ae27e6affffa946cc947755646ab1cf0..d33c8c5b542670c7c9df3725c34c5b038d1e0fa3 100644 (file)
@@ -17,3 +17,111 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 bpf-reset-register-bounds-before-narrowing-retval-ra.patch
 netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch
 thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
+pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
+pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
+gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
+ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
+ata-libahci_platform-support-non-consecutive-port-nu.patch
+ahci-introduce-ahci_ignore_port-helper.patch
+ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+of-reserved_mem-add-code-to-dynamically-allocate-res.patch
+of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
+btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
+btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
+phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
+keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+ipvs-fix-the-checksum-validations.patch
+ipvs-fix-places-with-wrong-packet-offsets.patch
+ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+spi-spi-cadence-supports-transmission-with-bits_per_.patch
+spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
+hwmon-nct6775-core-fix-number-of-temperature-registe.patch
+hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch
+hwmon-ina2xx-add-support-for-ina260.patch
+hwmon-ina226-add-support-for-sy24655.patch
+hwmon-ina2xx-make-it-easier-to-add-more-devices.patch
+hwmon-ina2xx-add-support-for-ina234.patch
+hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch
+hwmon-ina2xx-fix-various-overflow-issues.patch
+hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
+hwmon-sht3x-fix-unaligned-accesses.patch
+hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
+hwmon-nzxt-smart2-dma-align-output-buffer.patch
+net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+net-ethernet-mtk_eth_soc-support-named-irqs.patch
+net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch
+net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+idpf-adjust-txq-ring-count-minimum.patch
+idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
+bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
+bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
+bluetooth-iso-fix-leaking-sk-after-socket-release.patch
+bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
+bluetooth-btintel-validate-length-before-parsing-dia.patch
+bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
+bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
+bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
+bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
+scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+tracing-remove-trace_event_fl_filtered-logic.patch
+tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
+accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
+riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
+net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
+net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
+net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
+ksmbd-return-success-for-deferred-final-close.patch
+ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
diff --git a/queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..71c1abf
--- /dev/null
@@ -0,0 +1,85 @@
+From f2b7fc73c4ed556a8930d0158ea463ad1413eb9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 81a1c116e9031..882d535d17a4c 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -1436,8 +1436,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1549,8 +1551,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1816,8 +1820,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
new file mode 100644 (file)
index 0000000..e276d67
--- /dev/null
@@ -0,0 +1,111 @@
+From 40c1378b7f32f28037e77a77683bf27333ed5ccd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 18:25:10 +0530
+Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Srikanth Boyapally <srikanth.boyapally@amd.com>
+
+[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ]
+
+SPI host transfers could intermittently stall with spi_transfer timeouts.
+The TXFULL condition was checked only once in cdns_transfer_one() before
+cdns_spi_process_fifo(), so if the FIFO became full again during refill,
+writes could be dropped and the transfer would never complete.
+
+Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so
+the 10µs back-off is applied per FIFO entry during filling, ensuring
+forward progress and eliminating spurious timeouts.
+
+Restrict the delay to host mode using spi_controller_is_target(), the
+controller is passed into cdns_spi_process_fifo() so the check is made at
+the point of use. In target mode this delay must not run as it causes the
+target to miss its transfer window and corrupt data.
+
+Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()")
+Signed-off-by: Srikanth Boyapally <srikanth.boyapally@amd.com>
+Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index fee908b7f327e..015dce7fc9d81 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi)
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
++ * @ctlr:     Pointer to the spi_controller structure
+  * @xspi:     Pointer to the cdns_spi structure
+  * @ntx:      Number of bytes to pack into the TX FIFO
+  * @nrx:      Number of bytes to drain from the RX FIFO
+  */
+-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
++static void cdns_spi_process_fifo(struct spi_controller *ctlr,
++                                struct cdns_spi *xspi, int ntx, int nrx)
+ {
+       ntx = clamp(ntx, 0, xspi->tx_bytes);
+       nrx = clamp(nrx, 0, xspi->rx_bytes);
+@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+               }
+               if (ntx) {
++                      /* When xspi in busy condition, bytes may send failed,
++                       * then spi control didn't work thoroughly, add one byte
++                       * delay. Only in host mode; in target mode this delay
++                       * causes data corruption as the target fails to prepare
++                       * data in time.
++                       */
++                      if (!spi_controller_is_target(ctlr) &&
++                          (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL))
++                              udelay(10);
++
+                       cdns_spi_writer(xspi);
+                       ntx--;
+               }
+@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, 1);
+               if (xspi->tx_bytes) {
+-                      cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt);
+               } else {
+                       /* Fixed delay due to controller limitation with
+                        * RX_NEMPTY incorrect status
+                        * Xilinx AR:65885 contains more details
+                        */
+                       udelay(10);
+-                      cdns_spi_process_fifo(xspi, 0, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt);
+                       cdns_spi_write(xspi, CDNS_SPI_IDR,
+                                      CDNS_SPI_IXR_DEFAULT);
+                       spi_finalize_current_transfer(ctlr);
+@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
+       }
+-      /* When xspi in busy condition, bytes may send failed,
+-       * then spi control didn't work thoroughly, add one byte delay
+-       */
+-      if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+-              udelay(10);
+-
+       xspi->n_bytes = cdns_spi_n_bytes(transfer);
+       xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+       xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+-      cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
++      cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+       return transfer->len;
+-- 
+2.53.0
+
diff --git a/queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch
new file mode 100644 (file)
index 0000000..274a809
--- /dev/null
@@ -0,0 +1,200 @@
+From 18e80139530cb1ca620d7e0812c6f6fc9f0b4a89 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Oct 2025 15:30:02 +0800
+Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and
+ 32
+
+From: Jun Guo <jun.guo@cixtech.com>
+
+[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ]
+
+The default FIFO data width of the Cadence SPI IP is 8 bits, but
+the hardware supports configurations of 16 bits and 32 bits.
+This patch enhances the driver to support communication with both
+16-bits and 32-bits FIFO data widths.
+
+Signed-off-by: Jun Guo <jun.guo@cixtech.com>
+Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 93 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index 914384348448d..fee908b7f327e 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -109,6 +109,7 @@
+  * @rxbuf:            Pointer to the RX buffer
+  * @tx_bytes:         Number of bytes left to transfer
+  * @rx_bytes:         Number of bytes requested
++ * @n_bytes:          Number of bytes per word
+  * @dev_busy:         Device busy flag
+  * @is_decoded_cs:    Flag for decoder property set or not
+  * @tx_fifo_depth:    Depth of the TX FIFO
+@@ -120,16 +121,24 @@ struct cdns_spi {
+       struct clk *pclk;
+       unsigned int clk_rate;
+       u32 speed_hz;
+-      const u8 *txbuf;
+-      u8 *rxbuf;
++      const void *txbuf;
++      void *rxbuf;
+       int tx_bytes;
+       int rx_bytes;
++      u8 n_bytes;
+       u8 dev_busy;
+       u32 is_decoded_cs;
+       unsigned int tx_fifo_depth;
+       struct reset_control *rstc;
+ };
++enum cdns_spi_frame_n_bytes {
++      CDNS_SPI_N_BYTES_NULL = 0,
++      CDNS_SPI_N_BYTES_U8 = 1,
++      CDNS_SPI_N_BYTES_U16 = 2,
++      CDNS_SPI_N_BYTES_U32 = 4
++};
++
+ /* Macros for the SPI controller read/write */
+ static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
+ {
+@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
+       return 0;
+ }
++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
++{
++      if (transfer->bits_per_word <= 8)
++              return CDNS_SPI_N_BYTES_U8;
++      else if (transfer->bits_per_word <= 16)
++              return CDNS_SPI_N_BYTES_U16;
++      else
++              return CDNS_SPI_N_BYTES_U32;
++}
++
++static inline void cdns_spi_reader(struct cdns_spi *xspi)
++{
++      u32 rxw = 0;
++
++      if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
++              pr_err("%s: rxbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
++      if (xspi->rxbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      *(u8 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      *(u16 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      *(u32 *)xspi->rxbuf = rxw;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
++      }
++}
++
++static inline void cdns_spi_writer(struct cdns_spi *xspi)
++{
++      u32 txw = 0;
++
++      if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
++              pr_err("%s: txbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      if (xspi->txbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      txw = *(u8 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      txw = *(u16 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      txw = *(u32 *)xspi->txbuf;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
++              xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
++      }
++}
++
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
+  * @xspi:     Pointer to the cdns_spi structure
+@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+       while (ntx || nrx) {
+               if (nrx) {
+-                      u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
+-
+-                      if (xspi->rxbuf)
+-                              *xspi->rxbuf++ = data;
+-
++                      cdns_spi_reader(xspi);
+                       nrx--;
+               }
+               if (ntx) {
+-                      if (xspi->txbuf)
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
+-                      else
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
+-
++                      cdns_spi_writer(xspi);
+                       ntx--;
+               }
+-
+       }
+ }
+@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+       if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+               udelay(10);
++      xspi->n_bytes = cdns_spi_n_bytes(transfer);
++      xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
++      xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
++
+       cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
+       ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
+       ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
++      if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
++              ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
++
+       if (!spi_controller_is_target(ctlr)) {
+               ctlr->mode_bits |=  SPI_CS_HIGH;
+               ctlr->set_cs = cdns_spi_chipselect;
+@@ -809,6 +888,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
+ static const struct of_device_id cdns_spi_of_match[] = {
+       { .compatible = "xlnx,zynq-spi-r1p6" },
++      { .compatible = "cix,sky1-spi-r1p6" },
+       { .compatible = "cdns,spi-r1p6" },
+       { /* end of table */ }
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
new file mode 100644 (file)
index 0000000..501c826
--- /dev/null
@@ -0,0 +1,70 @@
+From 89b5ec1d7728a1cac731adc0766b3c55c489469a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:50:00 +0900
+Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging
+ functions
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ]
+
+mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into
+tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map().
+If these functions are invoked while mmio_trace_array is NULL (e.g. before
+initialization or after disabled), accessing tr->array_buffer.buffer will
+result in a NULL pointer dereference crash.
+
+Fix this by adding an explicit NULL check for tr at the beginning of
+__trace_mmiotrace_rw() and __trace_mmiotrace_map().
+
+Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2
+Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index 6b964471265e1..251ed7051e409 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -295,11 +295,15 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
+                               struct trace_array_cpu *data,
+                               struct mmiotrace_rw *rw)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_rw *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
+                                         sizeof(*entry), trace_ctx);
+@@ -324,11 +328,15 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
+                               struct trace_array_cpu *data,
+                               struct mmiotrace_map *map)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_map *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
+                                         sizeof(*entry), trace_ctx);
+-- 
+2.53.0
+
diff --git a/queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..1353b43
--- /dev/null
@@ -0,0 +1,43 @@
+From d0156f120da26130fc6529e3d96a49a89b06164a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index 4d9e5c830dbe1..c523ce5aa4958 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch b/queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch
new file mode 100644 (file)
index 0000000..7bbae9b
--- /dev/null
@@ -0,0 +1,445 @@
+From 626ccd985bae0c38165439fece6201e049cb2cb5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 11 Sep 2024 09:00:26 +0800
+Subject: tracing: Remove TRACE_EVENT_FL_FILTERED logic
+
+From: Zheng Yejian <zhengyejian@huaweicloud.com>
+
+[ Upstream commit 49e4154f4b16345da5e219b23ed9737a6e735bc1 ]
+
+After commit dcb0b5575d24 ("tracing: Remove TRACE_EVENT_FL_USE_CALL_FILTER
+ logic"), no one's going to set the TRACE_EVENT_FL_FILTERED or change the
+call->filter, so remove related logic.
+
+Link: https://lore.kernel.org/20240911010026.2302849-1-zhengyejian@huaweicloud.com
+Signed-off-by: Zheng Yejian <zhengyejian@huaweicloud.com>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Stable-dep-of: 12b80cdbc54c ("tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/trace_events.h         |  4 ---
+ kernel/trace/trace.c                 | 44 ++++++----------------------
+ kernel/trace/trace.h                 |  4 ---
+ kernel/trace/trace_branch.c          |  4 +--
+ kernel/trace/trace_events.c          |  2 --
+ kernel/trace/trace_functions_graph.c |  8 ++---
+ kernel/trace/trace_hwlat.c           |  4 +--
+ kernel/trace/trace_mmiotrace.c       |  8 ++---
+ kernel/trace/trace_osnoise.c         | 12 ++------
+ kernel/trace/trace_sched_wakeup.c    |  8 ++---
+ 10 files changed, 20 insertions(+), 78 deletions(-)
+
+diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
+index 54ba231ded519..98ee10b417e67 100644
+--- a/include/linux/trace_events.h
++++ b/include/linux/trace_events.h
+@@ -327,7 +327,6 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
+ void trace_event_buffer_commit(struct trace_event_buffer *fbuffer);
+ enum {
+-      TRACE_EVENT_FL_FILTERED_BIT,
+       TRACE_EVENT_FL_CAP_ANY_BIT,
+       TRACE_EVENT_FL_NO_SET_FILTER_BIT,
+       TRACE_EVENT_FL_IGNORE_ENABLE_BIT,
+@@ -343,7 +342,6 @@ enum {
+ /*
+  * Event flags:
+- *  FILTERED    - The event has a filter attached
+  *  CAP_ANY     - Any user can enable for perf
+  *  NO_SET_FILTER - Set when filter has error and is to be ignored
+  *  IGNORE_ENABLE - For trace internal events, do not enable with debugfs file
+@@ -359,7 +357,6 @@ enum {
+  *  TEST_STR      - The event has a "%s" that points to a string outside the event
+  */
+ enum {
+-      TRACE_EVENT_FL_FILTERED         = (1 << TRACE_EVENT_FL_FILTERED_BIT),
+       TRACE_EVENT_FL_CAP_ANY          = (1 << TRACE_EVENT_FL_CAP_ANY_BIT),
+       TRACE_EVENT_FL_NO_SET_FILTER    = (1 << TRACE_EVENT_FL_NO_SET_FILTER_BIT),
+       TRACE_EVENT_FL_IGNORE_ENABLE    = (1 << TRACE_EVENT_FL_IGNORE_ENABLE_BIT),
+@@ -385,7 +382,6 @@ struct trace_event_call {
+       };
+       struct trace_event      event;
+       char                    *print_fmt;
+-      struct event_filter     *filter;
+       /*
+        * Static events can disappear with modules,
+        * where as dynamic ones need their own ref count.
+diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
+index 9e5aa468d7e48..fcbeda360df3e 100644
+--- a/kernel/trace/trace.c
++++ b/kernel/trace/trace.c
+@@ -591,19 +591,6 @@ int tracing_check_open_get_tr(struct trace_array *tr)
+       return 0;
+ }
+-int call_filter_check_discard(struct trace_event_call *call, void *rec,
+-                            struct trace_buffer *buffer,
+-                            struct ring_buffer_event *event)
+-{
+-      if (unlikely(call->flags & TRACE_EVENT_FL_FILTERED) &&
+-          !filter_match_preds(call->filter, rec)) {
+-              __trace_event_discard_commit(buffer, event);
+-              return 1;
+-      }
+-
+-      return 0;
+-}
+-
+ /**
+  * trace_find_filtered_pid - check if a pid exists in a filtered_pid list
+  * @filtered_pids: The list of pids to check
+@@ -2894,7 +2881,6 @@ void
+ trace_function(struct trace_array *tr, unsigned long ip, unsigned long
+              parent_ip, unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_function;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ring_buffer_event *event;
+       struct ftrace_entry *entry;
+@@ -2907,11 +2893,9 @@ trace_function(struct trace_array *tr, unsigned long ip, unsigned long
+       entry->ip                       = ip;
+       entry->parent_ip                = parent_ip;
+-      if (!call_filter_check_discard(call, entry, buffer, event)) {
+-              if (static_branch_unlikely(&trace_function_exports_enabled))
+-                      ftrace_exports(event, TRACE_EXPORT_FUNCTION);
+-              __buffer_unlock_commit(buffer, event);
+-      }
++      if (static_branch_unlikely(&trace_function_exports_enabled))
++              ftrace_exports(event, TRACE_EXPORT_FUNCTION);
++      __buffer_unlock_commit(buffer, event);
+ }
+ #ifdef CONFIG_STACKTRACE
+@@ -2938,7 +2922,6 @@ static void __ftrace_trace_stack(struct trace_array *tr,
+                                unsigned int trace_ctx,
+                                int skip, struct pt_regs *regs)
+ {
+-      struct trace_event_call *call = &event_kernel_stack;
+       struct ring_buffer_event *event;
+       unsigned int size, nr_entries;
+       struct ftrace_stack *fstack;
+@@ -3011,8 +2994,7 @@ static void __ftrace_trace_stack(struct trace_array *tr,
+       memcpy(&entry->caller, fstack->calls,
+              flex_array_size(entry, caller, nr_entries));
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              __buffer_unlock_commit(buffer, event);
++      __buffer_unlock_commit(buffer, event);
+  out:
+       /* Again, don't let gcc optimize things here */
+@@ -3086,7 +3068,6 @@ static void
+ ftrace_trace_userstack(struct trace_array *tr,
+                      struct trace_buffer *buffer, unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_user_stack;
+       struct ring_buffer_event *event;
+       struct userstack_entry *entry;
+@@ -3120,8 +3101,7 @@ ftrace_trace_userstack(struct trace_array *tr,
+       memset(&entry->caller, 0, sizeof(entry->caller));
+       stack_trace_save_user(entry->caller, FTRACE_STACK_ENTRIES);
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              __buffer_unlock_commit(buffer, event);
++      __buffer_unlock_commit(buffer, event);
+  out_drop_count:
+       __this_cpu_dec(user_stack_count);
+@@ -3290,7 +3270,6 @@ static void trace_printk_start_stop_comm(int enabled)
+  */
+ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
+ {
+-      struct trace_event_call *call = &event_bprint;
+       struct ring_buffer_event *event;
+       struct trace_buffer *buffer;
+       struct trace_array *tr = READ_ONCE(printk_trace);
+@@ -3334,10 +3313,8 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
+       entry->fmt                      = fmt;
+       memcpy(entry->buf, tbuffer, sizeof(u32) * len);
+-      if (!call_filter_check_discard(call, entry, buffer, event)) {
+-              __buffer_unlock_commit(buffer, event);
+-              ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL);
+-      }
++      __buffer_unlock_commit(buffer, event);
++      ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL);
+ out:
+       ring_buffer_nest_end(buffer);
+@@ -3356,7 +3333,6 @@ static __printf(3, 0)
+ int __trace_array_vprintk(struct trace_buffer *buffer,
+                         unsigned long ip, const char *fmt, va_list args)
+ {
+-      struct trace_event_call *call = &event_print;
+       struct ring_buffer_event *event;
+       int len = 0, size;
+       struct print_entry *entry;
+@@ -3391,10 +3367,8 @@ int __trace_array_vprintk(struct trace_buffer *buffer,
+       entry->ip = ip;
+       memcpy(&entry->buf, tbuffer, len + 1);
+-      if (!call_filter_check_discard(call, entry, buffer, event)) {
+-              __buffer_unlock_commit(buffer, event);
+-              ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL);
+-      }
++      __buffer_unlock_commit(buffer, event);
++      ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL);
+ out:
+       ring_buffer_nest_end(buffer);
+diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
+index ed4fcc438a0bf..baa39009a25d1 100644
+--- a/kernel/trace/trace.h
++++ b/kernel/trace/trace.h
+@@ -1440,10 +1440,6 @@ struct trace_subsystem_dir {
+       int                             nr_events;
+ };
+-extern int call_filter_check_discard(struct trace_event_call *call, void *rec,
+-                                   struct trace_buffer *buffer,
+-                                   struct ring_buffer_event *event);
+-
+ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
+                                    struct trace_buffer *buffer,
+                                    struct ring_buffer_event *event,
+diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c
+index 30f72e0ecb5d4..138f69521eb4a 100644
+--- a/kernel/trace/trace_branch.c
++++ b/kernel/trace/trace_branch.c
+@@ -30,7 +30,6 @@ static struct trace_array *branch_tracer;
+ static void
+ probe_likely_condition(struct ftrace_likely_data *f, int val, int expect)
+ {
+-      struct trace_event_call *call = &event_branch;
+       struct trace_array *tr = branch_tracer;
+       struct trace_buffer *buffer;
+       struct trace_array_cpu *data;
+@@ -82,8 +81,7 @@ probe_likely_condition(struct ftrace_likely_data *f, int val, int expect)
+       entry->line = f->data.line;
+       entry->correct = val == expect;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+  out:
+       current->trace_recursion &= ~TRACE_BRANCH_BIT;
+diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
+index d07b8062ded30..14feebea4f000 100644
+--- a/kernel/trace/trace_events.c
++++ b/kernel/trace/trace_events.c
+@@ -3319,8 +3319,6 @@ static void __trace_remove_event_call(struct trace_event_call *call)
+ {
+       event_remove(call);
+       trace_destroy_fields(call);
+-      free_event_filter(call->filter);
+-      call->filter = NULL;
+ }
+ static int probe_remove_event_call(struct trace_event_call *call)
+diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
+index 47ea114ca9f46..04d403a38f04f 100644
+--- a/kernel/trace/trace_functions_graph.c
++++ b/kernel/trace/trace_functions_graph.c
+@@ -102,7 +102,6 @@ int __trace_graph_entry(struct trace_array *tr,
+                               struct ftrace_graph_ent *trace,
+                               unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_funcgraph_entry;
+       struct ring_buffer_event *event;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ftrace_graph_ent_entry *entry;
+@@ -113,8 +112,7 @@ int __trace_graph_entry(struct trace_array *tr,
+               return 0;
+       entry   = ring_buffer_event_data(event);
+       entry->graph_ent                        = *trace;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+       return 1;
+ }
+@@ -223,7 +221,6 @@ void __trace_graph_return(struct trace_array *tr,
+                               struct ftrace_graph_ret *trace,
+                               unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_funcgraph_exit;
+       struct ring_buffer_event *event;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ftrace_graph_ret_entry *entry;
+@@ -234,8 +231,7 @@ void __trace_graph_return(struct trace_array *tr,
+               return;
+       entry   = ring_buffer_event_data(event);
+       entry->ret                              = *trace;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+ }
+ void trace_graph_return(struct ftrace_graph_ret *trace,
+diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c
+index bc437b6ce8969..8703eb86a20c8 100644
+--- a/kernel/trace/trace_hwlat.c
++++ b/kernel/trace/trace_hwlat.c
+@@ -130,7 +130,6 @@ static bool hwlat_busy;
+ static void trace_hwlat_sample(struct hwlat_sample *sample)
+ {
+       struct trace_array *tr = hwlat_trace;
+-      struct trace_event_call *call = &event_hwlat;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ring_buffer_event *event;
+       struct hwlat_entry *entry;
+@@ -148,8 +147,7 @@ static void trace_hwlat_sample(struct hwlat_sample *sample)
+       entry->nmi_count                = sample->nmi_count;
+       entry->count                    = sample->count;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+ }
+ /* Macros to encapsulate the time capturing infrastructure */
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index c523ce5aa4958..6b964471265e1 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -295,7 +295,6 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
+                               struct trace_array_cpu *data,
+                               struct mmiotrace_rw *rw)
+ {
+-      struct trace_event_call *call = &event_mmiotrace_rw;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_rw *entry;
+@@ -311,8 +310,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
+       entry   = ring_buffer_event_data(event);
+       entry->rw                       = *rw;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
++      trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+ }
+ void mmio_trace_rw(struct mmiotrace_rw *rw)
+@@ -326,7 +324,6 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
+                               struct trace_array_cpu *data,
+                               struct mmiotrace_map *map)
+ {
+-      struct trace_event_call *call = &event_mmiotrace_map;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_map *entry;
+@@ -342,8 +339,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
+       entry   = ring_buffer_event_data(event);
+       entry->map                      = *map;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
++      trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+ }
+ void mmio_trace_mapping(struct mmiotrace_map *map)
+diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
+index 4daedf8debdc5..549ac1916dc39 100644
+--- a/kernel/trace/trace_osnoise.c
++++ b/kernel/trace/trace_osnoise.c
+@@ -501,7 +501,6 @@ static void print_osnoise_headers(struct seq_file *s)
+ static void
+ __trace_osnoise_sample(struct osnoise_sample *sample, struct trace_buffer *buffer)
+ {
+-      struct trace_event_call *call = &event_osnoise;
+       struct ring_buffer_event *event;
+       struct osnoise_entry *entry;
+@@ -519,8 +518,7 @@ __trace_osnoise_sample(struct osnoise_sample *sample, struct trace_buffer *buffe
+       entry->softirq_count    = sample->softirq_count;
+       entry->thread_count     = sample->thread_count;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+ }
+ /*
+@@ -580,7 +578,6 @@ static void print_timerlat_headers(struct seq_file *s)
+ static void
+ __trace_timerlat_sample(struct timerlat_sample *sample, struct trace_buffer *buffer)
+ {
+-      struct trace_event_call *call = &event_osnoise;
+       struct ring_buffer_event *event;
+       struct timerlat_entry *entry;
+@@ -593,8 +590,7 @@ __trace_timerlat_sample(struct timerlat_sample *sample, struct trace_buffer *buf
+       entry->context                  = sample->context;
+       entry->timer_latency            = sample->timer_latency;
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+ }
+ /*
+@@ -656,7 +652,6 @@ static void timerlat_save_stack(int skip)
+ static void
+ __timerlat_dump_stack(struct trace_buffer *buffer, struct trace_stack *fstack, unsigned int size)
+ {
+-      struct trace_event_call *call = &event_osnoise;
+       struct ring_buffer_event *event;
+       struct stack_entry *entry;
+@@ -670,8 +665,7 @@ __timerlat_dump_stack(struct trace_buffer *buffer, struct trace_stack *fstack, u
+       entry->size = fstack->nr_entries;
+       memcpy(&entry->caller, fstack->calls, size);
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit_nostack(buffer, event);
++      trace_buffer_unlock_commit_nostack(buffer, event);
+ }
+ /*
+diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
+index 039382576bc16..59e30d897a9da 100644
+--- a/kernel/trace/trace_sched_wakeup.c
++++ b/kernel/trace/trace_sched_wakeup.c
+@@ -376,7 +376,6 @@ tracing_sched_switch_trace(struct trace_array *tr,
+                          struct task_struct *next,
+                          unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_context_switch;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+       struct ring_buffer_event *event;
+       struct ctx_switch_entry *entry;
+@@ -394,8 +393,7 @@ tracing_sched_switch_trace(struct trace_array *tr,
+       entry->next_state               = task_state_index(next);
+       entry->next_cpu = task_cpu(next);
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
++      trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+ }
+ static void
+@@ -404,7 +402,6 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
+                          struct task_struct *curr,
+                          unsigned int trace_ctx)
+ {
+-      struct trace_event_call *call = &event_wakeup;
+       struct ring_buffer_event *event;
+       struct ctx_switch_entry *entry;
+       struct trace_buffer *buffer = tr->array_buffer.buffer;
+@@ -422,8 +419,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
+       entry->next_state               = task_state_index(wakee);
+       entry->next_cpu                 = task_cpu(wakee);
+-      if (!call_filter_check_discard(call, entry, buffer, event))
+-              trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
++      trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+ }
+ static void notrace
+-- 
+2.53.0
+
diff --git a/queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..df94b89
--- /dev/null
@@ -0,0 +1,52 @@
+From aec0e05ca4f9d13307fdf2e76c7343353887cf28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index d4ed0c0a335ca..2a23e431cbb5a 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
new file mode 100644 (file)
index 0000000..91463d8
--- /dev/null
@@ -0,0 +1,49 @@
+From a9a9b37003fa6a6b173974e4b0540bcbffa959a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 02:25:20 +0500
+Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ]
+
+In encode_message() the per-transaction lower-bound check compares
+trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer,
+instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr.
+
+Every other length check in this file (encode_message() at the loop
+guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so
+this is an inconsistency. On 64-bit builds the pointer and the struct
+are both 8 bytes, so the check is correct by coincidence and there is
+no behavioural change. On 32-bit builds the pointer is 4 bytes, which
+weakens the minimum-length check below the 8-byte header size.
+
+Use sizeof(*trans_hdr) so the check validates against the actual
+transaction header size on all builds.
+
+Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()")
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accel/qaic/qaic_control.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
+index 8eae30fe14f98..8759b53ba38eb 100644
+--- a/drivers/accel/qaic/qaic_control.c
++++ b/drivers/accel/qaic/qaic_control.c
+@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+                       break;
+               }
+               trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
+-              if (trans_hdr->len < sizeof(trans_hdr) ||
++              if (trans_hdr->len < sizeof(*trans_hdr) ||
+                   size_add(user_len, trans_hdr->len) > user_msg->len) {
+                       ret = -EINVAL;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch b/queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch
new file mode 100644 (file)
index 0000000..c16bf3c
--- /dev/null
@@ -0,0 +1,41 @@
+From d93886858d1051c551d395f77757e9f34a7ea295 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 14:29:01 -0400
+Subject: af_unix: fix listen() succeeding on sockets in the wrong state
+
+From: John Ericson <mail@johnericson.me>
+
+[ Upstream commit f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 ]
+
+Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
+reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
+= -EINVAL and the socket-state check in unix_listen(). Since
+prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
+socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
+already connected) now silently returns success without doing anything,
+instead of failing with EINVAL as it did before.
+
+Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
+Signed-off-by: John Ericson <mail@johnericson.me>
+Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/unix/af_unix.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
+index b65479a738f4c..52b17331e9d51 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -835,6 +835,7 @@ static int unix_listen(struct socket *sock, int backlog)
+       if (err)
+               goto out;
+       unix_state_lock(sk);
++      err = -EINVAL;
+       if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
+               goto out_unlock;
+       if (backlog > sk->sk_max_ack_backlog)
+-- 
+2.53.0
+
diff --git a/queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch b/queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch
new file mode 100644 (file)
index 0000000..9226a28
--- /dev/null
@@ -0,0 +1,104 @@
+From 93d8ad7629a4fa7bfe80c812af43c9abc771faa3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Oct 2025 16:34:05 -0700
+Subject: arch/x86: mshyperv: Discover Confidential VMBus availability
+
+From: Roman Kisel <romank@linux.microsoft.com>
+
+[ Upstream commit 7c8b6c326d830ca5c6b95f390c703966e14167e6 ]
+
+Confidential VMBus requires enabling paravisor SynIC, and
+the x86_64 guest has to inspect the Virtualization Stack (VS)
+CPUID leaf to see if Confidential VMBus is available. If it is,
+the guest shall enable the paravisor SynIC.
+
+Read the relevant data from the VS CPUID leaf. Refactor the
+code to avoid repeating CPUID and add flags to the struct
+ms_hyperv_info. For ARM64, the flag for Confidential VMBus
+is not set which provides the desired behaviour for now as
+it is not available on ARM64 just yet. Once ARM64 CCA guests
+are supported, this flag will be set unconditionally when
+running such a guest.
+
+Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/mshyperv.c | 28 +++++++++++++++-------------
+ include/asm-generic/mshyperv.h |  2 ++
+ 2 files changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
+index c4febdbcfe4d8..cae0dcd9006bd 100644
+--- a/arch/x86/kernel/cpu/mshyperv.c
++++ b/arch/x86/kernel/cpu/mshyperv.c
+@@ -440,7 +440,7 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
+ static void __init ms_hyperv_init_platform(void)
+ {
+-      int hv_max_functions_eax;
++      int hv_max_functions_eax, eax;
+ #ifdef CONFIG_PARAVIRT
+       pv_info.name = "Hyper-V";
+@@ -475,6 +475,19 @@ static void __init ms_hyperv_init_platform(void)
+               pr_info("Hyper-V: running on a nested hypervisor\n");
+       }
++      /*
++       * There is no check against the max function for HYPERV_CPUID_VIRT_STACK_* CPUID
++       * leaves as the hypervisor doesn't handle them. Even a nested root partition (L2
++       * root) will not get them because the nested (L1) hypervisor filters them out.
++       * These are handled through intercept processing by the Windows Hyper-V stack
++       * or the paravisor.
++       */
++      eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES);
++      ms_hyperv.confidential_vmbus_available =
++              eax & HYPERV_VS_PROPERTIES_EAX_CONFIDENTIAL_VMBUS_AVAILABLE;
++      ms_hyperv.msi_ext_dest_id =
++              eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE;
++
+       if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS &&
+           ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) {
+               x86_platform.calibrate_tsc = hv_get_tsc_khz;
+@@ -675,21 +688,10 @@ static bool __init ms_hyperv_x2apic_available(void)
+  * pci-hyperv host bridge.
+  *
+  * Note: for a Hyper-V root partition, this will always return false.
+- * The hypervisor doesn't expose these HYPERV_CPUID_VIRT_STACK_* cpuids by
+- * default, they are implemented as intercepts by the Windows Hyper-V stack.
+- * Even a nested root partition (L2 root) will not get them because the
+- * nested (L1) hypervisor filters them out.
+  */
+ static bool __init ms_hyperv_msi_ext_dest_id(void)
+ {
+-      u32 eax;
+-
+-      eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_INTERFACE);
+-      if (eax != HYPERV_VS_INTERFACE_EAX_SIGNATURE)
+-              return false;
+-
+-      eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES);
+-      return eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE;
++      return ms_hyperv.msi_ext_dest_id;
+ }
+ #ifdef CONFIG_AMD_MEM_ENCRYPT
+diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
+index b89c7e3a20474..8862f77241e6c 100644
+--- a/include/asm-generic/mshyperv.h
++++ b/include/asm-generic/mshyperv.h
+@@ -62,6 +62,8 @@ struct ms_hyperv_info {
+               };
+       };
+       u64 shared_gpa_boundary;
++      bool msi_ext_dest_id;
++      bool confidential_vmbus_available;
+ };
+ extern struct ms_hyperv_info ms_hyperv;
+ extern bool hv_nested;
+-- 
+2.53.0
+
diff --git a/queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..8720eb6
--- /dev/null
@@ -0,0 +1,61 @@
+From 024311e871e29dfd371fc72bff1deb4cd403b792 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index 5aff5a459a433..830caf62d0fa8 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2390,8 +2390,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..c4e2a77
--- /dev/null
@@ -0,0 +1,61 @@
+From d110519c67fc766c93f2d053a5f3977d1aea0122 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index cfb63fe69267b..ad2587d2df819 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch b/queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch
new file mode 100644 (file)
index 0000000..e7419a2
--- /dev/null
@@ -0,0 +1,39 @@
+From 52c7c4ba5d8a3de9528fa5862ab4d0417eb8d6a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 11:35:00 +0100
+Subject: ASoC: SDCA: Ensure that Control Range is large enough for header
+
+From: Charles Keepax <ckeepax@opensource.cirrus.com>
+
+[ Upstream commit 951e921b039b793bef7050eaf5c5fb1a4a5341d1 ]
+
+When reading the Ranges structure from an SDCA Control, ensure that the
+read data is large enough to encompass the required header before
+accessing it.
+
+Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures")
+Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
+Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sdca/sdca_functions.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
+index 4417278e39bb1..e0cae47641ba5 100644
+--- a/sound/soc/sdca/sdca_functions.c
++++ b/sound/soc/sdca/sdca_functions.c
+@@ -849,6 +849,8 @@ static int find_sdca_control_range(struct device *dev,
+               return 0;
+       else if (num_range < 0)
+               return num_range;
++      else if (num_range < 2 * sizeof(*limits))
++              return -EINVAL;
+       range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL);
+       if (!range_list)
+-- 
+2.53.0
+
diff --git a/queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch b/queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch
new file mode 100644 (file)
index 0000000..bf70b45
--- /dev/null
@@ -0,0 +1,44 @@
+From d81792e8dd5f471d3ad6bc74803c0b214676347f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:16:16 +0800
+Subject: ASoC: tas2781: Use correct calibration data for SINEGAIN2 register
+
+From: wangdicheng <wangdicheng@kylinos.cn>
+
+[ Upstream commit dd88cf6273de61f2f7206c2066af97798dbb38b0 ]
+
+The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[]
+rather than t->sin_gn2[], causing the second pilot tone gain
+calibration to be programmed with the wrong register address.
+
+These are distinct fields in struct fct_param_address and are
+populated from separate firmware parameters by the parser in
+tas2781-fmwlib.c.
+
+Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies")
+Signed-off-by: wangdicheng <wangdicheng@kylinos.cn>
+Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/tas2781-i2c.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
+index a3b4d2c3b4789..0b83446da45ea 100644
+--- a/sound/soc/codecs/tas2781-i2c.c
++++ b/sound/soc/codecs/tas2781-i2c.c
+@@ -1303,8 +1303,8 @@ static void cali_reg_update(struct bulk_reg_val *p,
+                               t->sin_gn[2]);
+                       break;
+               case TAS2781_PRM_SINEGAIN2_REG:
+-                      reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+-                              t->sin_gn[2]);
++                      reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1],
++                              t->sin_gn2[2]);
+                       break;
+               default:
+                       reg = 0;
+-- 
+2.53.0
+
diff --git a/queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..0bf5589
--- /dev/null
@@ -0,0 +1,66 @@
+From 20413f4c0b0c345697994a9dbbc5801db6a4de59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index 388e656ac9743..01619e88a52c9 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
new file mode 100644 (file)
index 0000000..acfdb7f
--- /dev/null
@@ -0,0 +1,76 @@
+From 96cb7b4470fdcafe5b7be1243f34b401a2f086d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 23:55:26 +0530
+Subject: ata: ahci_ceva: fix error paths in
+ ceva_ahci_platform_enable_resources()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
+
+On phy_init() failure the error path fallsthrough to disable_rsts, which
+deasserts the controller reset and then enters disable_phys calling
+phy_power_off() on PHYs that were never powered on. That corrupts the PHY
+power_count and triggers an extra runtime PM put.
+
+Use a separate exit_phys path that unwinds with phy_exit() only and falls
+through to disable_clks while the controller remains in reset.  Reserve
+phy_power_off() for the phy_power_on() failure path only, and skip
+masked-out ports in both unwind loops.
+
+On phy_power_on() failure re-assert the controller reset before disabling
+clocks and regulators, matching the teardown order used by
+ahci_platform_enable_resources() and ahci_platform_disable_resources().
+
+Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_ceva.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 2d6a08c23d6ad..2961e53288f42 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+-                      goto disable_rsts;
++                      goto exit_phys;
+       }
+       /* De-assert the controller reset */
+@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       return 0;
+-disable_rsts:
+-      ahci_platform_deassert_rsts(hpriv);
+-
+ disable_phys:
+       while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
++      ahci_platform_assert_rsts(hpriv);
++      goto disable_clks;
++
++exit_phys:
++      while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
++              phy_exit(hpriv->phys[i]);
++      }
+ disable_clks:
+       ahci_platform_disable_clks(hpriv);
+-- 
+2.53.0
+
diff --git a/queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
new file mode 100644 (file)
index 0000000..8fdea02
--- /dev/null
@@ -0,0 +1,44 @@
+From 32285b5e143e6b1f6875414ec702cde179e5b961 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 15:31:37 -0700
+Subject: ata: sata_mv: accept 1 or 2 resources in platform probe
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
+
+Board files in arch/arm/plat-orion, arch/arm/mach-dove,
+arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the
+"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ).
+Those devices are rejected with -EINVAL, so SATA no longer probes on
+legacy Marvell Orion/Kirkwood-style boards.
+
+Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2
+resources (legacy, IRQ supplied as a second resource) so both probing
+paths work.
+
+Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone")
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/sata_mv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
+index ffb396f61731f..1d8ca95fb7328 100644
+--- a/drivers/ata/sata_mv.c
++++ b/drivers/ata/sata_mv.c
+@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
+       /*
+        * Simple resource validation ..
+        */
+-      if (unlikely(pdev->num_resources != 1)) {
++      if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
+               dev_err(&pdev->dev, "invalid number of resources\n");
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch
new file mode 100644 (file)
index 0000000..216e5df
--- /dev/null
@@ -0,0 +1,40 @@
+From b3d9264ff081068e5cc9f047108cfbbbce389adc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 01:54:40 -0700
+Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV
+
+From: Zijun Hu <zijun.hu@oss.qualcomm.com>
+
+[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ]
+
+btintel_diagnostics() accesses tlv->val[0] without first validating
+that the diagnostics VSE is long enough to contain that field, so
+may cause reading data beyond the received frame.
+
+Fix by validating the length before access.
+
+Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support")
+Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bluetooth/btintel.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
+index 5e0a05edcbfd1..4d6b6bbb4267e 100644
+--- a/drivers/bluetooth/btintel.c
++++ b/drivers/bluetooth/btintel.c
+@@ -3694,6 +3694,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+       struct intel_tlv *tlv = (void *)&skb->data[5];
++      if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
++              goto recv_frame;
++
+       /* The first event is always an event type TLV */
+       if (tlv->type != INTEL_TLV_TYPE_ID)
+               goto recv_frame;
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-hci-add-initial-support-for-past.patch b/queue-6.18/bluetooth-hci-add-initial-support-for-past.patch
new file mode 100644 (file)
index 0000000..0f8b452
--- /dev/null
@@ -0,0 +1,360 @@
+From 29c7fe784270a2206168b289853a2a4c630ad1e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Sep 2025 11:11:40 -0400
+Subject: Bluetooth: HCI: Add initial support for PAST
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ Upstream commit 33b2835f0b7e2a458473b0e3a23b54b92108b6b0 ]
+
+This adds PAST related commands (HCI_OP_LE_PAST,
+HCI_OP_LE_PAST_SET_INFO and HCI_OP_LE_PAST_PARAMS) and events
+(HCI_EV_LE_PAST_RECEIVED) along with handling of PAST sender and
+receiver features bits including new MGMG settings (
+HCI_EV_LE_PAST_RECEIVED and MGMT_SETTING_PAST_RECEIVER) which
+userspace can use to determine if PAST is supported by the
+controller.
+
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/bluetooth/hci.h      | 54 ++++++++++++++++++++++
+ include/net/bluetooth/hci_core.h | 12 +++++
+ include/net/bluetooth/mgmt.h     |  2 +
+ net/bluetooth/hci_event.c        | 79 ++++++++++++++++++++++++++++----
+ net/bluetooth/hci_sync.c         |  3 ++
+ net/bluetooth/iso.c              | 25 ++++++++++
+ net/bluetooth/mgmt.c             | 12 +++++
+ 7 files changed, 177 insertions(+), 10 deletions(-)
+
+diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
+index 2613cfe5b8a39..87b3f99e6ebf1 100644
+--- a/include/net/bluetooth/hci.h
++++ b/include/net/bluetooth/hci.h
+@@ -647,6 +647,8 @@ enum {
+ #define HCI_LE_EXT_ADV                        0x10
+ #define HCI_LE_PERIODIC_ADV           0x20
+ #define HCI_LE_CHAN_SEL_ALG2          0x40
++#define HCI_LE_PAST_SENDER            0x01
++#define HCI_LE_PAST_RECEIVER          0x02
+ #define HCI_LE_CIS_CENTRAL            0x10
+ #define HCI_LE_CIS_PERIPHERAL         0x20
+ #define HCI_LE_ISO_BROADCASTER                0x40
+@@ -2068,6 +2070,44 @@ struct hci_cp_le_set_privacy_mode {
+       __u8  mode;
+ } __packed;
++#define HCI_OP_LE_PAST                        0x205a
++struct hci_cp_le_past {
++      __le16 handle;
++      __le16 service_data;
++      __le16 sync_handle;
++} __packed;
++
++struct hci_rp_le_past {
++      __u8   status;
++      __le16 handle;
++} __packed;
++
++#define HCI_OP_LE_PAST_SET_INFO               0x205b
++struct hci_cp_le_past_set_info {
++      __le16 handle;
++      __le16 service_data;
++      __u8   adv_handle;
++} __packed;
++
++struct hci_rp_le_past_set_info {
++      __u8   status;
++      __le16 handle;
++} __packed;
++
++#define HCI_OP_LE_PAST_PARAMS         0x205c
++struct hci_cp_le_past_params {
++      __le16  handle;
++      __u8    mode;
++      __le16  skip;
++      __le16  sync_timeout;
++      __u8    cte_type;
++} __packed;
++
++struct hci_rp_le_past_params {
++      __u8   status;
++      __le16 handle;
++} __packed;
++
+ #define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
+ struct hci_rp_le_read_buffer_size_v2 {
+       __u8    status;
+@@ -2800,6 +2840,20 @@ struct hci_evt_le_ext_adv_set_term {
+       __u8    num_evts;
+ } __packed;
++#define HCI_EV_LE_PAST_RECEIVED               0x18
++struct hci_ev_le_past_received {
++      __u8   status;
++      __le16 handle;
++      __le16 service_data;
++      __le16 sync_handle;
++      __u8   sid;
++      __u8   bdaddr_type;
++      bdaddr_t  bdaddr;
++      __u8   phy;
++      __le16 interval;
++      __u8   clock_accuracy;
++} __packed;
++
+ #define HCI_EVT_LE_CIS_ESTABLISHED    0x19
+ struct hci_evt_le_cis_established {
+       __u8  status;
+diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
+index b5640980fbf4e..a08d8910ba265 100644
+--- a/include/net/bluetooth/hci_core.h
++++ b/include/net/bluetooth/hci_core.h
+@@ -2054,6 +2054,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
+ #define sync_recv_capable(dev) \
+       ((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
+ #define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev))
++#define past_sender_capable(dev) \
++      ((dev)->le_features[3] & HCI_LE_PAST_SENDER)
++#define past_receiver_capable(dev) \
++      ((dev)->le_features[3] & HCI_LE_PAST_RECEIVER)
++#define past_capable(dev) \
++      (past_sender_capable(dev) || past_receiver_capable(dev))
++#define past_sender_enabled(dev) \
++      (le_enabled(dev) && past_sender_capable(dev))
++#define past_receiver_enabled(dev) \
++      (le_enabled(dev) && past_receiver_capable(dev))
++#define past_enabled(dev) \
++      (past_sender_enabled(dev) || past_receiver_enabled(dev))
+ #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
+       (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
+diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
+index f5be96f08b9d9..8234915854b68 100644
+--- a/include/net/bluetooth/mgmt.h
++++ b/include/net/bluetooth/mgmt.h
+@@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list {
+ #define MGMT_SETTING_ISO_BROADCASTER  BIT(20)
+ #define MGMT_SETTING_ISO_SYNC_RECEIVER        BIT(21)
+ #define MGMT_SETTING_LL_PRIVACY               BIT(22)
++#define MGMT_SETTING_PAST_SENDER      BIT(23)
++#define MGMT_SETTING_PAST_RECEIVER    BIT(24)
+ #define MGMT_OP_READ_INFO             0x0004
+ #define MGMT_READ_INFO_SIZE           0
+diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
+index 396aafa609c9a..ce89f7cbebc7d 100644
+--- a/net/bluetooth/hci_event.c
++++ b/net/bluetooth/hci_event.c
+@@ -5950,6 +5950,71 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data,
+       hci_dev_unlock(hdev);
+ }
++static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
++{
++      struct hci_cp_le_pa_term_sync cp;
++
++      memset(&cp, 0, sizeof(cp));
++      cp.handle = handle;
++
++      return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
++}
++
++static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
++                                   struct sk_buff *skb)
++{
++      struct hci_ev_le_past_received *ev = data;
++      int mask = hdev->link_mode;
++      __u8 flags = 0;
++      struct hci_conn *pa_sync, *conn;
++
++      bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
++
++      hci_dev_lock(hdev);
++
++      hci_dev_clear_flag(hdev, HCI_PA_SYNC);
++
++      conn = hci_conn_hash_lookup_create_pa_sync(hdev);
++      if (!conn) {
++              bt_dev_err(hdev,
++                         "Unable to find connection for dst %pMR sid 0x%2.2x",
++                         &ev->bdaddr, ev->sid);
++              goto unlock;
++      }
++
++      conn->sync_handle = le16_to_cpu(ev->sync_handle);
++      conn->sid = HCI_SID_INVALID;
++
++      mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK,
++                                    &flags);
++      if (!(mask & HCI_LM_ACCEPT)) {
++              hci_le_pa_term_sync(hdev, ev->sync_handle);
++              goto unlock;
++      }
++
++      if (!(flags & HCI_PROTO_DEFER))
++              goto unlock;
++
++      /* Add connection to indicate PA sync event */
++      pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
++                                   HCI_ROLE_SLAVE);
++
++      if (IS_ERR(pa_sync))
++              goto unlock;
++
++      pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
++
++      if (ev->status) {
++              set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
++
++              /* Notify iso layer */
++              hci_connect_cfm(pa_sync, ev->status);
++      }
++
++unlock:
++      hci_dev_unlock(hdev);
++}
++
+ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data,
+                                           struct sk_buff *skb)
+ {
+@@ -6426,16 +6491,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
+       hci_dev_unlock(hdev);
+ }
+-static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
+-{
+-      struct hci_cp_le_pa_term_sync cp;
+-
+-      memset(&cp, 0, sizeof(cp));
+-      cp.handle = handle;
+-
+-      return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
+-}
+-
+ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
+                                          struct sk_buff *skb)
+ {
+@@ -7250,6 +7305,10 @@ static const struct hci_le_ev {
+       /* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
+       HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
+                 sizeof(struct hci_evt_le_ext_adv_set_term)),
++      /* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */
++      HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED,
++                hci_le_past_received_evt,
++                sizeof(struct hci_ev_le_past_received)),
+       /* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
+       HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt,
+                 sizeof(struct hci_evt_le_cis_established)),
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 65c8a490c5553..87a2502fccd6e 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -4450,6 +4450,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
+       if (ext_adv_capable(hdev))
+               events[2] |= 0x02;      /* LE Advertising Set Terminated */
++      if (past_receiver_capable(hdev))
++              events[2] |= 0x80;      /* LE PAST Received */
++
+       if (cis_capable(hdev)) {
+               events[3] |= 0x01;      /* LE CIS Established */
+               if (cis_peripheral_capable(hdev))
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 4ea41b093ac65..f65e77f7e0349 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -80,6 +80,7 @@ static struct bt_iso_qos default_qos;
+ static bool check_ucast_qos(struct bt_iso_qos *qos);
+ static bool check_bcast_qos(struct bt_iso_qos *qos);
+ static bool iso_match_sid(struct sock *sk, void *data);
++static bool iso_match_sid_past(struct sock *sk, void *data);
+ static bool iso_match_sync_handle(struct sock *sk, void *data);
+ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data);
+ static void iso_sock_disconn(struct sock *sk);
+@@ -2147,6 +2148,16 @@ static bool iso_match_sid(struct sock *sk, void *data)
+       return ev->sid == iso_pi(sk)->bc_sid;
+ }
++static bool iso_match_sid_past(struct sock *sk, void *data)
++{
++      struct hci_ev_le_past_received *ev = data;
++
++      if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
++              return true;
++
++      return ev->sid == iso_pi(sk)->bc_sid;
++}
++
+ static bool iso_match_sync_handle(struct sock *sk, void *data)
+ {
+       struct hci_evt_le_big_info_adv_report *ev = data;
+@@ -2166,6 +2177,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data)
+ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+ {
+       struct hci_ev_le_pa_sync_established *ev1;
++      struct hci_ev_le_past_received *ev1a;
+       struct hci_evt_le_big_info_adv_report *ev2;
+       struct hci_ev_le_per_adv_report *ev3;
+       struct sock *sk;
+@@ -2179,6 +2191,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+        * SID to listen to and once sync is established its handle needs to
+        * be stored in iso_pi(sk)->sync_handle so it can be matched once
+        * receiving the BIG Info.
++       * 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1.
+        * 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a
+        * a BIG Info it attempts to check if there any listening socket with
+        * the same sync_handle and if it does then attempt to create a sync.
+@@ -2198,6 +2211,18 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+               goto done;
+       }
++      ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED);
++      if (ev1a) {
++              sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
++                                iso_match_sid_past, ev1a);
++              if (sk && !ev1a->status) {
++                      iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
++                      iso_pi(sk)->bc_sid = ev1a->sid;
++              }
++
++              goto done;
++      }
++
+       ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
+       if (ev2) {
+               /* Check if BIGInfo report has already been handled */
+diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
+index 79eb605be2804..424e7980f32e1 100644
+--- a/net/bluetooth/mgmt.c
++++ b/net/bluetooth/mgmt.c
+@@ -858,6 +858,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
+       if (ll_privacy_capable(hdev))
+               settings |= MGMT_SETTING_LL_PRIVACY;
++      if (past_sender_capable(hdev))
++              settings |= MGMT_SETTING_PAST_SENDER;
++
++      if (past_receiver_capable(hdev))
++              settings |= MGMT_SETTING_PAST_RECEIVER;
++
+       settings |= MGMT_SETTING_PHY_CONFIGURATION;
+       return settings;
+@@ -943,6 +949,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
+       if (ll_privacy_enabled(hdev))
+               settings |= MGMT_SETTING_LL_PRIVACY;
++      if (past_sender_enabled(hdev))
++              settings |= MGMT_SETTING_PAST_SENDER;
++
++      if (past_receiver_enabled(hdev))
++              settings |= MGMT_SETTING_PAST_RECEIVER;
++
+       return settings;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
new file mode 100644 (file)
index 0000000..c7375c8
--- /dev/null
@@ -0,0 +1,55 @@
+From 6e4b00bb5110ee13409ae2b9380b26a0fc0293e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:17 +0300
+Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that.
+
+Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index f485cffe35735..e630d6369a0c3 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2965,6 +2965,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+       return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
+ }
++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
++{
++      struct hci_conn *conn = data;
++
++      hci_conn_put(conn);
++}
++
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
+@@ -2990,7 +2997,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
++                                  abort_conn_destroy);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
new file mode 100644 (file)
index 0000000..e2d93f4
--- /dev/null
@@ -0,0 +1,43 @@
+From a41d539ab4ccff78007239708e9726dadd71e06d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:22 +0300
+Subject: Bluetooth: hci_sync: fix hci_conn_del() use in
+ hci_le_create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ]
+
+hci_conn_del() caller must hold hdev->lock, check the conn was not
+concurrently deleted, and usually inform socket the conn is going to be
+deleted.
+
+Use hci_abort_conn_sync() instead of calling hci_conn_del() without
+locks etc.
+
+Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index ca002269306c4..27c5cc1457e65 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6620,7 +6620,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE &&
+                   !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
+-                      hci_conn_del(conn);
++                      conn->state = BT_OPEN;
++                      hci_abort_conn_sync(hdev, conn,
++                                          HCI_ERROR_REJ_LIMITED_RESOURCES);
+                       hci_conn_put(conn);
+                       return -EBUSY;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
new file mode 100644 (file)
index 0000000..c86e898
--- /dev/null
@@ -0,0 +1,66 @@
+From c2de8c52056eba0decf5453412579e887198315a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 25 Mar 2026 21:07:45 +0200
+Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if
+ exists
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ]
+
+hci_cmd_sync_run_once() needs to indicate whether a queue item was
+added, so caller can know if callbacks are called, so it can avoid
+leaking resources.
+
+Change the function to return -EEXIST if queue item already exists.
+
+Modify all callsites vs. the changes.  The only callsite is
+hci_abort_conn().
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 4 +++-
+ net/bluetooth/hci_sync.c | 2 +-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index ceeb19158a36c..f485cffe35735 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2968,6 +2968,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
++      int err;
+       /* If abort_reason has already been set it means the connection is
+        * already being aborted so don't attempt to overwrite it.
+@@ -2989,7 +2990,8 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      return (err == -EEXIST) ? 0 : err;
+ }
+ void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 87a2502fccd6e..ca002269306c4 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -825,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                         void *data, hci_cmd_sync_work_destroy_t destroy)
+ {
+       if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
+-              return 0;
++              return -EEXIST;
+       return hci_cmd_sync_run(hdev, func, data, destroy);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
new file mode 100644 (file)
index 0000000..6494bb3
--- /dev/null
@@ -0,0 +1,80 @@
+From d2824fa429d31c0fe8d1ceb8d348496cc4bd95da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:23 +0300
+Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in
+ create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ]
+
+hci_conn_get() without already held reference is data race against
+concurrent deletion.
+
+In previous patches, the refcount has been changed to be taken before
+starting the hci_sync task, so remove these extra get() + put() as they
+are not needed.
+
+Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 27c5cc1457e65..1ba41e82c406e 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6604,11 +6604,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       bt_dev_dbg(hdev, "conn %p", conn);
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() at done.
+-       */
+-      hci_conn_get(conn);
+-
+       clear_bit(HCI_CONN_SCANNING, &conn->flags);
+       conn->state = BT_CONNECT;
+@@ -6623,7 +6618,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+                       conn->state = BT_OPEN;
+                       hci_abort_conn_sync(hdev, conn,
+                                           HCI_ERROR_REJ_LIMITED_RESOURCES);
+-                      hci_conn_put(conn);
+                       return -EBUSY;
+               }
+@@ -6717,7 +6711,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       /* Re-enable advertising after the connection attempt is finished. */
+       hci_resume_advertising_sync(hdev);
+-      hci_conn_put(conn);
+       return err;
+ }
+@@ -6992,11 +6985,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+       else
+               cp.role_switch = 0x00;
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() below.
+-       */
+-      hci_conn_get(conn);
+-
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+@@ -7008,7 +6996,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+                                      conn->conn_timeout, NULL);
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+-      hci_conn_put(conn);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
new file mode 100644 (file)
index 0000000..ec263a4
--- /dev/null
@@ -0,0 +1,188 @@
+From 3b60a2030df61f582406ed93bc0efa1445cb74ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:31 +0300
+Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ]
+
+iso_sock_timeout() takes lock_sock, so sync disabling the timer while
+holding that lock may deadlock.
+
+iso_sock_timeout() may also run concurrently with iso_conn_del(), which
+leads to UAF
+
+        [Task 1]                      [Task hdev->workqueue]
+        iso_sock_timeout              iso_conn_del
+          iso_conn_hold_unless_zero     iso_chan_del
+                           `------------> iso_conn_put
+                                      caller frees hcon
+          iso_conn_put
+            iso_conn_free
+              conn->hcon->iso_data = NULL; /* UAF */
+
+Fix the deadlock by removing the disable from the lock_sock sections.
+Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn
+which may need to be freed in lock_sock section. Convert some of the
+clear_timer to disable_timer.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 60 ++++++++++++++++++++++-----------------------
+ 1 file changed, 29 insertions(+), 31 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 001fb12ccee3b..3320be2d66b55 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -36,8 +36,6 @@ struct iso_conn {
+       spinlock_t      lock;
+       struct sock     *sk;
+-      struct delayed_work     timeout_work;
+-
+       struct sk_buff  *rx_skb;
+       __u32           rx_len;
+       __u16           tx_sn;
+@@ -80,6 +78,7 @@ struct iso_pinfo {
+       __u8                    base_len;
+       __u8                    base[BASE_MAX_LENGTH];
+       struct iso_conn         *conn;
++      struct delayed_work     timeout_work;
+ };
+ static struct bt_iso_qos default_qos;
+@@ -117,9 +116,6 @@ static void iso_conn_free(struct kref *ref)
+                       hci_conn_drop(conn->hcon);
+       }
+-      /* Ensure no more work items will run since hci_conn has been dropped */
+-      disable_delayed_work_sync(&conn->timeout_work);
+-
+       kfree_skb(conn->rx_skb);
+       kfree(conn);
+@@ -160,48 +156,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
+ static void iso_sock_timeout(struct work_struct *work)
+ {
+-      struct iso_conn *conn = container_of(work, struct iso_conn,
+-                                           timeout_work.work);
+-      struct sock *sk;
+-
+-      conn = iso_conn_hold_unless_zero(conn);
+-      if (!conn)
+-              return;
+-
+-      iso_conn_lock(conn);
+-      sk = iso_sock_hold(conn);
+-      iso_conn_unlock(conn);
+-      iso_conn_put(conn);
+-
+-      if (!sk)
+-              return;
++      struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
++                                          timeout_work.work);
++      struct sock *sk = &pi->bt.sk;
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+       lock_sock(sk);
+-      sk->sk_err = ETIMEDOUT;
+-      sk->sk_state_change(sk);
++      if (!sock_flag(sk, SOCK_ZAPPED)) {
++              sk->sk_err = ETIMEDOUT;
++              sk->sk_state_change(sk);
++      }
+       release_sock(sk);
+-      sock_put(sk);
+ }
+ static void iso_sock_set_timer(struct sock *sk, long timeout)
+ {
++      lockdep_assert(lockdep_sock_is_held(sk));
++
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++
+       if (!iso_pi(sk)->conn)
+               return;
+       BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
+-      schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
++      schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
+ }
+ static void iso_sock_clear_timer(struct sock *sk)
+ {
+-      if (!iso_pi(sk)->conn)
+-              return;
++      BT_DBG("sock %p state %d", sk, sk->sk_state);
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++}
++
++static void iso_sock_disable_timer(struct sock *sk)
++{
++      lockdep_assert(!lockdep_sock_is_held(sk));
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
++      disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
+ }
+ /* ---- ISO connections ---- */
+@@ -226,7 +219,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+       kref_init(&conn->ref);
+       spin_lock_init(&conn->lock);
+-      INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
+       hcon->iso_data = conn;
+       conn->hcon = hcon;
+@@ -291,8 +283,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+               return;
+       }
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
+       iso_sock_kill(sk);
+@@ -782,6 +775,8 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+@@ -877,8 +872,9 @@ static void __iso_sock_close(struct sock *sk)
+ /* Must be called on unlocked socket. */
+ static void iso_sock_close(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+ }
+@@ -947,6 +943,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
+       iso_pi(sk)->qos = default_qos;
+       iso_pi(sk)->sync_handle = -1;
++      INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
++
+       bt_sock_link(&iso_sk_list, sk);
+       return sk;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
new file mode 100644 (file)
index 0000000..609bfdd
--- /dev/null
@@ -0,0 +1,40 @@
+From 243515e681f93ae1ac3bd47fcc6f090284080f66 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 17:53:33 +0300
+Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ]
+
+When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is
+necessary, otherwise later iso_conn_free() will UAF.
+
+Fix clearing of iso_data in iso_sock_disconn()
+
+Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on
+iso_sock_release() followed by hci_abort_conn_sync().
+
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index f2ff10ae76e22..5868ad1f7128e 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -826,6 +826,7 @@ static void iso_sock_disconn(struct sock *sk)
+       sk->sk_state = BT_DISCONN;
+       iso_conn_lock(iso_pi(sk)->conn);
+       hci_conn_drop(iso_pi(sk)->conn->hcon);
++      iso_pi(sk)->conn->hcon->iso_data = NULL;
+       iso_pi(sk)->conn->hcon = NULL;
+       iso_conn_unlock(iso_pi(sk)->conn);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch b/queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch
new file mode 100644 (file)
index 0000000..4d7d1bf
--- /dev/null
@@ -0,0 +1,109 @@
+From 1a5b060db1b91ead2015af719ea79075f4dabd7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:32 +0300
+Subject: Bluetooth: ISO: ensure no dangling hcon references in iso_conn
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb ]
+
+After iso_conn_del(), ISO sockets should not dereference the hcon any
+more.  Currently, clearing iso_conn::hcon relies on iso_conn_del()
+releasing the last reference to the iso_conn.
+
+Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to
+avoid more complex reasoning on races about who holds the last
+reference.
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: fdfde532ab1c ("Bluetooth: ISO: fix refcounting of iso_conn")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 24 +++++++++++++++++++++---
+ 1 file changed, 21 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 3320be2d66b55..b21627b8e958b 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -262,6 +262,7 @@ static void iso_chan_del(struct sock *sk, int err)
+ }
+ static void iso_conn_del(struct hci_conn *hcon, int err)
++      __must_hold(&hcon->hdev->lock)
+ {
+       struct iso_conn *conn = hcon->iso_data;
+       struct sock *sk;
+@@ -276,11 +277,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       iso_conn_lock(conn);
+       sk = iso_sock_hold(conn);
+       iso_conn_unlock(conn);
+-      iso_conn_put(conn);
+       if (!sk) {
+               iso_conn_put(conn);
+-              return;
++              goto done;
+       }
+       iso_sock_disable_timer(sk);
+@@ -290,6 +290,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       release_sock(sk);
+       iso_sock_kill(sk);
+       sock_put(sk);
++
++done:
++      /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
++      iso_conn_lock(conn);
++      conn->hcon = NULL;
++      hcon->iso_data = NULL;
++      iso_conn_unlock(conn);
++
++      iso_conn_put(conn);
+ }
+ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+@@ -305,6 +314,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+               return -EBUSY;
+       }
++      if (!conn->hcon) {
++              BT_ERR("conn->hcon missing");
++              return -EIO;
++      }
++
+       iso_pi(sk)->conn = conn;
+       conn->sk = sk;
+       clear_bit(ISO_CONN_DROPPED, conn->flags);
+@@ -2410,6 +2424,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+ }
+ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
++      __must_hold(&hcon->hdev->lock)
+ {
+       if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
+           hcon->type != PA_LINK) {
+@@ -2421,8 +2436,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+                       struct hci_link *link, *t;
+                       list_for_each_entry_safe(link, t, &hcon->link_list,
+-                                               list)
++                                               list) {
++                              lockdep_assert_held(&link->conn->hdev->lock);
+                               iso_conn_del(link->conn, bt_to_errno(status));
++                      }
+                       return;
+               }
+@@ -2452,6 +2469,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+ }
+ static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
++      __must_hold(&hcon->hdev->lock)
+ {
+       if (hcon->type != CIS_LINK && hcon->type !=  BIS_LINK &&
+           hcon->type != PA_LINK)
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch b/queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch
new file mode 100644 (file)
index 0000000..7fcd139
--- /dev/null
@@ -0,0 +1,108 @@
+From 4721856be86871cdf6640d7ef4dfa82c7e0e95ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:24 +0300
+Subject: Bluetooth: ISO: fix CONNECTED -> CLOSED transition on
+ shutdown/release
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 0786469ee242952008628ed0e2d386098e2065ab ]
+
+Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
+merged a version of the UAF fix that breaks releasing connected
+ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't
+be called when the hci_conn disconnects, and the ISO socket does not emit
+POLLHUP correctly.
+
+Fix by retaining full hci_conn <-> iso_conn association while in
+BT_DISCONNECT state, so that local disconnect via shutdown() follows
+similar ISO socket code path as remote disconnect.  Use a separate flag
+to track whether hci_conn_drop() is needed, instead of setting
+iso_conn::hcon = NULL
+
+In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED,
+in case hcon connects while its drop is pending.
+
+Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 26 ++++++++++++++++++++------
+ 1 file changed, 20 insertions(+), 6 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 0d2d9c07bf66b..fb77a7e22a9dd 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = {
+ };
+ /* ---- ISO connections ---- */
++enum {
++      ISO_CONN_DROPPED,
++      __ISO_CONN_NUM_FLAGS
++};
++
+ struct iso_conn {
+       struct hci_conn *hcon;
++      DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
+       /* @lock: spinlock protecting changes to iso_conn fields */
+       spinlock_t      lock;
+@@ -106,7 +112,8 @@ static void iso_conn_free(struct kref *ref)
+       if (conn->hcon) {
+               conn->hcon->iso_data = NULL;
+-              hci_conn_drop(conn->hcon);
++              if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
++                      hci_conn_drop(conn->hcon);
+       }
+       /* Ensure no more work items will run since hci_conn has been dropped */
+@@ -305,6 +312,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+       iso_pi(sk)->conn = conn;
+       conn->sk = sk;
++      clear_bit(ISO_CONN_DROPPED, conn->flags);
+       if (parent)
+               bt_accept_enqueue(parent, sk, true);
+@@ -817,11 +825,8 @@ static void iso_sock_disconn(struct sock *sk)
+       }
+       sk->sk_state = BT_DISCONN;
+-      iso_conn_lock(iso_pi(sk)->conn);
+-      hci_conn_drop(iso_pi(sk)->conn->hcon);
+-      iso_pi(sk)->conn->hcon->iso_data = NULL;
+-      iso_pi(sk)->conn->hcon = NULL;
+-      iso_conn_unlock(iso_pi(sk)->conn);
++      if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
++              hci_conn_drop(iso_pi(sk)->conn->hcon);
+ }
+ static void __iso_sock_close(struct sock *sk)
+@@ -1964,9 +1969,18 @@ static void iso_sock_ready(struct sock *sk)
+               return;
+       lock_sock(sk);
++
++      switch (sk->sk_state) {
++      case BT_DISCONN:
++      case BT_CLOSED:
++              release_sock(sk);
++              return;
++      }
++
+       iso_sock_clear_timer(sk);
+       sk->sk_state = BT_CONNECTED;
+       sk->sk_state_change(sk);
++
+       release_sock(sk);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch b/queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch
new file mode 100644 (file)
index 0000000..ad5f03f
--- /dev/null
@@ -0,0 +1,227 @@
+From d6b822f0cbc1d4af1b4dc35de9a7012fca6d7837 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Apr 2026 11:51:22 +0900
+Subject: Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event
+ paths
+
+From: SeungJu Cheon <suunj1331@gmail.com>
+
+[ Upstream commit f958c7805b18e9d69f6b322b231ecee46ec6f331 ]
+
+Several iso_pi(sk) fields (qos, qos_user_set, bc_sid, base, base_len,
+sync_handle, bc_num_bis) are written under lock_sock in
+iso_sock_setsockopt() and iso_sock_bind(), but read and written under
+hci_dev_lock only in two other paths:
+
+  - iso_connect_bis() / iso_connect_cis(), invoked from connect(2),
+    read qos/base/bc_sid and reset qos to default_qos on the
+    qos_user_set validation failure -- all without lock_sock.
+
+  - iso_connect_ind(), invoked from hci_rx_work, writes sync_handle,
+    bc_sid, qos.bcast.encryption, bc_num_bis, base and base_len on
+    PA_SYNC_ESTABLISHED / PAST_RECEIVED / BIG_INFO_ADV_REPORT /
+    PER_ADV_REPORT events. The BIG_INFO handler additionally passes
+    &iso_pi(sk)->qos together with sync_handle / bc_num_bis / bc_bis
+    to hci_conn_big_create_sync() while setsockopt may be mutating
+    them.
+
+Acquire lock_sock around the affected accesses in both paths.
+
+The locking order hci_dev_lock -> lock_sock matches the existing
+iso_conn_big_sync() precedent, whose comment documents the same
+requirement for hci_conn_big_create_sync(). The HCI connect/bind
+helpers do not wait for command completion -- they enqueue work via
+hci_cmd_sync_queue{,_once}() / hci_le_create_cis_pending() and
+return -- so the added hold time is comparable to iso_conn_big_sync().
+
+KCSAN report:
+
+BUG: KCSAN: data-race in iso_connect_cis / iso_sock_setsockopt
+
+read to 0xffffa3ae8ce3cdc8 of 1 bytes by task 335 on cpu 0:
+ iso_connect_cis+0x49f/0xa20
+ iso_sock_connect+0x60e/0xb40
+ __sys_connect_file+0xbd/0xe0
+ __sys_connect+0xe0/0x110
+ __x64_sys_connect+0x40/0x50
+ x64_sys_call+0xcad/0x1c60
+ do_syscall_64+0x133/0x590
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+write to 0xffffa3ae8ce3cdc8 of 60 bytes by task 334 on cpu 1:
+ iso_sock_setsockopt+0x69a/0x930
+ do_sock_setsockopt+0xc3/0x170
+ __sys_setsockopt+0xd1/0x130
+ __x64_sys_setsockopt+0x64/0x80
+ x64_sys_call+0x1547/0x1c60
+ do_syscall_64+0x133/0x590
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Reported by Kernel Concurrency Sanitizer on:
+CPU: 1 UID: 0 PID: 334 Comm: iso_setup_race Not tainted 7.0.0-10949-g8541d8f725c6 #44 PREEMPT(lazy)
+
+The iso_connect_ind() races were found by inspection.
+
+Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
+Signed-off-by: SeungJu Cheon <suunj1331@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 54 +++++++++++++++++++++++++--------------------
+ 1 file changed, 30 insertions(+), 24 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index f65e77f7e0349..4377fe6cf14df 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -355,6 +355,7 @@ static int iso_connect_bis(struct sock *sk)
+               return -EHOSTUNREACH;
+       hci_dev_lock(hdev);
++      lock_sock(sk);
+       if (!bis_capable(hdev)) {
+               err = -EOPNOTSUPP;
+@@ -407,13 +408,9 @@ static int iso_connect_bis(struct sock *sk)
+               goto unlock;
+       }
+-      lock_sock(sk);
+-
+       err = iso_chan_add(conn, sk, NULL);
+-      if (err) {
+-              release_sock(sk);
++      if (err)
+               goto unlock;
+-      }
+       /* Update source addr of the socket */
+       bacpy(&iso_pi(sk)->src, &hcon->src);
+@@ -429,9 +426,8 @@ static int iso_connect_bis(struct sock *sk)
+               iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
+       }
+-      release_sock(sk);
+-
+ unlock:
++      release_sock(sk);
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+       return err;
+@@ -459,6 +455,7 @@ static int iso_connect_cis(struct sock *sk)
+               return -EHOSTUNREACH;
+       hci_dev_lock(hdev);
++      lock_sock(sk);
+       if (!cis_central_capable(hdev)) {
+               err = -EOPNOTSUPP;
+@@ -513,13 +510,9 @@ static int iso_connect_cis(struct sock *sk)
+               goto unlock;
+       }
+-      lock_sock(sk);
+-
+       err = iso_chan_add(conn, sk, NULL);
+-      if (err) {
+-              release_sock(sk);
++      if (err)
+               goto unlock;
+-      }
+       /* Update source addr of the socket */
+       bacpy(&iso_pi(sk)->src, &hcon->src);
+@@ -535,9 +528,8 @@ static int iso_connect_cis(struct sock *sk)
+               iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
+       }
+-      release_sock(sk);
+-
+ unlock:
++      release_sock(sk);
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+       return err;
+@@ -2204,8 +2196,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+               sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
+                                 iso_match_sid, ev1);
+               if (sk && !ev1->status) {
++                      lock_sock(sk);
+                       iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
+                       iso_pi(sk)->bc_sid = ev1->sid;
++                      release_sock(sk);
+               }
+               goto done;
+@@ -2216,8 +2210,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+               sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
+                                 iso_match_sid_past, ev1a);
+               if (sk && !ev1a->status) {
++                      lock_sock(sk);
+                       iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
+                       iso_pi(sk)->bc_sid = ev1a->sid;
++                      release_sock(sk);
+               }
+               goto done;
+@@ -2244,27 +2240,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                                         ev2);
+               if (sk) {
+-                      int err;
+-                      struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
++                      int err = 0;
++                      bool big_sync;
++                      struct hci_conn *hcon;
++                      lock_sock(sk);
++
++                      hcon = iso_pi(sk)->conn->hcon;
+                       iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
+                       if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
+                               iso_pi(sk)->bc_num_bis = ev2->num_bis;
+-                      if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
+-                          !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
++                      big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
++                                 !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags);
++
++                      if (big_sync)
+                               err = hci_conn_big_create_sync(hdev, hcon,
+                                                              &iso_pi(sk)->qos,
+                                                              iso_pi(sk)->sync_handle,
+                                                              iso_pi(sk)->bc_num_bis,
+                                                              iso_pi(sk)->bc_bis);
+-                              if (err) {
+-                                      bt_dev_err(hdev, "hci_le_big_create_sync: %d",
+-                                                 err);
+-                                      sock_put(sk);
+-                                      sk = NULL;
+-                              }
++
++                      release_sock(sk);
++
++                      if (big_sync && err) {
++                              bt_dev_err(hdev, "hci_le_big_create_sync: %d",
++                                         err);
++                              sock_put(sk);
++                              sk = NULL;
+                       }
+               }
+@@ -2318,8 +2322,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                       if (!base || base_len > BASE_MAX_LENGTH)
+                               goto done;
++                      lock_sock(sk);
+                       memcpy(iso_pi(sk)->base, base, base_len);
+                       iso_pi(sk)->base_len = base_len;
++                      release_sock(sk);
+               } else {
+                       /* This is a PA data fragment. Keep pa_data_len set to 0
+                        * until all data has been reassembled.
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch
new file mode 100644 (file)
index 0000000..6bbb1e6
--- /dev/null
@@ -0,0 +1,116 @@
+From b52a0101085ee70fb5dac9dca65c38f4352e777a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:30 +0300
+Subject: Bluetooth: ISO: fix leaking sk after socket release
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ]
+
+iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+sock_flag(sk, SOCK_DEAD) for early return, but this is always true since
+sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket
+always leaks, iso_sock_destruct is never called.
+
+The socket reference also leaks when __iso_sock_close() does not set
+SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after
+zapping.
+
+Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for
+something else, and lock_sock to ensure iso_sock_kill() puts sk only
+after socket release only once. Release and iso_conn_del may run
+concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up
+after zapping.
+
+Remove call to iso_sock_kill() from iso_sock_close(), as it's generally
+no-op there.
+
+Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 22 ++++++++++++++++++----
+ 1 file changed, 18 insertions(+), 4 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index cb629734062a5..001fb12ccee3b 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -61,6 +61,7 @@ static void iso_sock_kill(struct sock *sk);
+ enum {
+       BT_SK_BIG_SYNC,
+       BT_SK_PA_SYNC,
++      BT_SK_KILLED,
+ };
+ struct iso_pinfo {
+@@ -294,6 +295,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
++      iso_sock_kill(sk);
+       sock_put(sk);
+ }
+@@ -780,24 +782,29 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      lock_sock(sk);
++
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+-          sock_flag(sk, SOCK_DEAD))
++          test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
++              release_sock(sk);
+               return;
++      }
+       BT_DBG("sk %p state %d", sk, sk->sk_state);
+       /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
+-      lock_sock(sk);
+       if (iso_pi(sk)->conn) {
+               iso_conn_lock(iso_pi(sk)->conn);
+               iso_pi(sk)->conn->sk = NULL;
+               iso_conn_unlock(iso_pi(sk)->conn);
+       }
+-      release_sock(sk);
+       /* Kill poor orphan */
+       bt_sock_unlink(&iso_sk_list, sk);
+       sock_set_flag(sk, SOCK_DEAD);
++      set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
++
++      release_sock(sk);
+       sock_put(sk);
+ }
+@@ -874,7 +881,6 @@ static void iso_sock_close(struct sock *sk)
+       iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+-      iso_sock_kill(sk);
+ }
+ static void iso_sock_init(struct sock *sk, struct sock *parent)
+@@ -1958,8 +1964,16 @@ static int iso_sock_release(struct socket *sock)
+               release_sock(sk);
+       }
++      /* Make sure sk is valid even if iso_conn_del() is concurrent */
++      sock_hold(sk);
++
++      lock_sock(sk);
+       sock_orphan(sk);
++      release_sock(sk);
++
+       iso_sock_kill(sk);
++
++      sock_put(sk);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch b/queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch
new file mode 100644 (file)
index 0000000..e09cabb
--- /dev/null
@@ -0,0 +1,53 @@
+From fa25ef28917ca79979482d1bb9e9a99f7d4bf592 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Oct 2025 16:53:35 -0400
+Subject: Bluetooth: ISO: Fix not updating BIS sender source address
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ Upstream commit 577cf4c0a1e8471a0d6c0f36bb3716285e27ad5e ]
+
+The source address for a BIS sender/Broadcast Source shall be updated
+with the advertisement address since in case privacy is enabled it may
+use an RPA rather than an identity address.
+
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 98fa94644b4a6..0d2d9c07bf66b 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -2001,6 +2001,25 @@ static void iso_conn_ready(struct iso_conn *conn)
+       BT_DBG("conn %p", conn);
+       if (sk) {
++              /* Attempt to update source address in case of BIS Sender if
++               * the advertisement is using a random address.
++               */
++              if (conn->hcon->type == BIS_LINK &&
++                  conn->hcon->role == HCI_ROLE_MASTER &&
++                  !bacmp(&conn->hcon->dst, BDADDR_ANY)) {
++                      struct hci_conn *bis = conn->hcon;
++                      struct adv_info *adv;
++
++                      adv = hci_find_adv_instance(bis->hdev,
++                                                  bis->iso_qos.bcast.bis);
++                      if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
++                              lock_sock(sk);
++                              iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
++                              bacpy(&iso_pi(sk)->src, &adv->random_addr);
++                              release_sock(sk);
++                      }
++              }
++
+               iso_sock_ready(conn->sk);
+       } else {
+               hcon = conn->hcon;
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch b/queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch
new file mode 100644 (file)
index 0000000..14f1378
--- /dev/null
@@ -0,0 +1,142 @@
+From ef216fe405fd74f09003255b94ddc009af2786ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:33 +0300
+Subject: Bluetooth: ISO: fix refcounting of iso_conn
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ]
+
+iso_conn_del() and iso_chan_del() have a race that results to double-put
+of iso_conn:
+
+    [Task hdev->workqueue]         [Task 2]
+    iso_conn_del                   iso_chan_del
+      iso_conn_hold_unless_zero      iso_conn_lock
+      iso_conn_lock                  conn->sk = NULL
+                                     iso_conn_unlock
+      sk = iso_sock_hold(conn)  <---------´
+      if (!sk) iso_conn_put          iso_conn_put
+      iso_conn_put /* UAF */
+
+The extra put for !sk in iso_conn_del() is currently required since
+failing iso_chan_add() may leave iso_conn not associated with any sk.
+
+Fix by having iso_pi(sk)->conn own refcount when non-NULL, so
+iso_conn_del does not need to put it.  Adjust the iso_conn_add()
+refcounting so that conn is put if it does not get associated with an
+sk.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 29 +++++++++++++++++------------
+ 1 file changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index b21627b8e958b..fcf4fd78c7cc9 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -107,9 +107,6 @@ static void iso_conn_free(struct kref *ref)
+       BT_DBG("conn %p", conn);
+-      if (conn->sk)
+-              iso_pi(conn->sk)->conn = NULL;
+-
+       if (conn->hcon) {
+               conn->hcon->iso_data = NULL;
+               if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
+@@ -144,6 +141,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
+       return conn;
+ }
++static struct iso_conn *iso_conn_hold(struct iso_conn *conn)
++{
++      BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref));
++
++      kref_get(&conn->ref);
++      return conn;
++}
++
+ static struct sock *iso_sock_hold(struct iso_conn *conn)
+ {
+       if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk))
+@@ -209,7 +214,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+                       conn->hcon = hcon;
+                       iso_conn_unlock(conn);
+               }
+-              iso_conn_put(conn);
+               return conn;
+       }
+@@ -278,10 +282,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       sk = iso_sock_hold(conn);
+       iso_conn_unlock(conn);
+-      if (!sk) {
+-              iso_conn_put(conn);
++      if (!sk)
+               goto done;
+-      }
+       iso_sock_disable_timer(sk);
+@@ -319,7 +321,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+               return -EIO;
+       }
+-      iso_pi(sk)->conn = conn;
++      iso_pi(sk)->conn = iso_conn_hold(conn);
+       conn->sk = sk;
+       clear_bit(ISO_CONN_DROPPED, conn->flags);
+@@ -426,6 +428,7 @@ static int iso_connect_bis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
++      iso_conn_put(conn);
+       if (err)
+               goto unlock;
+@@ -528,6 +531,7 @@ static int iso_connect_cis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
++      iso_conn_put(conn);
+       if (err)
+               goto unlock;
+@@ -1228,10 +1232,9 @@ static int iso_listen_bis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
+-      if (err) {
+-              hci_conn_drop(hcon);
++      iso_conn_put(conn);
++      if (err)
+               goto unlock;
+-      }
+ unlock:
+       release_sock(sk);
+@@ -2461,8 +2464,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+               struct iso_conn *conn;
+               conn = iso_conn_add(hcon);
+-              if (conn)
++              if (conn) {
+                       iso_conn_ready(conn);
++                      iso_conn_put(conn);
++              }
+       } else {
+               iso_conn_del(hcon, bt_to_errno(status));
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
new file mode 100644 (file)
index 0000000..d668188
--- /dev/null
@@ -0,0 +1,38 @@
+From 7ad37a986d0565e8ccae79a05148c96297b4bc1f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:27 +0300
+Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ]
+
+In iso.c check_bcast_qos(), missing bcast.timeout is not set to its
+default value, and appears typoed as bcast.sync_timeout.
+
+Fix the typo.
+
+Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index d667cbf8a1392..a0ef76ecb80b8 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1709,7 +1709,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
+               return false;
+       if (!qos->bcast.timeout)
+-              qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
++              qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
+       if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch b/queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch
new file mode 100644 (file)
index 0000000..1d80471
--- /dev/null
@@ -0,0 +1,133 @@
+From 8f6f297417ae19cd148b14ecef9eb5d094c7afbe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:29 +0300
+Subject: Bluetooth: ISO: hold sk properly in iso_conn_ready
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 0d255e63fcf3f13a570d7ac11678fa1164ac015c ]
+
+sk deref in iso_conn_ready must be done either under conn->lock, or
+holding a refcount, to avoid concurrent close. conn->sk is currently
+accessed without either:
+
+    [Task 1]            [Task 2]
+                        iso_sock_release
+    iso_conn_ready
+      sk = conn->sk
+                          lock_sock(sk)
+                            conn->sk = NULL
+      lock_sock(sk)
+                          release_sock(sk)
+                          iso_sock_kill(sk)
+       UAF on sk deref
+
+Fix possible UAF by holding sk refcount in iso_conn_ready().  Also
+recheck after lock_sock that the socket is still valid.  Adjust locking
+so conn->sk is cleared only under lock_sock.
+
+Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index fb77a7e22a9dd..cb629734062a5 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -787,11 +787,13 @@ static void iso_sock_kill(struct sock *sk)
+       BT_DBG("sk %p state %d", sk, sk->sk_state);
+       /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
++      lock_sock(sk);
+       if (iso_pi(sk)->conn) {
+               iso_conn_lock(iso_pi(sk)->conn);
+               iso_pi(sk)->conn->sk = NULL;
+               iso_conn_unlock(iso_pi(sk)->conn);
+       }
++      release_sock(sk);
+       /* Kill poor orphan */
+       bt_sock_unlink(&iso_sk_list, sk);
+@@ -1965,23 +1967,17 @@ static void iso_sock_ready(struct sock *sk)
+ {
+       BT_DBG("sk %p", sk);
+-      if (!sk)
+-              return;
+-
+-      lock_sock(sk);
++      lockdep_assert(lockdep_sock_is_held(sk));
+       switch (sk->sk_state) {
+       case BT_DISCONN:
+       case BT_CLOSED:
+-              release_sock(sk);
+               return;
+       }
+       iso_sock_clear_timer(sk);
+       sk->sk_state = BT_CONNECTED;
+       sk->sk_state_change(sk);
+-
+-      release_sock(sk);
+ }
+ static bool iso_match_big(struct sock *sk, void *data)
+@@ -2006,7 +2002,7 @@ static bool iso_match_pa_sync_flag(struct sock *sk, void *data)
+ static void iso_conn_ready(struct iso_conn *conn)
+ {
+       struct sock *parent = NULL;
+-      struct sock *sk = conn->sk;
++      struct sock *sk;
+       struct hci_ev_le_big_sync_established *ev = NULL;
+       struct hci_ev_le_pa_sync_established *ev2 = NULL;
+       struct hci_ev_le_per_adv_report *ev3 = NULL;
+@@ -2014,7 +2010,22 @@ static void iso_conn_ready(struct iso_conn *conn)
+       BT_DBG("conn %p", conn);
++      iso_conn_lock(conn);
++      sk = iso_sock_hold(conn);
++      iso_conn_unlock(conn);
++
+       if (sk) {
++              lock_sock(sk);
++
++              /* conn->sk may have become NULL if racing with sk close, but
++               * due to held hdev->lock, it can't become different sk.
++               */
++              if (!conn->sk) {
++                      release_sock(sk);
++                      sock_put(sk);
++                      return;
++              }
++
+               /* Attempt to update source address in case of BIS Sender if
+                * the advertisement is using a random address.
+                */
+@@ -2027,14 +2038,15 @@ static void iso_conn_ready(struct iso_conn *conn)
+                       adv = hci_find_adv_instance(bis->hdev,
+                                                   bis->iso_qos.bcast.bis);
+                       if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
+-                              lock_sock(sk);
+                               iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
+                               bacpy(&iso_pi(sk)->src, &adv->random_addr);
+-                              release_sock(sk);
+                       }
+               }
+-              iso_sock_ready(conn->sk);
++              iso_sock_ready(sk);
++
++              release_sock(sk);
++              sock_put(sk);
+       } else {
+               hcon = conn->hcon;
+               if (!hcon)
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch b/queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch
new file mode 100644 (file)
index 0000000..57d97c9
--- /dev/null
@@ -0,0 +1,92 @@
+From 877218b6c830a40e3810c93260d3ab930db59d95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:26 +0300
+Subject: Bluetooth: ISO: lock sk in iso_connect_ind
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 4311fd6f429065a8ba208660360a895627a00cf3 ]
+
+Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the
+"ev3" part of iso_connect_ind.  It may also be NULL if socket has
+transitioned away from the LISTEN/CONNECT states before locking.
+
+Fix by adding lock/release. Recheck hcon is valid after lock acquire
+where needed.
+
+Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 4377fe6cf14df..d667cbf8a1392 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -2246,7 +2246,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                       lock_sock(sk);
+-                      hcon = iso_pi(sk)->conn->hcon;
++                      hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
+                       iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
+                       if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
+@@ -2286,9 +2286,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+               if (!sk)
+                       goto done;
+-              hcon = iso_pi(sk)->conn->hcon;
++              lock_sock(sk);
++
++              hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
+               if (!hcon)
+-                      goto done;
++                      goto release3;
+               if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
+                       /* The controller was unable to retrieve PA data. */
+@@ -2296,12 +2298,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                              HCI_MAX_PER_AD_TOT_LEN);
+                       hcon->le_per_adv_data_len = 0;
+                       hcon->le_per_adv_data_offset = 0;
+-                      goto done;
++                      goto release3;
+               }
+               if (hcon->le_per_adv_data_offset + ev3->length >
+                   HCI_MAX_PER_AD_TOT_LEN)
+-                      goto done;
++                      goto release3;
+               memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
+                      ev3->data, ev3->length);
+@@ -2320,18 +2322,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                                                   &base_len);
+                       if (!base || base_len > BASE_MAX_LENGTH)
+-                              goto done;
++                              goto release3;
+-                      lock_sock(sk);
+                       memcpy(iso_pi(sk)->base, base, base_len);
+                       iso_pi(sk)->base_len = base_len;
+-                      release_sock(sk);
+               } else {
+                       /* This is a PA data fragment. Keep pa_data_len set to 0
+                        * until all data has been reassembled.
+                        */
+                       hcon->le_per_adv_data_len = 0;
+               }
++
++release3:
++              release_sock(sk);
+       } else {
+               sk = iso_get_sock(&hdev->bdaddr, BDADDR_ANY,
+                                 BT_LISTEN, NULL, NULL);
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch b/queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch
new file mode 100644 (file)
index 0000000..45a0519
--- /dev/null
@@ -0,0 +1,46 @@
+From 0ab8ce0a50cb6b44e4629f82860e3f0d8685d403 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:25 +0300
+Subject: Bluetooth: ISO: lock sk in iso_sock_getname
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 ]
+
+Accessing iso_pi(sk)->conn requires lock_sock, which is not held here.
+
+Fix by adding the lock/release.
+
+Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 5868ad1f7128e..4ea41b093ac65 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1392,6 +1392,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
+       BT_DBG("sock %p, sk %p", sock, sk);
++      lock_sock(sk);
++
+       addr->sa_family = AF_BLUETOOTH;
+       if (peer) {
+@@ -1413,6 +1415,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
+               sa->iso_bdaddr_type = iso_pi(sk)->src_type;
+       }
++      release_sock(sk);
++
+       return len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
new file mode 100644 (file)
index 0000000..6ef13f3
--- /dev/null
@@ -0,0 +1,49 @@
+From 6eb20cb8cfe3cec217e149eae5e54030e9152fdb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:28 +0300
+Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ]
+
+iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before
+validating the BIS values, so it's possible to end up with bc_num_bis
+inconsistent.
+
+Assign to iso_pi(sk)->bc_num_bis only after validation.
+
+Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index a0ef76ecb80b8..98fa94644b4a6 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1022,15 +1022,15 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
+               goto done;
+       }
+-      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
+-
+-      for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
++      for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
+               if (sa->iso_bc->bc_bis[i] < 0x01 ||
+                   sa->iso_bc->bc_bis[i] > 0x1f) {
+                       err = -EINVAL;
+                       goto done;
+               }
++      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
++
+       memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
+              iso_pi(sk)->bc_num_bis);
+-- 
+2.53.0
+
diff --git a/queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..b4df872
--- /dev/null
@@ -0,0 +1,60 @@
+From 16ceb79c28e11cf333f580f97fd034fb326949bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index e04948fb6832e..6133c65b20172 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       if (!chan)
+               return -EBADSLT;
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
new file mode 100644 (file)
index 0000000..ee5295a
--- /dev/null
@@ -0,0 +1,79 @@
+From b3799c578e81fc664a568b218b9bcb355e6f1847 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 4 Jul 2026 17:58:56 +0930
+Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ]
+
+[BUG]
+The following script can lead to unexpected qgroup rescan failure:
+
+  # mkfs.btrfs -f -O quota $dev
+  # mount $dev $mnt
+  # mount -o remount,rescue=ibadroots $mnt
+    ^^^^^ This above command is expected to fail
+
+  # btrfs quota rescan -w $mnt
+    ^^^^^ The above qgroup rescan is not expected to fail
+
+  # btrfs qgroup show $mnt
+  WARNING: qgroup data inconsistent, rescan recommended
+  Qgroupid    Referenced    Exclusive   Path
+  --------    ----------    ---------   ----
+  0/5           16.00KiB     16.00KiB   <toplevel>
+
+The above short script will be converted to a proper fstests case.
+
+[CAUSE]
+Inside btrfs_reconfigure(), if either btrfs_check_options() or
+btrfs_check_features() failed, we will always have
+BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful
+remount.
+
+That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations,
+including:
+
+- Qgroup rescan
+- Auto defrag
+- Space reclaim
+
+[FIX]
+Change the error handling of btrfs_check_options() and
+btrfs_check_features() to goto restore label.
+
+Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context")
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.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>
+---
+ fs/btrfs/super.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index 736575f925179..157d551344707 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -1518,12 +1518,14 @@ static int btrfs_reconfigure(struct fs_context *fc)
+       sync_filesystem(sb);
+       set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
+-      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags))
+-              return -EINVAL;
++      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) {
++              ret = -EINVAL;
++              goto restore;
++      }
+       ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY));
+       if (ret < 0)
+-              return ret;
++              goto restore;
+       btrfs_ctx_to_info(fs_info, ctx);
+       btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags);
+-- 
+2.53.0
+
diff --git a/queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch b/queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch
new file mode 100644 (file)
index 0000000..87d28de
--- /dev/null
@@ -0,0 +1,58 @@
+From c347a08ac8f96c358e64b5aa8daaae6787004d66 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 13:12:51 +0930
+Subject: btrfs: raid56: fix an incorrect csum skip during scrub
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 330dcc553f282e8dc0b88c9495b4c296465364e1 ]
+
+Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all()
+helper") uses the new helper to replace the nested loop inside
+verify_bio_data_sectors(), which simplifies the code.
+
+However that also changed the behavior of "continue" when a block has no
+data checksum.
+
+Previously the "continue" would skip the old for() loop, which would also
+increase @total_sector_nr.
+
+Now the "continue" will skip the new btrfs_bio_for_each_block_all()
+loop, which doesn't update @total_sector_nr.
+
+This means if we hit a block that has no data checksum, we will skip all
+the remaining blocks no matter if they have data checksum.
+As @total_sector_nr will never be updated, and that test_bit() will
+always return false.
+
+Fix it by increasing @total_sector_nr before calling "continue".
+
+Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper")
+Reviewed-by: Daniel Vacek <neelx@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/raid56.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
+index 0135dceb7baaa..4d4852f2ba7eb 100644
+--- a/fs/btrfs/raid56.c
++++ b/fs/btrfs/raid56.c
+@@ -1583,8 +1583,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
+               int ret;
+               /* No csum for this sector, skip to the next sector. */
+-              if (!test_bit(total_sector_nr, rbio->csum_bitmap))
++              if (!test_bit(total_sector_nr, rbio->csum_bitmap)) {
++                      total_sector_nr++;
+                       continue;
++              }
+               ret = btrfs_check_block_csum(fs_info, paddr,
+                                            csum_buf, expected_csum);
+-- 
+2.53.0
+
diff --git a/queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
new file mode 100644 (file)
index 0000000..be545b3
--- /dev/null
@@ -0,0 +1,77 @@
+From b88703a9e4fd0f112d4e59c5882646ddc4035b17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:40 +0200
+Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction
+ commit
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ]
+
+When writing out metadata extent buffers in a zoned filesystem,
+btree_writepages() holds fs_info->zoned_meta_io_lock across the whole
+writeback loop, including the call to btrfs_check_meta_write_pointer() ->
+check_bg_is_active().
+
+For the tree-log block group, check_bg_is_active() may fail to activate
+the zone and fall back to btrfs_zone_finish_one_bg() to free an active
+zone. That path waits for the running transaction to commit while still
+holding zoned_meta_io_lock, but the committer needs that same lock to
+write out the tree extents, so the two tasks deadlock:
+
+  Task A (kworker, metadata writeback)      Task B (fsstress, transaction commit)
+  ------------------------------------      -------------------------------------
+  wb_workfn()                               btrfs_commit_transaction(T)
+   btree_writepages()                        btrfs_write_and_wait_transaction()
+    btrfs_zoned_meta_io_lock()                btrfs_write_marked_extents()
+    btrfs_check_meta_write_pointer()           btree_writepages()
+     check_bg_is_active() [treelog_bg]          btrfs_zoned_meta_io_lock()
+      btrfs_zone_finish_one_bg()               <blocks on zoned_meta_io_lock,
+       btrfs_zone_finish()                      held by Task A>
+        do_zone_finish()
+         btrfs_inc_block_group_ro()
+          btrfs_wait_for_commit()
+           <blocks waiting for commit
+            of transaction T, done by
+            Task B>
+
+The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock
+around do_zone_finish() for this exact reason. Do the same in the tree-log
+branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire
+it afterwards. The lock only protects fs_info->active_{meta,system}_bg,
+which this branch does not touch, and ctx->zoned_bg keeps a reference to
+the block group across the unlock, so nothing is lost while the lock
+is dropped.
+
+This hang occasionally reproduces with fstests generic/475 on a zoned
+btrfs filesystem.
+
+Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index b8fc97e33009b..340380dbe81d1 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -2167,7 +2167,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx,
+       if (fs_info->treelog_bg == block_group->start) {
+               if (!btrfs_zone_activate(block_group)) {
+-                      int ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      int ret_fin;
++
++                      btrfs_zoned_meta_io_unlock(fs_info);
++                      ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      btrfs_zoned_meta_io_lock(fs_info);
+                       if (ret_fin != 1 || !btrfs_zone_activate(block_group))
+                               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch b/queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch
new file mode 100644 (file)
index 0000000..7d9701a
--- /dev/null
@@ -0,0 +1,56 @@
+From 80ecdcb40ee175cb44c7fec1ea97a7702ad2c510 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:45 +0200
+Subject: btrfs: zoned: reset meta_write_pointer on zone reset
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 5fabb1cf25d723274009d7b759545fd59f230c9d ]
+
+btrfs_reset_unused_block_groups() resets a block group's zone and sets
+alloc_offset back to 0 so the space can be reused, but it leaves
+meta_write_pointer pointing at the previous end of the zone.
+
+Once the block group is reactivated and reused for metadata, newly
+allocated tree blocks live before that stale write pointer.
+btrfs_check_meta_write_pointer() then sees them behind the write pointer,
+so they can never be written out in sequential order: the dirty extent
+buffers are stranded and pin their btree_inode folios until unmount.
+
+Reset meta_write_pointer back to the start of the block group for
+metadata and system block groups.
+
+Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index 340380dbe81d1..0dfbb28b7445c 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -3171,6 +3171,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num
+               reclaimed = bg->alloc_offset;
+               bg->zone_unusable = bg->length - bg->zone_capacity;
+               bg->alloc_offset = 0;
++              /*
++               * The zone was just reset to empty, so alloc_offset went back to
++               * the start of the zone. For metadata/system block groups the
++               * write pointer must follow it back to the start of the zone;
++               * otherwise it stays stale at the previous (finished) zone end,
++               * and metadata written into the reused zone would sit behind the
++               * write pointer, could never be written out in sequential order,
++               * and would be stranded (pinning its folio) until unmount.
++               */
++              if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM))
++                      bg->meta_write_pointer = bg->start;
+               /*
+                * This holds because we currently reset fully used then freed
+                * block group.
+-- 
+2.53.0
+
diff --git a/queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..c6b8eae
--- /dev/null
@@ -0,0 +1,57 @@
+From 22a858da2c2c2adcc7e4a0e478a1c87f2d2e6411 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index c21d816a52747..95e14631a1eb9 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1888,13 +1888,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
new file mode 100644 (file)
index 0000000..6e28321
--- /dev/null
@@ -0,0 +1,65 @@
+From d5fa93acbdda5bb1d9e98ae4db0b22f95f812a64 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 May 2026 10:15:50 -0400
+Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open()
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ]
+
+The failed_dev_add and failed_dev_name paths drop the file-device
+reference while wq->wq_lock is still held. If put_device(fdev) drops the
+last reference, idxd_file_dev_release() runs synchronously and tries to
+take wq->wq_lock again, deadlocking.
+
+Those paths also fall through into the later ctx cleanup labels even
+though idxd_file_dev_release() owns that cleanup and frees ctx. This can
+make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context.
+
+Move idxd_wq_get() before file-device setup can fail, since the release
+callback always calls idxd_wq_put(). Then unlock wq->wq_lock before
+put_device(fdev) and return directly from the file-device setup failure
+path, leaving ctx cleanup to the release callback.
+
+Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/idxd/cdev.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
+index 4105688cf3f06..37addcdc10bc2 100644
+--- a/drivers/dma/idxd/cdev.c
++++ b/drivers/dma/idxd/cdev.c
+@@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+       fdev->parent = cdev_dev(idxd_cdev);
+       fdev->bus = &dsa_bus_type;
+       fdev->type = &idxd_cdev_file_type;
++      idxd_wq_get(wq);
+       rc = dev_set_name(fdev, "file%d", ctx->id);
+       if (rc < 0) {
+@@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+               goto failed_dev_add;
+       }
+-      idxd_wq_get(wq);
+       mutex_unlock(&wq->wq_lock);
+       return 0;
+ failed_dev_add:
+ failed_dev_name:
++      mutex_unlock(&wq->wq_lock);
+       put_device(fdev);
++      return rc;
+ failed_ida:
+ failed_set_pasid:
+       if (device_user_pasid_enabled(idxd))
+-- 
+2.53.0
+
diff --git a/queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..afaba58
--- /dev/null
@@ -0,0 +1,60 @@
+From 64f10f396db74f269ec45dfb831bfa0541e5248b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index f9d876deb1f05..ae362a2f7c40f 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -935,16 +935,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch b/queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch
new file mode 100644 (file)
index 0000000..df00e5c
--- /dev/null
@@ -0,0 +1,294 @@
+From 216aef528319180a957576357360b5871594836d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Oct 2025 16:34:09 -0700
+Subject: Drivers: hv: Allocate the paravisor SynIC pages when required
+
+From: Roman Kisel <romank@linux.microsoft.com>
+
+[ Upstream commit 226494e5ee4eb0bae4fc7b525505828271d5047e ]
+
+Confidential VMBus requires interacting with two SynICs -- one
+provided by the host hypervisor, and one provided by the paravisor.
+Each SynIC requires its own message and event pages.
+
+Refactor and extend the existing code to add allocating and freeing
+the message and event pages for the paravisor SynIC when it is
+present.
+
+Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
+Reviewed-by: Tianyu Lan <tiala@microsoft.com>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/hv.c           | 184 +++++++++++++++++++-------------------
+ drivers/hv/hyperv_vmbus.h |  18 ++++
+ 2 files changed, 112 insertions(+), 90 deletions(-)
+
+diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
+index b7419d0fad1d3..90db1e17582d8 100644
+--- a/drivers/hv/hv.c
++++ b/drivers/hv/hv.c
+@@ -96,10 +96,70 @@ int hv_post_message(union hv_connection_id connection_id,
+       return hv_result(status);
+ }
++static int hv_alloc_page(void **page, bool decrypt, const char *note)
++{
++      int ret = 0;
++
++      /*
++       * After the page changes its encryption status, its contents might
++       * appear scrambled on some hardware. Thus `get_zeroed_page` would
++       * zero the page out in vain, so do that explicitly exactly once.
++       *
++       * By default, the page is allocated encrypted in a CoCo VM.
++       */
++      *page = (void *)__get_free_page(GFP_KERNEL);
++      if (!*page)
++              return -ENOMEM;
++
++      if (decrypt)
++              ret = set_memory_decrypted((unsigned long)*page, 1);
++      if (ret)
++              goto failed;
++
++      memset(*page, 0, PAGE_SIZE);
++      return 0;
++
++failed:
++      /*
++       * Report the failure but don't put the page back on the free list as
++       * its encryption status is unknown.
++       */
++      pr_err("allocation failed for %s page, error %d, decrypted %d\n",
++              note, ret, decrypt);
++      *page = NULL;
++      return ret;
++}
++
++static int hv_free_page(void **page, bool encrypt, const char *note)
++{
++      int ret = 0;
++
++      if (!*page)
++              return 0;
++
++      if (encrypt)
++              ret = set_memory_encrypted((unsigned long)*page, 1);
++
++      /*
++       * In the case of the failure, the page is leaked. Something is wrong,
++       * prefer to lose the page with the unknown encryption status and stay afloat.
++       */
++      if (ret)
++              pr_err("deallocation failed for %s page, error %d, encrypt %d\n",
++                      note, ret, encrypt);
++      else
++              free_page((unsigned long)*page);
++
++      *page = NULL;
++
++      return ret;
++}
++
+ int hv_synic_alloc(void)
+ {
+       int cpu, ret = -ENOMEM;
+       struct hv_per_cpu_context *hv_cpu;
++      const bool decrypt = !vmbus_is_confidential();
+       /*
+        * First, zero all per-cpu memory areas so hv_synic_free() can
+@@ -125,73 +185,37 @@ int hv_synic_alloc(void)
+                            vmbus_on_msg_dpc, (unsigned long)hv_cpu);
+               if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
+-                      hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
+-                      if (!hv_cpu->post_msg_page) {
+-                              pr_err("Unable to allocate post msg page\n");
++                      ret = hv_alloc_page(&hv_cpu->post_msg_page,
++                              decrypt, "post msg");
++                      if (ret)
+                               goto err;
+-                      }
+-
+-                      ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1);
+-                      if (ret) {
+-                              pr_err("Failed to decrypt post msg page: %d\n", ret);
+-                              /* Just leak the page, as it's unsafe to free the page. */
+-                              hv_cpu->post_msg_page = NULL;
+-                              goto err;
+-                      }
+-
+-                      memset(hv_cpu->post_msg_page, 0, PAGE_SIZE);
+               }
+               /*
+-               * Synic message and event pages are allocated by paravisor.
+-               * Skip these pages allocation here.
++               * If these SynIC pages are not allocated, SIEF and SIM pages
++               * are configured using what the root partition or the paravisor
++               * provides upon reading the SIEFP and SIMP registers.
+                */
+               if (!ms_hyperv.paravisor_present && !hv_root_partition()) {
+-                      hv_cpu->hyp_synic_message_page =
+-                              (void *)get_zeroed_page(GFP_ATOMIC);
+-                      if (!hv_cpu->hyp_synic_message_page) {
+-                              pr_err("Unable to allocate SYNIC message page\n");
++                      ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page,
++                              decrypt, "hypervisor SynIC msg");
++                      if (ret)
+                               goto err;
+-                      }
+-
+-                      hv_cpu->hyp_synic_event_page =
+-                              (void *)get_zeroed_page(GFP_ATOMIC);
+-                      if (!hv_cpu->hyp_synic_event_page) {
+-                              pr_err("Unable to allocate SYNIC event page\n");
+-
+-                              free_page((unsigned long)hv_cpu->hyp_synic_message_page);
+-                              hv_cpu->hyp_synic_message_page = NULL;
++                      ret = hv_alloc_page(&hv_cpu->hyp_synic_event_page,
++                              decrypt, "hypervisor SynIC event");
++                      if (ret)
+                               goto err;
+-                      }
+               }
+-              if (!ms_hyperv.paravisor_present &&
+-                  (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
+-                      ret = set_memory_decrypted((unsigned long)
+-                              hv_cpu->hyp_synic_message_page, 1);
+-                      if (ret) {
+-                              pr_err("Failed to decrypt SYNIC msg page: %d\n", ret);
+-                              hv_cpu->hyp_synic_message_page = NULL;
+-
+-                              /*
+-                               * Free the event page here so that hv_synic_free()
+-                               * won't later try to re-encrypt it.
+-                               */
+-                              free_page((unsigned long)hv_cpu->hyp_synic_event_page);
+-                              hv_cpu->hyp_synic_event_page = NULL;
++              if (vmbus_is_confidential()) {
++                      ret = hv_alloc_page(&hv_cpu->para_synic_message_page,
++                              false, "paravisor SynIC msg");
++                      if (ret)
+                               goto err;
+-                      }
+-
+-                      ret = set_memory_decrypted((unsigned long)
+-                              hv_cpu->hyp_synic_event_page, 1);
+-                      if (ret) {
+-                              pr_err("Failed to decrypt SYNIC event page: %d\n", ret);
+-                              hv_cpu->hyp_synic_event_page = NULL;
++                      ret = hv_alloc_page(&hv_cpu->para_synic_event_page,
++                              false, "paravisor SynIC event");
++                      if (ret)
+                               goto err;
+-                      }
+-
+-                      memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE);
+-                      memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE);
+               }
+       }
+@@ -207,48 +231,28 @@ int hv_synic_alloc(void)
+ void hv_synic_free(void)
+ {
+-      int cpu, ret;
++      int cpu;
++      const bool encrypt = !vmbus_is_confidential();
+       for_each_present_cpu(cpu) {
+               struct hv_per_cpu_context *hv_cpu =
+                       per_cpu_ptr(hv_context.cpu_context, cpu);
+-              /* It's better to leak the page if the encryption fails. */
+-              if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
+-                      if (hv_cpu->post_msg_page) {
+-                              ret = set_memory_encrypted((unsigned long)
+-                                      hv_cpu->post_msg_page, 1);
+-                              if (ret) {
+-                                      pr_err("Failed to encrypt post msg page: %d\n", ret);
+-                                      hv_cpu->post_msg_page = NULL;
+-                              }
+-                      }
++              if (ms_hyperv.paravisor_present && hv_isolation_type_tdx())
++                      hv_free_page(&hv_cpu->post_msg_page,
++                              encrypt, "post msg");
++              if (!ms_hyperv.paravisor_present && !hv_root_partition()) {
++                      hv_free_page(&hv_cpu->hyp_synic_event_page,
++                              encrypt, "hypervisor SynIC event");
++                      hv_free_page(&hv_cpu->hyp_synic_message_page,
++                              encrypt, "hypervisor SynIC msg");
+               }
+-
+-              if (!ms_hyperv.paravisor_present &&
+-                  (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
+-                      if (hv_cpu->hyp_synic_message_page) {
+-                              ret = set_memory_encrypted((unsigned long)
+-                                      hv_cpu->hyp_synic_message_page, 1);
+-                              if (ret) {
+-                                      pr_err("Failed to encrypt SYNIC msg page: %d\n", ret);
+-                                      hv_cpu->hyp_synic_message_page = NULL;
+-                              }
+-                      }
+-
+-                      if (hv_cpu->hyp_synic_event_page) {
+-                              ret = set_memory_encrypted((unsigned long)
+-                                      hv_cpu->hyp_synic_event_page, 1);
+-                              if (ret) {
+-                                      pr_err("Failed to encrypt SYNIC event page: %d\n", ret);
+-                                      hv_cpu->hyp_synic_event_page = NULL;
+-                              }
+-                      }
++              if (vmbus_is_confidential()) {
++                      hv_free_page(&hv_cpu->para_synic_event_page,
++                              false, "paravisor SynIC event");
++                      hv_free_page(&hv_cpu->para_synic_message_page,
++                              false, "paravisor SynIC msg");
+               }
+-
+-              free_page((unsigned long)hv_cpu->post_msg_page);
+-              free_page((unsigned long)hv_cpu->hyp_synic_event_page);
+-              free_page((unsigned long)hv_cpu->hyp_synic_message_page);
+       }
+       kfree(hv_context.hv_numa_map);
+diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
+index 1b05a57286101..39cdded062e8b 100644
+--- a/drivers/hv/hyperv_vmbus.h
++++ b/drivers/hv/hyperv_vmbus.h
+@@ -120,8 +120,26 @@ enum {
+  * Per cpu state for channel handling
+  */
+ struct hv_per_cpu_context {
++      /*
++       * SynIC pages for communicating with the host.
++       *
++       * These pages are accessible to the host partition and the hypervisor.
++       * They may be used for exchanging data with the host partition and the
++       * hypervisor even when they aren't trusted yet the guest partition
++       * must be prepared to handle the malicious behavior.
++       */
+       void *hyp_synic_message_page;
+       void *hyp_synic_event_page;
++      /*
++       * SynIC pages for communicating with the paravisor.
++       *
++       * These pages may be accessed from within the guest partition only in
++       * CoCo VMs. Neither the host partition nor the hypervisor can access
++       * these pages in that case; they are used for exchanging data with the
++       * paravisor.
++       */
++      void *para_synic_message_page;
++      void *para_synic_event_page;
+       /*
+        * The page is only used in hv_post_message() for a TDX VM (with the
+-- 
+2.53.0
+
diff --git a/queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch b/queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch
new file mode 100644 (file)
index 0000000..7b9256d
--- /dev/null
@@ -0,0 +1,327 @@
+From d35dad7522434863a8d053f83909a07237c91c30 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Oct 2025 16:34:08 -0700
+Subject: Drivers: hv: Rename fields for SynIC message and event pages
+
+From: Roman Kisel <romank@linux.microsoft.com>
+
+[ Upstream commit 163224c189e8b679ce919aa64ccabb7a992ca2d1 ]
+
+Confidential VMBus requires interacting with two SynICs -- one
+provided by the host hypervisor, and one provided by the paravisor.
+Each SynIC requires its own message and event pages.
+
+Rename the existing host-accessible SynIC message and event pages
+with the "hyp_" prefix to clearly distinguish them from the paravisor
+ones. The field name is also changed in mshv_root.* for consistency.
+
+No functional changes.
+
+Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
+Reviewed-by: Tianyu Lan <tiala@microsoft.com>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/channel_mgmt.c |  6 ++--
+ drivers/hv/hv.c           | 66 +++++++++++++++++++--------------------
+ drivers/hv/hyperv_vmbus.h |  4 +--
+ drivers/hv/mshv_root.h    |  2 +-
+ drivers/hv/mshv_synic.c   |  6 ++--
+ drivers/hv/vmbus_drv.c    |  6 ++--
+ 6 files changed, 45 insertions(+), 45 deletions(-)
+
+diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
+index fd1d675ae37a4..d5348195e4fb0 100644
+--- a/drivers/hv/channel_mgmt.c
++++ b/drivers/hv/channel_mgmt.c
+@@ -844,14 +844,14 @@ static void vmbus_wait_for_unload(void)
+                               = per_cpu_ptr(hv_context.cpu_context, cpu);
+                       /*
+-                       * In a CoCo VM the synic_message_page is not allocated
++                       * In a CoCo VM the hyp_synic_message_page is not allocated
+                        * in hv_synic_alloc(). Instead it is set/cleared in
+                        * hv_synic_enable_regs() and hv_synic_disable_regs()
+                        * such that it is set only when the CPU is online. If
+                        * not all present CPUs are online, the message page
+                        * might be NULL, so skip such CPUs.
+                        */
+-                      page_addr = hv_cpu->synic_message_page;
++                      page_addr = hv_cpu->hyp_synic_message_page;
+                       if (!page_addr)
+                               continue;
+@@ -892,7 +892,7 @@ static void vmbus_wait_for_unload(void)
+               struct hv_per_cpu_context *hv_cpu
+                       = per_cpu_ptr(hv_context.cpu_context, cpu);
+-              page_addr = hv_cpu->synic_message_page;
++              page_addr = hv_cpu->hyp_synic_message_page;
+               if (!page_addr)
+                       continue;
+diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
+index b14c5f9e0ef29..b7419d0fad1d3 100644
+--- a/drivers/hv/hv.c
++++ b/drivers/hv/hv.c
+@@ -147,20 +147,20 @@ int hv_synic_alloc(void)
+                * Skip these pages allocation here.
+                */
+               if (!ms_hyperv.paravisor_present && !hv_root_partition()) {
+-                      hv_cpu->synic_message_page =
++                      hv_cpu->hyp_synic_message_page =
+                               (void *)get_zeroed_page(GFP_ATOMIC);
+-                      if (!hv_cpu->synic_message_page) {
++                      if (!hv_cpu->hyp_synic_message_page) {
+                               pr_err("Unable to allocate SYNIC message page\n");
+                               goto err;
+                       }
+-                      hv_cpu->synic_event_page =
++                      hv_cpu->hyp_synic_event_page =
+                               (void *)get_zeroed_page(GFP_ATOMIC);
+-                      if (!hv_cpu->synic_event_page) {
++                      if (!hv_cpu->hyp_synic_event_page) {
+                               pr_err("Unable to allocate SYNIC event page\n");
+-                              free_page((unsigned long)hv_cpu->synic_message_page);
+-                              hv_cpu->synic_message_page = NULL;
++                              free_page((unsigned long)hv_cpu->hyp_synic_message_page);
++                              hv_cpu->hyp_synic_message_page = NULL;
+                               goto err;
+                       }
+               }
+@@ -168,30 +168,30 @@ int hv_synic_alloc(void)
+               if (!ms_hyperv.paravisor_present &&
+                   (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
+                       ret = set_memory_decrypted((unsigned long)
+-                              hv_cpu->synic_message_page, 1);
++                              hv_cpu->hyp_synic_message_page, 1);
+                       if (ret) {
+                               pr_err("Failed to decrypt SYNIC msg page: %d\n", ret);
+-                              hv_cpu->synic_message_page = NULL;
++                              hv_cpu->hyp_synic_message_page = NULL;
+                               /*
+                                * Free the event page here so that hv_synic_free()
+                                * won't later try to re-encrypt it.
+                                */
+-                              free_page((unsigned long)hv_cpu->synic_event_page);
+-                              hv_cpu->synic_event_page = NULL;
++                              free_page((unsigned long)hv_cpu->hyp_synic_event_page);
++                              hv_cpu->hyp_synic_event_page = NULL;
+                               goto err;
+                       }
+                       ret = set_memory_decrypted((unsigned long)
+-                              hv_cpu->synic_event_page, 1);
++                              hv_cpu->hyp_synic_event_page, 1);
+                       if (ret) {
+                               pr_err("Failed to decrypt SYNIC event page: %d\n", ret);
+-                              hv_cpu->synic_event_page = NULL;
++                              hv_cpu->hyp_synic_event_page = NULL;
+                               goto err;
+                       }
+-                      memset(hv_cpu->synic_message_page, 0, PAGE_SIZE);
+-                      memset(hv_cpu->synic_event_page, 0, PAGE_SIZE);
++                      memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE);
++                      memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE);
+               }
+       }
+@@ -227,28 +227,28 @@ void hv_synic_free(void)
+               if (!ms_hyperv.paravisor_present &&
+                   (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
+-                      if (hv_cpu->synic_message_page) {
++                      if (hv_cpu->hyp_synic_message_page) {
+                               ret = set_memory_encrypted((unsigned long)
+-                                      hv_cpu->synic_message_page, 1);
++                                      hv_cpu->hyp_synic_message_page, 1);
+                               if (ret) {
+                                       pr_err("Failed to encrypt SYNIC msg page: %d\n", ret);
+-                                      hv_cpu->synic_message_page = NULL;
++                                      hv_cpu->hyp_synic_message_page = NULL;
+                               }
+                       }
+-                      if (hv_cpu->synic_event_page) {
++                      if (hv_cpu->hyp_synic_event_page) {
+                               ret = set_memory_encrypted((unsigned long)
+-                                      hv_cpu->synic_event_page, 1);
++                                      hv_cpu->hyp_synic_event_page, 1);
+                               if (ret) {
+                                       pr_err("Failed to encrypt SYNIC event page: %d\n", ret);
+-                                      hv_cpu->synic_event_page = NULL;
++                                      hv_cpu->hyp_synic_event_page = NULL;
+                               }
+                       }
+               }
+               free_page((unsigned long)hv_cpu->post_msg_page);
+-              free_page((unsigned long)hv_cpu->synic_event_page);
+-              free_page((unsigned long)hv_cpu->synic_message_page);
++              free_page((unsigned long)hv_cpu->hyp_synic_event_page);
++              free_page((unsigned long)hv_cpu->hyp_synic_message_page);
+       }
+       kfree(hv_context.hv_numa_map);
+@@ -278,12 +278,12 @@ void hv_synic_enable_regs(unsigned int cpu)
+               /* Mask out vTOM bit. ioremap_cache() maps decrypted */
+               u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) &
+                               ~ms_hyperv.shared_gpa_boundary;
+-              hv_cpu->synic_message_page =
++              hv_cpu->hyp_synic_message_page =
+                       (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
+-              if (!hv_cpu->synic_message_page)
++              if (!hv_cpu->hyp_synic_message_page)
+                       pr_err("Fail to map synic message page.\n");
+       } else {
+-              simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
++              simp.base_simp_gpa = virt_to_phys(hv_cpu->hyp_synic_message_page)
+                       >> HV_HYP_PAGE_SHIFT;
+       }
+@@ -297,12 +297,12 @@ void hv_synic_enable_regs(unsigned int cpu)
+               /* Mask out vTOM bit. ioremap_cache() maps decrypted */
+               u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) &
+                               ~ms_hyperv.shared_gpa_boundary;
+-              hv_cpu->synic_event_page =
++              hv_cpu->hyp_synic_event_page =
+                       (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
+-              if (!hv_cpu->synic_event_page)
++              if (!hv_cpu->hyp_synic_event_page)
+                       pr_err("Fail to map synic event page.\n");
+       } else {
+-              siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
++              siefp.base_siefp_gpa = virt_to_phys(hv_cpu->hyp_synic_event_page)
+                       >> HV_HYP_PAGE_SHIFT;
+       }
+@@ -360,8 +360,8 @@ void hv_synic_disable_regs(unsigned int cpu)
+        */
+       simp.simp_enabled = 0;
+       if (ms_hyperv.paravisor_present || hv_root_partition()) {
+-              iounmap(hv_cpu->synic_message_page);
+-              hv_cpu->synic_message_page = NULL;
++              iounmap(hv_cpu->hyp_synic_message_page);
++              hv_cpu->hyp_synic_message_page = NULL;
+       } else {
+               simp.base_simp_gpa = 0;
+       }
+@@ -372,8 +372,8 @@ void hv_synic_disable_regs(unsigned int cpu)
+       siefp.siefp_enabled = 0;
+       if (ms_hyperv.paravisor_present || hv_root_partition()) {
+-              iounmap(hv_cpu->synic_event_page);
+-              hv_cpu->synic_event_page = NULL;
++              iounmap(hv_cpu->hyp_synic_event_page);
++              hv_cpu->hyp_synic_event_page = NULL;
+       } else {
+               siefp.base_siefp_gpa = 0;
+       }
+@@ -403,7 +403,7 @@ static bool hv_synic_event_pending(void)
+ {
+       struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context);
+       union hv_synic_event_flags *event =
+-              (union hv_synic_event_flags *)hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT;
++              (union hv_synic_event_flags *)hv_cpu->hyp_synic_event_page + VMBUS_MESSAGE_SINT;
+       unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */
+       bool pending;
+       u32 relid;
+diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
+index 34943de7d6ac4..1b05a57286101 100644
+--- a/drivers/hv/hyperv_vmbus.h
++++ b/drivers/hv/hyperv_vmbus.h
+@@ -120,8 +120,8 @@ enum {
+  * Per cpu state for channel handling
+  */
+ struct hv_per_cpu_context {
+-      void *synic_message_page;
+-      void *synic_event_page;
++      void *hyp_synic_message_page;
++      void *hyp_synic_event_page;
+       /*
+        * The page is only used in hv_post_message() for a TDX VM (with the
+diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
+index e3931b0f12693..db6b42db2fdc6 100644
+--- a/drivers/hv/mshv_root.h
++++ b/drivers/hv/mshv_root.h
+@@ -169,7 +169,7 @@ struct mshv_girq_routing_table {
+ };
+ struct hv_synic_pages {
+-      struct hv_message_page *synic_message_page;
++      struct hv_message_page *hyp_synic_message_page;
+       struct hv_synic_event_flags_page *synic_event_flags_page;
+       struct hv_synic_event_ring_page *synic_event_ring_page;
+ };
+diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
+index 1a822beae2390..3c65b1c4abbe3 100644
+--- a/drivers/hv/mshv_synic.c
++++ b/drivers/hv/mshv_synic.c
+@@ -399,7 +399,7 @@ mshv_intercept_isr(struct hv_message *msg)
+ void mshv_isr(void)
+ {
+       struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages);
+-      struct hv_message_page **msg_page = &spages->synic_message_page;
++      struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
+       struct hv_message *msg;
+       bool handled;
+@@ -461,7 +461,7 @@ int mshv_synic_init(unsigned int cpu)
+ #endif
+       union hv_synic_scontrol sctrl;
+       struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages);
+-      struct hv_message_page **msg_page = &spages->synic_message_page;
++      struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
+       struct hv_synic_event_flags_page **event_flags_page =
+                       &spages->synic_event_flags_page;
+       struct hv_synic_event_ring_page **event_ring_page =
+@@ -555,7 +555,7 @@ int mshv_synic_cleanup(unsigned int cpu)
+       union hv_synic_sirbp sirbp;
+       union hv_synic_scontrol sctrl;
+       struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages);
+-      struct hv_message_page **msg_page = &spages->synic_message_page;
++      struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
+       struct hv_synic_event_flags_page **event_flags_page =
+               &spages->synic_event_flags_page;
+       struct hv_synic_event_ring_page **event_ring_page =
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index 1a0e350c19404..0788281b28ded 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -1048,7 +1048,7 @@ static void vmbus_onmessage_work(struct work_struct *work)
+ void vmbus_on_msg_dpc(unsigned long data)
+ {
+       struct hv_per_cpu_context *hv_cpu = (void *)data;
+-      void *page_addr = hv_cpu->synic_message_page;
++      void *page_addr = hv_cpu->hyp_synic_message_page;
+       struct hv_message msg_copy, *msg = (struct hv_message *)page_addr +
+                                 VMBUS_MESSAGE_SINT;
+       struct vmbus_channel_message_header *hdr;
+@@ -1232,7 +1232,7 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
+        * The event page can be directly checked to get the id of
+        * the channel that has the interrupt pending.
+        */
+-      void *page_addr = hv_cpu->synic_event_page;
++      void *page_addr = hv_cpu->hyp_synic_event_page;
+       union hv_synic_event_flags *event
+               = (union hv_synic_event_flags *)page_addr +
+                                        VMBUS_MESSAGE_SINT;
+@@ -1315,7 +1315,7 @@ static void __vmbus_isr(void)
+       vmbus_chan_sched(hv_cpu);
+-      page_addr = hv_cpu->synic_message_page;
++      page_addr = hv_cpu->hyp_synic_message_page;
+       msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
+       /* Check if there are actual msgs to be processed */
+-- 
+2.53.0
+
diff --git a/queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
new file mode 100644 (file)
index 0000000..615ebdd
--- /dev/null
@@ -0,0 +1,55 @@
+From ff46847f417fd3960b611bf8edec254ba5495234 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 17:32:15 +0200
+Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep
+ annotation
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ]
+
+lockdep_hardirq_threaded() is supposed to be used within IRQ core code
+and not within drivers. It is not obvious from within the driver, that
+this is the only interrupt service routing and that it is not shared
+handler.
+
+Replace lockdep_hardirq_threaded() with a lockdep annotation limiting
+threaded context on PREEMPT_RT to __vmbus_isr().
+
+Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/vmbus_drv.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index 0788281b28ded..4f1f5f294a735 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -1372,8 +1372,19 @@ static void vmbus_isr(void)
+       if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
+               vmbus_irqd_wake();
+       } else {
+-              lockdep_hardirq_threaded();
++              static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG);
++
++              /*
++               * vmbus_isr is never force-threaded and always invoked at hard
++               * IRQ level. __vmbus_isr() below can acquire a spinlock_t
++               * which becomes a sleeping lock and must not be acquired in
++               * this context. Therefore on PREEMPT_RT this will be threaded
++               * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets
++               * lockdep know that acquiring a spinlock_t is not an issue.
++               */
++              lock_map_acquire_try(&vmbus_map);
+               __vmbus_isr();
++              lock_map_release(&vmbus_map);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch b/queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch
new file mode 100644 (file)
index 0000000..cad8b81
--- /dev/null
@@ -0,0 +1,80 @@
+From 752c1d48925da00682511d8f744174f5b0b12e60 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 12:52:07 +0200
+Subject: drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC
+ encoder
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Alexander Kaplan <alexander.kaplan@sms-medipool.de>
+
+[ Upstream commit 8891e39e89042e285fd82fdde325d1311ec750a1 ]
+
+intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its
+DSC max FRL rate whenever the sink supports DSC 1.2.
+However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies
+to compressed video transport, which requires a DSC 1.2 encoder in
+the PCON (configured via intel_dp_pcon_dsc_configure()).
+Without such an encoder the HDMI link always carries uncompressed
+video, for which the regular Max_FRL_Rate is the correct limit.
+
+Applying the DSC limit unconditionally trains the FRL link at a lower
+rate than both the PCON and the sink support.
+E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps)
+behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC
+encoder):
+
+  Sink max rate from EDID = 24 Gbps
+  FRL trained with : 24 Gbps
+
+while Windows/macOS train the same hardware at 40/48 Gbps.
+The too low FRL rate needlessly constrains the formats available to
+the sink.
+
+Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2
+encoder, matching the gate in intel_dp_pcon_dsc_configure().
+PCONs with a DSC encoder keep the current conservative behavior,
+since the link is trained once and compressed transport may be used
+for any subsequent mode.
+With this the setup above trains at 48 Gbps.
+
+Tested on PTL (xe) with the above PCON/sink combo.
+
+Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding")
+Cc: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Reviewed-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Signed-off-by: Alexander Kaplan <alexander.kaplan@sms-medipool.de>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de
+(cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
+index 2a0750acf2053..84fca860c93ee 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -3859,7 +3859,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
+       rate_per_lane = info->hdmi.max_frl_rate_per_lane;
+       max_frl_rate = max_lanes * rate_per_lane;
+-      if (info->hdmi.dsc_cap.v_1p2) {
++      /*
++       * The sink's DSC max FRL rate only applies to compressed video
++       * transport, which requires a DSC 1.2 encoder in the PCON. Without
++       * one the HDMI link always carries uncompressed video, for which
++       * the regular max FRL rate is the limit.
++       */
++      if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) &&
++          info->hdmi.dsc_cap.v_1p2) {
+               max_dsc_lanes = info->hdmi.dsc_cap.max_lanes;
+               dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane;
+               if (max_dsc_lanes && dsc_rate_per_lane)
+-- 
+2.53.0
+
diff --git a/queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch b/queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch
new file mode 100644 (file)
index 0000000..74db704
--- /dev/null
@@ -0,0 +1,117 @@
+From 65b2fa379c96e8f2a20c9c02eae57dc4e22e52d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 07:55:44 +0530
+Subject: drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status
+
+From: Jerome Tollet <jerome.tollet@gmail.com>
+
+[ Upstream commit 1afb8eaeec44fd011f2b93ccd9fd426d753d963b ]
+
+HDMI 2.0 section 6.1.3.1 specifies that after enabling
+Scrambling_Enable and starting scrambled video transmission, the source
+should poll Scrambling_Status until it reads 1 or until a timeout of
+200 ms expires.
+
+Add a polling step after enabling the HDMI port to check the scrambling
+status when HDMI scrambling is enabled.
+
+On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz
+(594 MHz) failing to come up correctly because the sink has not yet
+finished its scrambling setup. In practice, waiting for the scrambling
+status here fixes such sinks.
+
+While this synchronous polling is not itself explicitly required for
+correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it
+as the way for the source to verify that the TMDS link is functioning
+correctly with scrambling enabled.
+
+v3:
+ - Add explicit HDMI 2.0 section reference in code comment
+ - Clarify commit message around the observed sink fix
+
+v2:
+ - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed
+   delay
+
+Reported-by: Jerome Tollet <jtollet@cisco.com>
+Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868
+Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/
+Signed-off-by: Jerome Tollet <jerome.tollet@gmail.com>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com
+(cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774)
+Fixes: 15953637886d ("drm/i915: enable scrambling")
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_ddi.c  |  2 ++
+ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++
+ drivers/gpu/drm/i915/display/intel_hdmi.h |  2 ++
+ 3 files changed, 30 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
+index c09aa759f4d4f..3de3e9167d0d6 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi.c
+@@ -3502,6 +3502,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state,
+       }
+       intel_ddi_buf_enable(encoder, buf_ctl);
++
++      intel_hdmi_poll_for_scrambling_enable(crtc_state, connector);
+ }
+ static void intel_ddi_enable(struct intel_atomic_state *state,
+diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
+index 4ab7e2e3bfd42..e77c6422b001f 100644
+--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
++++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
+@@ -2680,6 +2680,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_
+               drm_connector_attach_max_bpc_property(&connector->base, 8, 12);
+ }
++/*
++ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after
++ * enabling Scrambling_Enable and starting scrambled video
++ * transmission, poll Scrambling_Status for up to 200 ms.
++ */
++void
++intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
++                                    struct drm_connector *_connector)
++{
++      struct intel_connector *connector = to_intel_connector(_connector);
++      struct intel_display *display = to_intel_display(crtc_state);
++      bool scrambling_enabled = false;
++      int ret;
++
++      if (!crtc_state->hdmi_scrambling)
++              return;
++
++      /* Poll for a max of 200 msec as per HDMI spec */
++      ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base),
++                            scrambling_enabled, 1000, 200 * 1000, false);
++      if (ret)
++              drm_dbg_kms(display->drm,
++                          "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n",
++                          connector->base.base.id, connector->base.name);
++}
++
+ /*
+  * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup
+  * @encoder: intel_encoder
+diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
+index dec2ad7dd8a22..2de9056367916 100644
+--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
++++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
+@@ -69,5 +69,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder,
+                       const struct intel_crtc_state *crtc_state,
+                       unsigned int type,
+                       void *frame, ssize_t len);
++void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
++                                         struct drm_connector *_connector);
+ #endif /* __INTEL_HDMI_H__ */
+-- 
+2.53.0
+
diff --git a/queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..5234aa0
--- /dev/null
@@ -0,0 +1,51 @@
+From 0361283dc1e85b03c5d9ff3ec2e091e9f6bcf5fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
+index c4c6d0249df56..aa8e6f6ddcd32 100644
+--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
+@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+-- 
+2.53.0
+
diff --git a/queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..9fe0189
--- /dev/null
@@ -0,0 +1,47 @@
+From d1275db875ad85f1f620ae0efa4746ae5edf5ff6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index 19aa1f1538aa3..c09fb7694facb 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch b/queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch
new file mode 100644 (file)
index 0000000..cfe05fb
--- /dev/null
@@ -0,0 +1,48 @@
+From 39de6e32b86aec489bfa25fea7e3785f3294bc49 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 08:27:33 +0900
+Subject: fprobe: Fix module reference count leak on error in register_fprobe()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 ]
+
+In register_fprobe(), get_ips_from_filter() resolves target function
+addresses and increments module reference counts via try_module_get() for
+symbols in kernel modules. If get_ips_from_filter() fails on the second
+pass and returns an error, register_fprobe() returned directly without
+releasing module references acquired up to that point.
+
+Fix this by ensuring the cleanup loop executing module_put() runs even when
+get_ips_from_filter() returns a negative error.
+
+Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/
+
+Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/fprobe.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
+index 01f98f8a86e60..1c9ae332afad1 100644
+--- a/kernel/trace/fprobe.c
++++ b/kernel/trace/fprobe.c
+@@ -868,10 +868,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
+               return -ENOMEM;
+       ret = get_ips_from_filter(filter, notfilter, addrs, mods, num);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = register_fprobe_ips(fp, addrs, ret);
++      if (ret >= 0)
++              ret = register_fprobe_ips(fp, addrs, ret);
+       for (int i = 0; i < num; i++) {
+               if (mods[i])
+-- 
+2.53.0
+
diff --git a/queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
new file mode 100644 (file)
index 0000000..ff3b89b
--- /dev/null
@@ -0,0 +1,55 @@
+From f111ea9d0a481626e0340cb96bb6c55165832321 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:23:08 +0530
+Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe()
+
+From: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+
+[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ]
+
+The memory allocated for priv->blob.data is not freed in the error paths
+that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as
+well as in the remove function. Fix that by using device managed action
+to free the memory on remove.
+
+Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling")
+Signed-off-by: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
+Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+index 969dddd3d6faf..0f4a6228a7488 100644
+--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c
++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val)
+       return 0;
+ }
++static void fops_buf_release(void *data)
++{
++      struct gpio_la_poll_priv *priv = data;
++
++      vfree(priv->blob.data);
++}
++
+ static int fops_buf_size_set(void *data, u64 val)
+ {
+       struct gpio_la_poll_priv *priv = data;
+@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
+               return ret;
+       fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
++      ret = devm_add_action_or_reset(dev, fops_buf_release, priv);
++      if (ret)
++              return ret;
+       priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN);
+       if (IS_ERR(priv->descs))
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..3e174e2
--- /dev/null
@@ -0,0 +1,51 @@
+From 7e867e200107fe0decd1b85a45ba67cfb2e35a90 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 06b19fd382457..a927e0b6d3319 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..cd5476c
--- /dev/null
@@ -0,0 +1,83 @@
+From 6439755e104baf4c587e6c45d0f512369f7a53f6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index ef8f411df61be..06b19fd382457 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..7d7b418
--- /dev/null
@@ -0,0 +1,80 @@
+From 22178441dc45a1187c78cb3656088a5f83654abf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 43c0130342622..3a2d408a45be8 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..1b990c2
--- /dev/null
@@ -0,0 +1,115 @@
+From 9f44cde625696d9e37a4948a8a7eff45b2202334 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index dbee6926fa055..ef8f411df61be 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..70ee6d4
--- /dev/null
@@ -0,0 +1,57 @@
+From 6a1b038e5eef578c5d9101695f4b921e9d773421 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 3a2d408a45be8..0fad4bfe53df6 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..71efc9f
--- /dev/null
@@ -0,0 +1,62 @@
+From 6b0c80e1551f71a1560fceddabf47735e351d7cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 41fa6a0e8b937..f0a45538ded6d 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..f679da1
--- /dev/null
@@ -0,0 +1,71 @@
+From 534115e7f4e2fce9d5ec1b0b2b79b10d6fc9b303 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index a927e0b6d3319..41fa6a0e8b937 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..4c37445
--- /dev/null
@@ -0,0 +1,111 @@
+From b46d6418cb50c31c30b14d112388a9588a3d31a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index f0a45538ded6d..43c0130342622 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch b/queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch
new file mode 100644 (file)
index 0000000..664abb8
--- /dev/null
@@ -0,0 +1,133 @@
+From f7ca6478d5aa2bc1b480fd5ae4074b7f151cd49c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 13:20:22 +0200
+Subject: hwmon: (ina2xx) Add support for INA234
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ian Ray <ian.ray@gehealthcare.com>
+
+[ Upstream commit 88a928eebccdc5445d874ddcbf1683b76c9f1431 ]
+
+INA234 is register compatible to INA226 (excepting manufacturer and die
+or device id registers) but has different scaling.
+
+Signed-off-by: Ian Ray <ian.ray@gehealthcare.com>
+Reviewed-by: Bence Csókás <bence98@sch.bme.hu> # v2
+Tested-by: Jens Almer <bagawk@gmail.com>
+Tested-by: Jonas Rebmann <jre@pengutronix.de>
+Link: https://lore.kernel.org/r/20260220112024.97446-4-ian.ray@gehealthcare.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 13 ++++++++++++-
+ drivers/hwmon/Kconfig          |  2 +-
+ drivers/hwmon/ina2xx.c         | 18 ++++++++++++++++++
+ 3 files changed, 31 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index a4ddf4bd2b081..d64e7af46a124 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -74,6 +74,16 @@ Supported chips:
+              https://us1.silergy.com/
++  * Texas Instruments INA234
++
++    Prefix: 'ina234'
++
++    Addresses: I2C 0x40 - 0x43
++
++    Datasheet: Publicly available at the Texas Instruments website
++
++             https://www.ti.com/
++
+ Author: Lothar Felten <lothar.felten@gmail.com>
+ Description
+@@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage.
+ The INA226 is a current shunt and power monitor with an I2C interface.
+ The INA226 monitors both a shunt voltage drop and bus supply voltage.
+-INA230 and INA231 are high or low side current shunt and power monitors
++INA230, INA231, and INA234 are high or low side current shunt and power monitors
+ with an I2C interface. The chips monitor both a shunt voltage drop and
+ bus supply voltage.
+@@ -132,6 +142,7 @@ Additional entries are available for the following chips:
+   * ina226
+   * ina230
+   * ina231
++  * ina234
+   * ina260
+   * sy24655
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index c8a7e45e8006c..4d852e68b4749 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2275,7 +2275,7 @@ config SENSORS_INA2XX
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for INA219, INA220, INA226,
+-        INA230, INA231, INA260, and SY24655 power monitor chips.
++        INA230, INA231, INA234, INA260, and SY24655 power monitor chips.
+         The INA2xx driver is configured for the default configuration of
+         the part as described in the datasheet.
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index afd0018a17e03..e07f24901bfbb 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -138,6 +138,7 @@ static const struct regmap_config ina2xx_regmap_config = {
+ enum ina2xx_ids {
+       ina219,
+       ina226,
++      ina234,
+       ina260,
+       sy24655
+ };
+@@ -192,6 +193,18 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_power_average = false,
+               .has_update_interval = true,
+       },
++      [ina234] = {
++              .config_default = INA226_CONFIG_DEFAULT,
++              .calibration_value = 2048,
++              .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .bus_voltage_shift = 4,
++              .bus_voltage_lsb = 25600,
++              .power_lsb_factor = 32,
++              .has_alerts = true,
++              .has_ishunt = false,
++              .has_power_average = false,
++              .has_update_interval = true,
++      },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+               .shunt_div = 400,
+@@ -1000,6 +1013,7 @@ static const struct i2c_device_id ina2xx_id[] = {
+       { "ina226", ina226 },
+       { "ina230", ina226 },
+       { "ina231", ina226 },
++      { "ina234", ina234 },
+       { "ina260", ina260 },
+       { "sy24655", sy24655 },
+       { }
+@@ -1031,6 +1045,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
+               .compatible = "ti,ina231",
+               .data = (void *)ina226
+       },
++      {
++              .compatible = "ti,ina234",
++              .data = (void *)ina234
++      },
+       {
+               .compatible = "ti,ina260",
+               .data = (void *)ina260
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch
new file mode 100644 (file)
index 0000000..356072e
--- /dev/null
@@ -0,0 +1,156 @@
+From 3415ad3f4779b000b072760c670c3d4f9cb61373 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 10 Jun 2026 07:46:16 -0700
+Subject: hwmon: (ina2xx) Fix various overflow issues
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ]
+
+Sashiko reports several integer overflow problems in the ina2xx driver
+caused by unbounded multiplications and inadequate types for intermediate
+calculations.
+
+Specifically:
+- In ina2xx_get_value(), the return type is changed from int to long.
+  Intermediate calculations for current are now performed using 64-bit
+  types to prevent 32-bit integer overflow before the division by 1000.
+- When calculating power in ina2xx_get_value() and
+  sy24655_average_power_read(), interim values are cast to u64 and clamped
+  to LONG_MAX. This prevents overflow when regval or accumulator_24 is
+  multiplied by power_lsb_uW.
+- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t().
+  This safely avoids integer overflows when scaling user-provided values
+  for shunt voltage, bus voltage, power, and current limits.
+
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index a46a118406726..9c2dc9c0fd4d5 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -31,6 +31,7 @@
+ #include <linux/i2c.h>
+ #include <linux/init.h>
+ #include <linux/kernel.h>
++#include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+@@ -283,30 +284,34 @@ static u16 ina226_interval_to_reg(long interval)
+       return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits);
+ }
+-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+-                          unsigned int regval)
++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg,
++                           unsigned int regval)
+ {
+-      int val;
++      s64 val64;
++      long val;
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+               /* signed register */
+-              val = (s16)regval >> data->config->shunt_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
++              val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift,
++                                      data->config->shunt_div);
+               break;
+       case INA2XX_BUS_VOLTAGE:
+-              val = (regval >> data->config->bus_voltage_shift) *
+-                data->config->bus_voltage_lsb;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) *
++                                      data->config->bus_voltage_lsb, 1000);
+               break;
+       case INA2XX_POWER:
+-              val = regval * data->power_lsb_uW;
++              val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX);
+               break;
+       case INA2XX_CURRENT:
+               /* signed register, result in mA */
+-              val = ((s16)regval >> data->config->current_shift) *
++              val64 = (s64)((s16)regval >> data->config->current_shift) *
+                 data->current_lsb_uA;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              if (val64 < 0)
++                      val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000);
++              else
++                      val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000);
++              val = clamp_val(val64, LONG_MIN, LONG_MAX);
+               break;
+       case INA2XX_CALIBRATION:
+               val = regval;
+@@ -395,27 +400,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
+  */
+ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+ {
++      long limit;
++
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+-              val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
+-              val *= data->config->shunt_div;
+-              val <<= data->config->shunt_voltage_shift;
+-              return clamp_val(val, 0, SHRT_MAX);
++              val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div));
++              return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift,
++                           SHRT_MAX);
+       case INA2XX_BUS_VOLTAGE:
+-              val = clamp_val(val, 0, 200000);
+-              val = (val * 1000) << data->config->bus_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, 130000);
++              return min_t(long,
++                           DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift,
++                                             data->config->bus_voltage_lsb),
++                           USHRT_MAX);
+       case INA2XX_POWER:
+-              val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW);
+-              val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, LONG_MAX - data->power_lsb_uW);
++              return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX);
+       case INA2XX_CURRENT:
+-              val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
++              limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000;
++              val = min_t(long, val, limit);
+               /* signed register, result in mA */
+               val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
+-              val <<= data->config->current_shift;
+-              return clamp_val(val, SHRT_MIN, SHRT_MAX);
++              limit = SHRT_MAX >> data->config->current_shift;
++              return (u16)(min_t(long, val, limit) << data->config->current_shift);
+       default:
+               /* programmer goofed */
+               WARN_ON_ONCE(1);
+@@ -560,6 +567,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+       u8 template[6];
+       int ret;
+       long accumulator_24, sample_count;
++      u64 val64;
+       /* 48-bit register read */
+       ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
+@@ -578,7 +586,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+               return 0;
+       }
+-      *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      *val = min_t(u64, val64, LONG_MAX);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch b/queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch
new file mode 100644 (file)
index 0000000..5c7bc01
--- /dev/null
@@ -0,0 +1,133 @@
+From 7f3e8e729a6da94a8648b206710546b8a79f10b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 13:20:21 +0200
+Subject: hwmon: (ina2xx) Make it easier to add more devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ian Ray <ian.ray@gehealthcare.com>
+
+[ Upstream commit f6e14b5bcabf4ee97a2d535c3c2d7e72c8da4c15 ]
+
+* Make sysfs entries documentation easier to maintain.
+* Use multi-line enum.
+* Correct "has_power_average" comment.
+
+Create a new "has_update_interval" member for chips which support
+averaging.
+
+Signed-off-by: Ian Ray <ian.ray@gehealthcare.com>
+Reviewed-by: Bence Csókás <bence98@sch.bme.hu> # v2
+Tested-by: Jens Almer <bagawk@gmail.com>
+Link: https://lore.kernel.org/r/20260220112024.97446-3-ian.ray@gehealthcare.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/hwmon/ina2xx.rst | 12 ++++++++++--
+ drivers/hwmon/ina2xx.c         | 18 ++++++++++++++----
+ 2 files changed, 24 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst
+index a3860aae444c0..a4ddf4bd2b081 100644
+--- a/Documentation/hwmon/ina2xx.rst
++++ b/Documentation/hwmon/ina2xx.rst
+@@ -124,8 +124,16 @@ power1_input              Power(uW) measurement channel
+ shunt_resistor                Shunt resistance(uOhm) channel (not for ina260)
+ ======================= ===============================================
+-Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
+-------------------------------------------------------------------------
++Additional sysfs entries
++------------------------
++
++Additional entries are available for the following chips:
++
++  * ina226
++  * ina230
++  * ina231
++  * ina260
++  * sy24655
+ ======================= ====================================================
+ curr1_lcrit           Critical low current
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index bc3c1f7314b3e..afd0018a17e03 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -135,13 +135,19 @@ static const struct regmap_config ina2xx_regmap_config = {
+       .writeable_reg = ina2xx_writeable_reg,
+ };
+-enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
++enum ina2xx_ids {
++      ina219,
++      ina226,
++      ina260,
++      sy24655
++};
+ struct ina2xx_config {
+       u16 config_default;
+       bool has_alerts;        /* chip supports alerts and limits */
+       bool has_ishunt;        /* chip has internal shunt resistor */
+-      bool has_power_average; /* chip has internal shunt resistor */
++      bool has_power_average; /* chip supports average power */
++      bool has_update_interval;
+       int calibration_value;
+       int shunt_div;
+       int bus_voltage_shift;
+@@ -172,6 +178,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = false,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .has_update_interval = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+@@ -183,6 +190,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .has_update_interval = true,
+       },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+@@ -193,6 +201,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = true,
+               .has_power_average = false,
++              .has_update_interval = true,
+       },
+       [sy24655] = {
+               .config_default = SY24655_CONFIG_DEFAULT,
+@@ -204,6 +213,7 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = true,
++              .has_update_interval = false,
+       },
+ };
+@@ -713,7 +723,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+       const struct ina2xx_data *data = _data;
+       bool has_alerts = data->config->has_alerts;
+       bool has_power_average = data->config->has_power_average;
+-      enum ina2xx_ids chip = data->chip;
++      bool has_update_interval = data->config->has_update_interval;
+       switch (type) {
+       case hwmon_in:
+@@ -775,7 +785,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+-                      if (chip == ina226 || chip == ina260)
++                      if (has_update_interval)
+                               return 0644;
+                       break;
+               default:
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch b/queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch
new file mode 100644 (file)
index 0000000..9e45aa3
--- /dev/null
@@ -0,0 +1,163 @@
+From bee38cf8d68ebf9122d1605337c846fe9956fc08 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Mar 2026 12:07:02 +0100
+Subject: hwmon: (ina2xx) Shift INA234 shunt and current registers
+
+From: Jonas Rebmann <jre@pengutronix.de>
+
+[ Upstream commit eeca1114d1e2cc0eacaebb80f3f2afbaebfc60be ]
+
+The INA219 has the lowest three bits of the bus voltage register
+zero-reserved, the bus_voltage_shift ina2xx_config field was introduced
+to accommodate for that.
+
+The INA234 has four bits of the bus voltage, of the shunt voltage, and
+of the current registers zero-reserved but the latter two were
+implemented by choosing a 16x higher shunt_div instead of a separate
+field specifying a bit shift.
+
+This is possible because shunt voltage and current are divided by
+shunt_div, hence a 16x higher shunt_div results in a 16x smaller LSB for
+both the shunt voltage and the current register, perfectly accounting
+for the missing bit shift.
+
+For consistency and correctness, account for the reserved bits via
+shunt_voltage_shift and current_shift configuration fields as already
+done for voltage registers and use the conversion constants given in the
+INA234 datasheet.
+
+Signed-off-by: Jonas Rebmann <jre@pengutronix.de>
+Link: https://lore.kernel.org/r/20260303-ina234-shift-v1-2-318c33ac4480@pengutronix.de
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index e07f24901bfbb..a46a118406726 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -151,9 +151,11 @@ struct ina2xx_config {
+       bool has_update_interval;
+       int calibration_value;
+       int shunt_div;
++      int shunt_voltage_shift;
+       int bus_voltage_shift;
+       int bus_voltage_lsb;    /* uV */
+       int power_lsb_factor;
++      int current_shift;
+ };
+ struct ina2xx_data {
+@@ -173,59 +175,69 @@ static const struct ina2xx_config ina2xx_config[] = {
+               .config_default = INA219_CONFIG_DEFAULT,
+               .calibration_value = 4096,
+               .shunt_div = 100,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 3,
+               .bus_voltage_lsb = 4000,
+               .power_lsb_factor = 20,
+               .has_alerts = false,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = false,
+       },
+       [ina226] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+               .calibration_value = 2048,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = true,
+       },
+       [ina234] = {
+               .config_default = INA226_CONFIG_DEFAULT,
+               .calibration_value = 2048,
+-              .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */
++              .shunt_voltage_shift = 4,
+               .bus_voltage_shift = 4,
+               .bus_voltage_lsb = 25600,
+               .power_lsb_factor = 32,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = false,
++              .current_shift = 4,
+               .has_update_interval = true,
+       },
+       [ina260] = {
+               .config_default = INA260_CONFIG_DEFAULT,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 8,
+               .has_alerts = true,
+               .has_ishunt = true,
+               .has_power_average = false,
++              .current_shift = 0,
+               .has_update_interval = true,
+       },
+       [sy24655] = {
+               .config_default = SY24655_CONFIG_DEFAULT,
+               .calibration_value = 4096,
+               .shunt_div = 400,
++              .shunt_voltage_shift = 0,
+               .bus_voltage_shift = 0,
+               .bus_voltage_lsb = 1250,
+               .power_lsb_factor = 25,
+               .has_alerts = true,
+               .has_ishunt = false,
+               .has_power_average = true,
++              .current_shift = 0,
+               .has_update_interval = false,
+       },
+ };
+@@ -279,7 +291,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+               /* signed register */
+-              val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
++              val = (s16)regval >> data->config->shunt_voltage_shift;
++              val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
+               break;
+       case INA2XX_BUS_VOLTAGE:
+               val = (regval >> data->config->bus_voltage_shift) *
+@@ -291,7 +304,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+               break;
+       case INA2XX_CURRENT:
+               /* signed register, result in mA */
+-              val = (s16)regval * data->current_lsb_uA;
++              val = ((s16)regval >> data->config->current_shift) *
++                data->current_lsb_uA;
+               val = DIV_ROUND_CLOSEST(val, 1000);
+               break;
+       case INA2XX_CALIBRATION:
+@@ -385,6 +399,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+       case INA2XX_SHUNT_VOLTAGE:
+               val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
+               val *= data->config->shunt_div;
++              val <<= data->config->shunt_voltage_shift;
+               return clamp_val(val, 0, SHRT_MAX);
+       case INA2XX_BUS_VOLTAGE:
+               val = clamp_val(val, 0, 200000);
+@@ -399,6 +414,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+               val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
+               /* signed register, result in mA */
+               val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
++              val <<= data->config->current_shift;
+               return clamp_val(val, SHRT_MIN, SHRT_MAX);
+       default:
+               /* programmer goofed */
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
new file mode 100644 (file)
index 0000000..e8d3eb0
--- /dev/null
@@ -0,0 +1,54 @@
+From 94fd25f1dffd0f1ba4ecbec547b8730509fdd697 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 15:27:28 -0700
+Subject: hwmon: (lm90) Only report alarms if driver is ready
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
+
+Userspace can read sysfs attributes before driver registration is complete,
+immediately after devm_hwmon_device_register_with_info() has been called.
+At that time, data->hwmon_dev is not yet initialized. This can trigger
+a NULL pointer access since lm90_update_device() and with it
+lm90_update_alarms_locked() will be called. This call schedules
+report_work and lm90_report_alarms(), which passes the still-NULL
+data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer
+dereference.
+
+Fix the problem by only scheduling the report and alert workers
+data->hwmon_dev is set.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/lm90.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
+index a465a8a7ef5af..e1795ccde876c 100644
+--- a/drivers/hwmon/lm90.c
++++ b/drivers/hwmon/lm90.c
+@@ -1196,7 +1196,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+               check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+                       (data->config & 0x80);
+-              if (force || check_enable)
++              if (data->hwmon_dev && (force || check_enable))
+                       schedule_work(&data->report_work);
+               /*
+@@ -1204,7 +1204,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+                * alarms are all clear, and alerts are currently disabled.
+                * Otherwise (re)schedule worker if needed.
+                */
+-              if (check_enable) {
++              if (check_enable && data->hwmon_dev) {
+                       if (!(data->current_alarms & data->alert_alarms)) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               lm90_update_confreg(data, data->config & ~0x80);
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
new file mode 100644 (file)
index 0000000..6ef2d36
--- /dev/null
@@ -0,0 +1,39 @@
+From 15cc8d5c81072885cd703c97ff0de71e5f8cc49e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 12:27:15 -0800
+Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ]
+
+Coverity reports an out-of-bounds access when reading the minimum alarm
+voltage for the VGPIO channel. Add the missing return statement to fix
+the problem.
+
+Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282")
+Cc: Nuno Sa <nuno.sa@analog.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ltc4282.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
+index 58c2d3a624329..4000bcbc7353d 100644
+--- a/drivers/hwmon/ltc4282.c
++++ b/drivers/hwmon/ltc4282.c
+@@ -384,8 +384,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
+                                                  channel, val);
+       case hwmon_in_min_alarm:
+               if (channel == LTC4282_CHAN_VGPIO)
+-                      ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
+-                                         LTC4282_GPIO_ALARM_L_MASK, val);
++                      return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
++                                                LTC4282_GPIO_ALARM_L_MASK, val);
+               return ltc4282_vdd_source_read_alm(st,
+                                                  LTC4282_VSOURCE_ALARM_L_MASK,
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch
new file mode 100644 (file)
index 0000000..9cd6050
--- /dev/null
@@ -0,0 +1,90 @@
+From 9d79fec841fa41cb393c2140c67165598e831bfa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:14:36 -0700
+Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
+
+Unlike NCT6106, NCT6116 only has three temperature registers, and with
+it only three temperature source and temperature source configuration
+registers. The register addresses match those of NCT6106 and can be
+re-used.
+
+The code used a separate array to list the temperature source registers
+for NCT6116, but used the size of the NCT6106 register array to set
+the number of registers. The NCT6106 register array provides six addresses,
+while the temperature source register array for NCT6116 only provides three
+addresses. This causes a KASAN report.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775]
+Read of size 2 at addr ffffffffc19561a6 by task modprobe/954
+...
+Call Trace:
+ dump_stack+0x7d/0xa7
+ print_address_description.constprop.0+0x1c/0x220
+ ? __kasan_kmalloc.constprop.0+0xc9/0xd0
+ ? __kmalloc_node_track_caller+0x194/0x5b0
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+...
+
+Fix the problem by hard-coding the number of temperature and temperature
+configuration registers to three for NCT6116. Drop the unnecessary
+NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
+
+Reported-by: Florian Bezdeka <florian.bezdeka@siemens.com>
+Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 79bc67ffb9986..506d57025c3bd 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+ static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+ static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+ static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+-      0xb0, 0xb1, 0xb2 };
+ static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+       0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                 = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+               data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+               data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+-              data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
++              data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
+-              num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
++              num_reg_temp = 3;
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
+               num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+-              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
++              num_reg_temp_config = 3;
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..56e55ab
--- /dev/null
@@ -0,0 +1,74 @@
+From b0702ea6ff50e80d8f03cc4ca06c9b622988c86c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 506d57025c3bd..ce2cf1f229004 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch
new file mode 100644 (file)
index 0000000..d9a77ad
--- /dev/null
@@ -0,0 +1,53 @@
+From ed6d42a723fa25929814e8f31f16d397de5e8eee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 09:54:23 -0700
+Subject: hwmon: (nzxt-smart2) DMA-align output buffer
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
+
+Sashiko reports:
+
+When send_output_report() calls hid_hw_output_report(), the underlying USB
+HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
+
+When the DMA mapping flushes or invalidates the cacheline, it will corrupt
+the adjacent variables (mutex, update_interval) that were modified
+concurrently by the CPU. This causes memory corruption due to cacheline
+sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA
+API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings
+for this violation.
+
+Any operation that triggers send_output_report() (like setting a fan speed
+or updating the interval) causes the USB DMA mapping. On systems with
+non-coherent caches, this structural bug causes immediate and deterministic
+memory corruption.
+
+Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.")
+Cc: Aleksandr Mezin <mezin.alexander@gmail.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nzxt-smart2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
+index e2316c46629d6..ff0c0bee0e839 100644
+--- a/drivers/hwmon/nzxt-smart2.c
++++ b/drivers/hwmon/nzxt-smart2.c
+@@ -203,7 +203,7 @@ struct drvdata {
+        */
+       struct mutex mutex;
+       long update_interval;
+-      u8 output_buffer[OUTPUT_REPORT_SIZE];
++      u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ };
+ static long scale_pwm_value(long val, long orig_max, long new_max)
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..b36ef1c
--- /dev/null
@@ -0,0 +1,39 @@
+From de1b7bb75fcd9820ae9324fba910c7b663ab8262 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index ddf64e72751cc..4e1c66670ead9 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -512,7 +512,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS");
+-- 
+2.53.0
+
diff --git a/queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch
new file mode 100644 (file)
index 0000000..fd06a21
--- /dev/null
@@ -0,0 +1,76 @@
+From bd795d62339f6bf7835052bc44bed452d8dd010d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 09:34:46 -0700
+Subject: hwmon: (sht3x) Fix unaligned accesses
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ]
+
+Sashiko reports:
+
+In sht3x_update_client(), the 16-bit temperature and humidity values are
+extracted from a stack-allocated byte array using be16_to_cpup(). The
+pointers passed to this function are calculated as buf and buf + 3. Since
+the difference between the two pointers is an odd number of bytes, at
+least one of them is guaranteed to be at an unaligned offset.
+
+This will trigger an alignment fault on strict-alignment architectures
+such as ARMv5 or SPARC, resulting in a kernel panic.
+
+Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(),
+and put_unaligned_be16() instead of cpu_to_be16().
+
+Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/sht3x.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
+index f36c0229328fa..5d04013efb522 100644
+--- a/drivers/hwmon/sht3x.c
++++ b/drivers/hwmon/sht3x.c
+@@ -21,6 +21,7 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/jiffies.h>
++#include <linux/unaligned.h>
+ /* commands (high repeatability mode) */
+ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 };
+@@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev)
+               if (ret)
+                       goto out;
+-              val = be16_to_cpup((__be16 *)buf);
++              val = get_unaligned_be16(buf);
+               data->temperature = sht3x_extract_temperature(val);
+-              val = be16_to_cpup((__be16 *)(buf + 3));
++              val = get_unaligned_be16(buf + 3);
+               data->humidity = sht3x_extract_humidity(val);
+               data->last_update = jiffies;
+       }
+@@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data)
+               if (ret)
+                       return ret;
+-              raw = be16_to_cpup((__be16 *)buffer);
++              raw = get_unaligned_be16(buffer);
+               temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
+               humidity = sht3x_extract_humidity(raw & 0xfe00);
+               data->temperature_limits[index] = temperature;
+@@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev,
+       raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
+       raw |= ((humidity * 42950) >> 16) & 0xfe00;
+-      *((__be16 *)position) = cpu_to_be16(raw);
++      put_unaligned_be16(raw, position);
+       position += SHT3X_WORD_LEN;
+       *position = crc8(sht3x_crc8_table,
+                        position - SHT3X_WORD_LEN,
+-- 
+2.53.0
+
diff --git a/queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch b/queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch
new file mode 100644 (file)
index 0000000..e95841a
--- /dev/null
@@ -0,0 +1,89 @@
+From 6a496d074d0b106a771d2c2f3ed680f821ae008a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 13:50:06 +0200
+Subject: ice: suppress DPLL errors during reset recovery
+
+From: Przemyslaw Korba <przemyslaw.korba@intel.com>
+
+[ Upstream commit b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 ]
+
+During reset recovery, the admin queue returns EBUSY which is expected
+behavior. However, the DPLL subsystem was logging these as errors and
+incrementing the error counter, potentially leading to unnecessary
+warnings and even disabling the DPLL periodic worker if the threshold
+was reached.
+
+Suppress error logging and error counter increments when the admin
+queue returns EBUSY, as this is expected during reset recovery and
+not a real failure condition.
+
+test case:
+- ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared
+mac-shared phy-shared ram-shared
+- observe if dmesg EBUSY errors are gone
+
+Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu")
+Signed-off-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
+index 023d0de12670c..2a47d9f3cf7ef 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
++++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
+@@ -703,7 +703,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+                                  ret,
+                                  libie_aq_str(pf->hw.adminq.sq_last_status),
+                                  pin_type_name[pin_type], pin->idx);
+-      else
++      else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
+               dev_err_ratelimited(ice_pf_to_dev(pf),
+                                   "err:%d %s failed to update %s pin:%u\n",
+                                   ret,
+@@ -2669,7 +2669,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf,
+       *phase_offset_pins_updated = 0;
+       ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
+                                              ARRAY_SIZE(meas));
+-      if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) {
++      if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN ||
++                  pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) {
+               return 0;
+       } else if (ret) {
+               dev_err(ice_pf_to_dev(pf),
+@@ -2731,10 +2732,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
+               d->dpll_idx, d->prev_input_idx, d->input_idx,
+               d->dpll_state, d->prev_dpll_state, d->mode);
+       if (ret) {
+-              dev_err(ice_pf_to_dev(pf),
+-                      "update dpll=%d state failed, ret=%d %s\n",
+-                      d->dpll_idx, ret,
+-                      libie_aq_str(pf->hw.adminq.sq_last_status));
++              /* EBUSY is expected during reset recovery, don't log error */
++              if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
++                      dev_err(ice_pf_to_dev(pf),
++                              "update dpll=%d state failed, ret=%d %s\n",
++                              d->dpll_idx, ret,
++                              libie_aq_str(pf->hw.adminq.sq_last_status));
+               return ret;
+       }
+       if (init) {
+@@ -2803,7 +2806,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
+           d->periodic_counter % dp->phase_offset_monitor_period == 0)
+               ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf);
+       if (ret) {
+-              d->cgu_state_acq_err_num++;
++              /* EBUSY is expected during reset recovery */
++              if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
++                      d->cgu_state_acq_err_num++;
+               /* stop rescheduling this worker */
+               if (d->cgu_state_acq_err_num >
+                   ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
+-- 
+2.53.0
+
diff --git a/queue-6.18/idpf-adjust-txq-ring-count-minimum.patch b/queue-6.18/idpf-adjust-txq-ring-count-minimum.patch
new file mode 100644 (file)
index 0000000..a2a2e46
--- /dev/null
@@ -0,0 +1,72 @@
+From bb171e0f25224ae957c75eab6cbc9136187cd33a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Jun 2026 16:56:19 -0700
+Subject: idpf: adjust TxQ ring count minimum
+
+From: Joshua Hay <joshua.a.hay@intel.com>
+
+[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ]
+
+Set the TxQ ring count minimum to 128 descriptors. Any lower than this,
+and the queue will stall and trigger Tx timeouts in flow based
+scheduling mode. This is because next_to_clean might never be updated.
+
+In flow based scheduling mode, next_to_clean is only updated after a
+descriptor completion is processed, i.e. after the RE bit is set in the
+last descriptor of a Tx packet. This will never happen with a ring size
+of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value
+of last_re is initialized/set to, the calculated gap will be at most 63
+and never trigger the RE bit.
+
+Even a ring size of 96 does not solve this. Because of how infrequent
+next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED
+will be much smaller on average. This increases the chance the queue
+will be stopped because a multi-descriptor packet, e.g. a large LSO
+packet, does not see enough resources on the ring. In this case, the
+queue will trigger the stop logic. The queue permanently stalls because
+there is no chance for a descriptor completion to update next_to_clean
+since it is dependent on a packet being sent.
+
+Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool")
+Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +----
+ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +-
+ 2 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+index c859665b2dc89..e6563ad31f5ca 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+@@ -3075,10 +3075,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
+               tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
+               tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
+-              /* Set the RE bit to periodically "clean" the descriptor ring.
+-               * MIN_GAP is set to MIN_RING size to ensure it will be set at
+-               * least once each time around the ring.
+-               */
++              /* Set the RE bit periodically to "clean" the descriptor ring */
+               if (idpf_tx_splitq_need_re(tx_q)) {
+                       tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE;
+                       tx_q->txq_grp->num_completions_pending++;
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+index 423cc9486dce7..aa0b93b5c8599 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+@@ -21,7 +21,7 @@
+ /* Mailbox Queue */
+ #define IDPF_MAX_MBXQ                         1
+-#define IDPF_MIN_TXQ_DESC                     64
++#define IDPF_MIN_TXQ_DESC                     128
+ #define IDPF_MIN_RXQ_DESC                     64
+ #define IDPF_MIN_TXQ_COMPLQ_DESC              256
+ #define IDPF_MAX_QIDS                         256
+-- 
+2.53.0
+
diff --git a/queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
new file mode 100644 (file)
index 0000000..b252cfc
--- /dev/null
@@ -0,0 +1,41 @@
+From 9821cd97f88033c77bcff5e72555b328f724d958 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 01:03:32 -0400
+Subject: idpf: Fix mailbox IRQ name leak on request failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ]
+
+idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling
+request_irq(). On success, the name is released later through
+kfree(free_irq()), but request_irq() failure returns without freeing it.
+
+Free the allocated name on the request_irq() failure path.
+
+Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+index 131a8121839bd..590119f4097d0 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "IRQ request for mailbox failed, error: %d\n", err);
+-
++              kfree(name);
+               return err;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch b/queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch
new file mode 100644 (file)
index 0000000..c0443f2
--- /dev/null
@@ -0,0 +1,77 @@
+From d40e86fb49d7934842fd144aa5c51aa18740ed28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jun 2026 14:52:29 +0200
+Subject: iomap: add a separate bio_set for iomap_split_ioend
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 ]
+
+iomap_split_ioend can split bios that already come from
+iomap_ioend_bioset and thus deadlock when the bioset is exhausted.
+
+Add a separate bio_set to avoid this deadlock.
+
+Christian Brauner <brauner@kernel.org> says:
+Mark iomap_ioend_split_bioset static as it is only used in ioend.c,
+fixing the sparse warning reported by the kernel test robot.
+
+Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers")
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/iomap/ioend.c | 21 +++++++++++++++++++--
+ 1 file changed, 19 insertions(+), 2 deletions(-)
+
+diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
+index 6c75fbf4d5af4..2a86b05c88efc 100644
+--- a/fs/iomap/ioend.c
++++ b/fs/iomap/ioend.c
+@@ -11,6 +11,7 @@
+ struct bio_set iomap_ioend_bioset;
+ EXPORT_SYMBOL_GPL(iomap_ioend_bioset);
++static struct bio_set iomap_ioend_split_bioset;
+ struct iomap_ioend *iomap_init_ioend(struct inode *inode,
+               struct bio *bio, loff_t file_offset, u16 ioend_flags)
+@@ -409,7 +410,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend,
+       sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT,
+                       i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT;
+-      split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset);
++      split = bio_split(bio, sector_offset, GFP_NOFS,
++                      &iomap_ioend_split_bioset);
+       if (IS_ERR(split))
+               return ERR_CAST(split);
+       split->bi_private = bio->bi_private;
+@@ -432,8 +434,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend);
+ static int __init iomap_ioend_init(void)
+ {
+-      return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
++      const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE);
++      int error;
++
++      error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries,
+                          offsetof(struct iomap_ioend, io_bio),
+                          BIOSET_NEED_BVECS);
++      if (error)
++              return error;
++      error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries,
++                         offsetof(struct iomap_ioend, io_bio),
++                         BIOSET_NEED_BVECS);
++      if (error)
++              goto out_exit_ioend_bioset;
++      return 0;
++
++out_exit_ioend_bioset:
++      bioset_exit(&iomap_ioend_bioset);
++      return error;
+ }
+ fs_initcall(iomap_ioend_init);
+-- 
+2.53.0
+
diff --git a/queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch b/queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch
new file mode 100644 (file)
index 0000000..6c85388
--- /dev/null
@@ -0,0 +1,67 @@
+From 7e29c6e0d6bb9e37597bc9f9276b825b6525587a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Jul 2026 22:36:11 -0700
+Subject: iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a
+ vDEVICE
+
+From: Nicolin Chen <nicolinc@nvidia.com>
+
+[ Upstream commit c3b8ee84a965058b41275069d4696f37a8b14bf6 ]
+
+arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID
+taken from master->streams[0], assuming a device has exactly one stream. A
+device with several streams gets only its first one mapped, so a guest vSID
+invalidation cannot reach the others' ATC and IOTLB entries; a device with
+none makes master->streams a ZERO_SIZE_PTR, read out of bounds.
+
+Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP
+when master->num_streams is not one, rather than mapping it silently.
+
+Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object")
+Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Assisted-by: Claude:claude-opus-4-8
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c   | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+index 9ec5591565083..a52f86454f77d 100644
+--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+@@ -285,6 +285,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid)
+       return ret;
+ }
++static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev)
++{
++      struct device *dev = iommufd_vdevice_to_device(vdev);
++      struct arm_smmu_master *master = dev_iommu_priv_get(dev);
++
++      /*
++       * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so
++       * more streams would leave the rest stale and none reads out of bounds.
++       */
++      if (master->num_streams != 1)
++              return -EOPNOTSUPP;
++      return 0;
++}
++
+ /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */
+ struct arm_vsmmu_invalidation_cmd {
+       union {
+@@ -391,6 +405,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu,
+ static const struct iommufd_viommu_ops arm_vsmmu_ops = {
+       .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
+       .cache_invalidate = arm_vsmmu_cache_invalidate,
++      .vdevice_init = arm_vsmmu_vdevice_init,
+ };
+ size_t arm_smmu_get_viommu_size(struct device *dev,
+-- 
+2.53.0
+
diff --git a/queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
new file mode 100644 (file)
index 0000000..188683d
--- /dev/null
@@ -0,0 +1,335 @@
+From 3ae1bd04c685d0fcec46430b7bc5205c1c4f20b6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:17 +0300
+Subject: ipvs: do not mangle ICMP replies for non-first fragments
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
+
+Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the
+payload for embedded non-first IPv4 fragments. The problem is
+in the very old inverted pp->dont_defrag check which should not
+continue when embedded is a non-first TCP/UDP/SCTP fragment.
+
+Check for embedded non-first fragment is also missing from
+ip_vs_out_icmp_v6(), it is needed before any connection
+lookups that expect ports after the network headers.
+
+Drop the blocking code from ip_vs_in_icmp_v6() which prevents
+ICMPv6 from local clients to use non-MASQ forwarding.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h             | 11 +++---
+ net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++---------------------
+ net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++----
+ 3 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 71ffbba542464..17a97086b294f 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1640,8 +1640,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, unsigned int toff,
+-                  unsigned int wlen, unsigned int hooknum,
+-                  struct ip_vs_iphdr *ciph);
++                  unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1655,8 +1654,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, unsigned int toff,
+-                     unsigned int wlen, unsigned int hooknum,
+-                     struct ip_vs_iphdr *ciph);
++                     unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1721,12 +1719,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir, unsigned int toff);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff,
++                  bool has_ports);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph);
++                     bool has_ports, struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 784c00ec01ef7..6207a91e93f3b 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout, unsigned int toff)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff,
++                  bool has_ports)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+       struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+-          IPPROTO_SCTP == ciph->protocol) {
++      if (has_ports) {
+               __be16 *ports = (void *)ciph + ciph->ihl*4;
+               if (inout)
+@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int inout, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph)
++                     bool has_ports, struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      int protocol;
+       struct icmp6hdr *icmph;
+       struct ipv6hdr *cih;
+       icmph = (struct icmp6hdr *)(skb->data + toff);
+       cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ciph->protocol;
+-
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+               cih->daddr = cp->vaddr.in6;
+@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!ciph->fragoffs &&
+-          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
+-           protocol == IPPROTO_SCTP)) {
++      if (has_ports) {
+               __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       unsigned int ctoff = ciph->len;
++      bool has_ports = false;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       }
+       if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+-          ciph->protocol == IPPROTO_SCTP)
++          ciph->protocol == IPPROTO_SCTP) {
+               ctoff += 2 * sizeof(__u16);
++              has_ports = true;
++      }
+       if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!pp)
+               return NF_ACCEPT;
++      /* Is the embedded protocol header present? */
++      if (unlikely(ciph.fragoffs && !pp->dont_defrag))
++              return NF_ACCEPT;
++
+       /* The embedded headers contain source and dest in reverse order */
+       cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
+                            ipvs, AF_INET6, skb, &ciph);
+@@ -1687,8 +1688,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       pp = pd->pp;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1696,7 +1696,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       offset2 = offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
+-      offset = ciph.len;
+       /* The embedded headers contain source and dest in reverse order.
+        * For IPIP/UDP/GRE tunnel this is error for request, not for reply.
+@@ -1790,11 +1789,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+-          IPPROTO_SCTP == cih->protocol)
+-              offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
+-                                &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1854,8 +1849,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       pp = pd->pp;
+-      /* Cannot handle fragmented embedded protocol */
+-      if (ciph.fragoffs)
++      /* Is the embedded protocol header present? */
++      if (ciph.fragoffs && !pp->dont_defrag)
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+@@ -1879,13 +1874,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               new_cp = true;
+       }
+-      /* VS/TUN, VS/DR and LOCALNODE just let it go */
+-      if ((hooknum == NF_INET_LOCAL_OUT) &&
+-          (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+-              verdict = NF_ACCEPT;
+-              goto out;
+-      }
+-
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+@@ -1901,14 +1889,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      /* Need to mangle contained IPv6 header in ICMPv6 packet */
+-      offset = ciph.len;
+-      if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+-          IPPROTO_SCTP == ciph.protocol)
+-              offset += 2 * sizeof(__u16); /* Also mangle ports */
+-
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
+-                                   &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index fba791d140e70..ac1827ef78949 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1500,13 +1500,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+       int local;
+       int rt_mode, was_input;
++      bool has_ports = false;
++      unsigned int wlen;
+       /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
+          forwarded directly here, because there is no need to
+@@ -1562,6 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1569,7 +1577,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1586,10 +1594,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
++      bool has_ports = false;
+       struct rt6_info *rt;    /* Route to the other host */
++      unsigned int wlen;
+       int rc;
+       int local;
+       int rt_mode;
+@@ -1647,6 +1656,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1654,7 +1670,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch
new file mode 100644 (file)
index 0000000..3de0c95
--- /dev/null
@@ -0,0 +1,624 @@
+From 5c1dacf7ce4465603548c912f753021ed444ddca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:16 +0300
+Subject: ipvs: fix places with wrong packet offsets
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
+
+The offsets we use to packet headers and payloads should be
+based on skb->data. We even already respect non-zero
+network offset in ip_vs_fill_iph_skb() but some places
+do it wrongly and support only zero offset which is expected
+for the IP layer where IPVS has hooks.
+
+Change all places that instead of skb->data use offsets based
+on the network header (skb_network_header, ip_hdr, etc) because
+this doubles the network offset as noted by Sashiko.
+
+For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header
+parsing done by the caller.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   |  15 +--
+ net/netfilter/ipvs/ip_vs_app.c        |   4 +-
+ net/netfilter/ipvs/ip_vs_core.c       | 133 +++++++++++++-------------
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_xmit.c       |  26 ++---
+ 7 files changed, 97 insertions(+), 93 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 7dcaadc32a76f..71ffbba542464 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1639,8 +1639,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                  struct ip_vs_protocol *pp, int offset,
+-                  unsigned int hooknum, struct ip_vs_iphdr *iph);
++                  struct ip_vs_protocol *pp, unsigned int toff,
++                  unsigned int wlen, unsigned int hooknum,
++                  struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1653,8 +1654,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                     struct ip_vs_protocol *pp, int offset,
+-                     unsigned int hooknum, struct ip_vs_iphdr *iph);
++                     struct ip_vs_protocol *pp, unsigned int toff,
++                     unsigned int wlen, unsigned int hooknum,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1719,11 +1721,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                     struct ip_vs_conn *cp, int dir);
++                     struct ip_vs_conn *cp, int dir, unsigned int toff,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
+index f9132b359f0c6..0c690a30a85dc 100644
+--- a/net/netfilter/ipvs/ip_vs_app.c
++++ b/net/netfilter/ipvs/ip_vs_app.c
+@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 306a8227d300e..784c00ec01ef7 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+-      unsigned int icmp_offset = iph->ihl*4;
+-      struct icmphdr *icmph    = (struct icmphdr *)(skb_network_header(skb) +
+-                                                    icmp_offset);
++      struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+       struct iphdr *ciph       = (struct iphdr *)(icmph + 1);
++      unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+       if (inout) {
+               iph->saddr = cp->vaddr.ip;
+@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->checksum = 0;
+-      icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
++      icmph->checksum = ip_vs_checksum_complete(skb, toff);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered outgoing ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered outgoing ICMP");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered incoming ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered incoming ICMP");
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                     struct ip_vs_conn *cp, int inout, unsigned int toff,
++                     struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      unsigned int icmp_offset = 0;
+-      unsigned int offs        = 0; /* header offset*/
+       int protocol;
+       struct icmp6hdr *icmph;
+-      struct ipv6hdr *ciph;
+-      unsigned short fragoffs;
++      struct ipv6hdr *cih;
+-      ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+-      icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+-      offs = icmp_offset + sizeof(struct icmp6hdr);
+-      ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
++      icmph = (struct icmp6hdr *)(skb->data + toff);
++      cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
++      protocol = ciph->protocol;
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+-              ciph->daddr = cp->vaddr.in6;
++              cih->daddr = cp->vaddr.in6;
+       } else {
+               iph->daddr = cp->daddr.in6;
+-              ciph->saddr = cp->daddr.in6;
++              cih->saddr = cp->daddr.in6;
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-                        IPPROTO_SCTP == protocol)) {
+-              __be16 *ports = (void *)(skb_network_header(skb) + offs);
++      if (!ciph->fragoffs &&
++          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
++           protocol == IPPROTO_SCTP)) {
++              __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+                             ntohs(inout ? ports[1] : ports[0]),
+@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
+-                                            skb->len - icmp_offset,
++                                            skb->len - toff,
+                                             IPPROTO_ICMPV6, 0);
+-      skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
++      skb->csum_start = skb_headroom(skb) + toff;
+       skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered outgoing ICMPv6");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered incoming ICMPv6");
+ }
+ #endif
+@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+  */
+ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+-                              __u8 protocol, struct ip_vs_conn *cp,
++                              struct ip_vs_conn *cp,
+                               struct ip_vs_protocol *pp,
+-                              unsigned int offset, unsigned int ihl,
+-                              unsigned int hooknum)
++                              struct ip_vs_iphdr *ciph,
++                              unsigned int toff, unsigned int hooknum)
+ {
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
++      unsigned int ctoff = ciph->len;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
++      if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+               goto out;
+       }
+-      if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-          IPPROTO_SCTP == protocol)
+-              offset += 2 * sizeof(__u16);
+-      if (skb_ensure_writable(skb, offset))
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP)
++              ctoff += 2 * sizeof(__u16);
++      if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+  *    Currently handles error types - unreachable, quench, ttl exceeded.
+  */
+ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+-                        int *related, unsigned int hooknum)
++                        int *related, unsigned int hooknum,
++                        struct ip_vs_iphdr *ipvsh)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = ipvsh->len;
++      offset = ipvsh->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &ipvsh->saddr.ip, &ipvsh->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* Now find the contained IP header */
+       offset += sizeof(_icmph);
+       cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+-      if (cih == NULL)
++      if (!(cih && cih->version == 4 && cih->ihl >= 5))
+               return NF_ACCEPT; /* The packet looks wrong, ignore */
+       pp = ip_vs_proto_get(cih->protocol);
+@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!cp)
+               return NF_ACCEPT;
+-      snet.ip = iph->saddr;
+-      return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+-                                  pp, ciph.len, ihl, hooknum);
++      snet.ip = ipvsh->saddr.ip;
++      return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
++                                  hooknum);
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       struct ip_vs_conn *cp;
+       struct ip_vs_protocol *pp;
+       union nf_inet_addr snet;
+-      unsigned int offset;
+       *related = 1;
+       ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
+@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       snet.in6 = ciph.saddr.in6;
+-      offset = ciph.len;
+-      return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+-                                  pp, offset, ipvsh->len, hooknum);
++      return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
++                                  ipvsh->len, hooknum);
+ }
+ #endif
+@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
+ #endif
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+-                      int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
++                      int verdict = ip_vs_out_icmp(ipvs, skb, &related,
++                                                   hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -1576,9 +1572,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
+  */
+ static int
+ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+-            unsigned int hooknum)
++            unsigned int hooknum, struct ip_vs_iphdr *iph)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1588,7 +1583,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       unsigned int offset, offset2, ihl, verdict;
+       bool tunnel, new_cp = false;
+       union nf_inet_addr *raddr;
+-      char *outer_proto = "IPIP";
++      char *outer_proto __maybe_unused = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
+@@ -1598,17 +1593,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = iph->len;
++      offset = iph->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &iph->saddr.ip, &iph->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1725,7 +1722,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+           !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+-                        &iph->saddr);
++                        &iph->saddr.ip);
+               goto out;
+       }
+@@ -1796,7 +1793,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+           IPPROTO_SCTP == cih->protocol)
+               offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
++                                &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1909,7 +1907,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+           IPPROTO_SCTP == ciph.protocol)
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
++                                   &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1988,7 +1987,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+-                                                  hooknum);
++                                                  hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -2124,6 +2123,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+                  const struct nf_hook_state *state)
+ {
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
++      struct ip_vs_iphdr iphdr;
+       int r;
+       /* ipvs enabled in this netns ? */
+@@ -2133,10 +2133,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+       if (state->pf == NFPROTO_IPV4) {
+               if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
+                       return NF_ACCEPT;
++              ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
+ #ifdef CONFIG_IP_VS_IPV6
+       } else {
+-              struct ip_vs_iphdr iphdr;
+-
+               ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
+               if (iphdr.protocol != IPPROTO_ICMPV6)
+@@ -2146,7 +2145,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+ #endif
+       }
+-      return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
++      return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
+ }
+ static const struct nf_hook_ops ip_vs_ops4[] = {
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index f6f732b7dfa86..3dbd3096e1637 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index bf31127338aa0..1ac9c233537d3 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->source = cp->vport;
+       /* Adjust TCP checksums */
+@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index 40d30649b3048..96ac882df15c1 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->source = cp->vport;
+       /*
+@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index f564b4197cee9..fba791d140e70 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1499,8 +1499,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+  */
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *iph)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+@@ -1512,7 +1513,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, iph);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1530,7 +1531,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+-                                 NULL, iph);
++                                 NULL, ciph);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
+@@ -1562,13 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1584,8 +1585,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ #ifdef CONFIG_IP_VS_IPV6
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *ipvsh)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rt6_info *rt;    /* Route to the other host */
+       int rc;
+@@ -1597,7 +1599,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, ipvsh);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1614,7 +1616,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+-                                    &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
++                                    &cp->daddr.in6, NULL, ciph, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = dst_rt6_info(skb_dst(skb));
+@@ -1646,13 +1648,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.18/ipvs-fix-the-checksum-validations.patch b/queue-6.18/ipvs-fix-the-checksum-validations.patch
new file mode 100644 (file)
index 0000000..b796e9c
--- /dev/null
@@ -0,0 +1,389 @@
+From 5340f7071f1f09d1c6066b1be5e9c6e2ee477daa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:15 +0300
+Subject: ipvs: fix the checksum validations
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
+
+ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6
+packets from clients. In fact, as for TCP/UDP we should
+validate the checksum for ICMP packets only when we
+mangle the packets on MASQ or on reply for tunnel.
+
+Also, Sashiko points out that handle_response_icmp() being
+common for IPv4 and IPv6 is missing the pseudo-header
+calculation while validating ICMPv6 messages from real
+servers which is a problem if checksum is not validated
+by the hardware.
+
+Fix the problems by creating ip_vs_checksum_common_check()
+helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6.
+Rely on the nf_checksum() for validating the ICMP messages
+but use it also for TCP and UDP.
+
+Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
+
+IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum
+validation on LOCAL_OUT (local clients or local real
+servers) and on FORWARD (traffic from servers on LAN).
+Do it only on LOCAL_IN, in case nf_checksum() is not
+called on PRE_ROUTING.
+
+Also, ip_vs_checksum_complete() can be marked static.
+
+Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6")
+Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   | 31 +++++++++++++++--
+ net/netfilter/ipvs/ip_vs_core.c       | 20 +++++++++--
+ net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++----
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  | 44 +++++------------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  | 50 ++++++---------------------
+ 5 files changed, 74 insertions(+), 86 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 2be04ca409d1f..7dcaadc32a76f 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -24,7 +24,9 @@
+ #include <linux/netfilter.h>          /* for union nf_inet_addr */
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>                       /* for struct ipv6hdr */
++#include <net/route.h>
+ #include <net/ipv6.h>
++#include <net/ip6_fib.h>
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ #include <net/netfilter/nf_conntrack.h>
+ #endif
+@@ -1724,8 +1726,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+-
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+ {
+       __be32 diff[2] = { ~old, new };
+@@ -1751,6 +1751,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
+       return csum_partial(diff, sizeof(diff), oldsum);
+ }
++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
++{
++      /* Checksum unnecessary or already validated? */
++      if (skb_csum_unnecessary(skb))
++              return false;
++      /* LOCAL_OUT ? */
++      if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
++              return false;
++      /* !LOCAL_IN (FORWARD) ? */
++      if (af == AF_INET6) {
++              if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
++                      return false;
++      } else {
++              if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
++                      return false;
++      }
++      return true;
++}
++
++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
++                                             int offset, int proto, int af)
++{
++      if (!ip_vs_checksum_needed(skb, af))
++              return true;
++      return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
++}
++
+ /* Forget current conntrack (unconfirmed) and attach notrack entry */
+ static inline void ip_vs_notrack(struct sk_buff *skb)
+ {
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 90619453cb6f0..306a8227d300e 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+ {
+       return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
+ }
+@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               unsigned int offset, unsigned int ihl,
+                               unsigned int hooknum)
+ {
++      int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+@@ -1720,7 +1721,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
++          !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+                         &iph->saddr);
+@@ -1886,6 +1888,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               goto out;
+       }
++      verdict = NF_DROP;
++
++      /* Ensure the checksum is correct */
++      if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
++          !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
++                                       AF_INET6)) {
++              /* Failed checksum! */
++              IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
++                        &iph->saddr);
++              goto out;
++      }
++
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index c67317be17dfa..f6f732b7dfa86 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -11,7 +11,7 @@
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff);
++              struct ip_vs_iphdr *iph);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff)
++              struct ip_vs_iphdr *iph)
+ {
++      unsigned int sctphoff = iph->len;
+       struct sctphdr *sh;
+       __le32 cmp, val;
++      if (!ip_vs_checksum_needed(skb, af))
++              return 1;
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+       if (val != cmp) {
+               /* CRC failure, dump it. */
+-              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                              "Failed checksum for");
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
+               return 0;
+       }
+       return 1;
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index b382810156b2c..bf31127338aa0 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -30,7 +30,7 @@
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff)
++             struct ip_vs_iphdr *iph)
+ {
+-      switch (skb->ip_summed) {
+-      case CHECKSUM_NONE:
+-              skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+-              fallthrough;
+-      case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-              if (af == AF_INET6) {
+-                      if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                          &ipv6_hdr(skb)->daddr,
+-                                          skb->len - tcphoff,
+-                                          IPPROTO_TCP,
+-                                          skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              } else
+-#endif
+-                      if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                            ip_hdr(skb)->daddr,
+-                                            skb->len - tcphoff,
+-                                            ip_hdr(skb)->protocol,
+-                                            skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              break;
+-      default:
+-              /* No need to checksum. */
+-              break;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+-
+       return 1;
+ }
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index dbd4155bb0752..40d30649b3048 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -26,7 +26,7 @@
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff)
++             struct ip_vs_iphdr *iph)
+ {
+       struct udphdr _udph, *uh;
+-      uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
++      uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+-      if (uh->check != 0) {
+-              switch (skb->ip_summed) {
+-              case CHECKSUM_NONE:
+-                      skb->csum = skb_checksum(skb, udphoff,
+-                                               skb->len - udphoff, 0);
+-                      fallthrough;
+-              case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-                      if (af == AF_INET6) {
+-                              if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                                  &ipv6_hdr(skb)->daddr,
+-                                                  skb->len - udphoff,
+-                                                  IPPROTO_UDP,
+-                                                  skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      } else
+-#endif
+-                              if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                                    ip_hdr(skb)->daddr,
+-                                                    skb->len - udphoff,
+-                                                    ip_hdr(skb)->protocol,
+-                                                    skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      break;
+-              default:
+-                      /* No need to checksum. */
+-                      break;
+-              }
++      if (!uh->check)
++              return 1;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+       return 1;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..756425f
--- /dev/null
@@ -0,0 +1,64 @@
+From 8625d8c6fcbd177a4e1a31ed588d3dcea9563325 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index e105349794f23..b9ca9dc9b0c3f 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..17cc8b1
--- /dev/null
@@ -0,0 +1,63 @@
+From 4e42560edb7a328b70f5775632698cef77cced0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index b9ca9dc9b0c3f..fd95a0eb7a466 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
new file mode 100644 (file)
index 0000000..18ab8ef
--- /dev/null
@@ -0,0 +1,96 @@
+From 42edd53e1e1eef7e9058e3f6d6a25278ceb11b2a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:22:30 +0300
+Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return
+ type
+
+From: Fabrice Derepas <fabrice.derepas@canonical.com>
+
+[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ]
+
+Two correctness and type-hygiene issues exist in the DCP trusted keys
+implementation.
+
+First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob
+without checking if it exceeds MAX_KEY_SIZE.  If a crafted blob provides a
+payload_len larger than 128, the subsequent do_aead_crypto() call writes
+past the end of the p->key array into the adjacent p->blob buffer within
+the same struct trusted_key_payload -- the caller's own input, not
+unrelated kernel memory.  While not exploitable, this violates strict array
+bounds and triggers static analyzers.  Fix this by adding a validation
+check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the
+length, matching the checks already done in trusted_core.c.
+
+Second, calc_blob_len() calculates a sum in size_t that truncates to
+unsigned int on 64-bit platforms.  Because the DCP hardware is only present
+on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally
+equivalent in production, making this truncation harmless in practice.
+Nevertheless, updating the return type to size_t (and subsequently updating
+'blen' in the seal/unseal paths) resolves type-narrowing warnings and
+improves overall code hygiene.
+
+Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys")
+Signed-off-by: Fabrice Derepas <fabrice.derepas@canonical.com>
+Reviewed-by: David Gstir <david@sigma-star.at>
+Reviewed-by: Richard Weinberger <richard@nod.at>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c
+index 7b6eb655df0cb..c078adebe190e 100644
+--- a/security/keys/trusted-keys/trusted_dcp.c
++++ b/security/keys/trusted-keys/trusted_dcp.c
+@@ -69,7 +69,7 @@ static bool skip_zk_test;
+ module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
+ MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
+-static unsigned int calc_blob_len(unsigned int payload_len)
++static size_t calc_blob_len(unsigned int payload_len)
+ {
+       return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
+ }
+@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key)
+ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key;
+       blen = calc_blob_len(p->key_len);
+@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key = NULL;
+       if (b->fmt_version != DCP_BLOB_VERSION) {
+@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+       }
+       p->key_len = le32_to_cpu(b->payload_len);
++      if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) {
++              ret = -EINVAL;
++              goto out;
++      }
++
+       blen = calc_blob_len(p->key_len);
+       if (blen != p->blob_len) {
+-              pr_err("DCP blob has bad length: %i != %i\n", blen,
++              pr_err("DCP blob has bad length: %zu != %u\n", blen,
+                      p->blob_len);
+               ret = -EINVAL;
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
new file mode 100644 (file)
index 0000000..d71124a
--- /dev/null
@@ -0,0 +1,50 @@
+From df74112f5a6d213976cefe20c9ef930f2d59bcfc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:04:19 +0900
+Subject: ksmbd: fix use-after-free in __close_file_table_ids()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
+
+A ksmbd_file can remain alive after logical close while another session
+holds a temporary reference obtained through ksmbd_lookup_fd_inode().
+ksmbd_close_fd() currently marks the file closed and drops the idr-owned
+reference, but leaves the pointer published in the closing session's idr
+until the final reference is dropped.
+
+If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final()
+supplies the foreign session's file table to __ksmbd_close_fd(). The object
+is then freed without being removed from its owner's idr, and the owner
+session later dereferences the stale pointer during file-table teardown.
+
+Remove the volatile id from the owner's idr while ksmbd_close_fd() still
+holds that table's lock, and clear volatile_id before dropping
+the idr-owned reference. A later foreign final put then only performs
+physical destruction and cannot remove the object from the wrong table.
+
+Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp")
+Reported-by: Yunseong Kim <yunseong.kim@est.tech>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 0d3341927b483..51e37e89d1aa5 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      idr_remove(ft->idr, id);
++                      fp->volatile_id = KSMBD_NO_FID;
+                       closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+-- 
+2.53.0
+
diff --git a/queue-6.18/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.18/ksmbd-return-success-for-deferred-final-close.patch
new file mode 100644 (file)
index 0000000..d51ae4f
--- /dev/null
@@ -0,0 +1,64 @@
+From 255944d9c251b74e758c3643bae5c9957265b972 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jun 2026 19:41:08 +0900
+Subject: ksmbd: return success for deferred final close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ]
+
+ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
+reference. If another in-flight request still holds a reference, the final
+close is deferred until that request drops its reference.
+
+The function currently returns -EINVAL in that deferred-final-close case
+because fp is cleared when the reference count does not reach zero.  That
+turns a valid close into STATUS_FILE_CLOSED.
+
+smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
+closes the same directory handle before receiving the find response.
+The query holds a reference while it builds the response, so close must
+mark the handle closed and return success even though final teardown is
+delayed. Track whether the handle was successfully transitioned to
+FP_CLOSED and return success when only the final close is deferred.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 5b6d8bb8edb27..0d3341927b483 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+ {
+       struct ksmbd_file       *fp;
+       struct ksmbd_file_table *ft;
++      bool closed = false;
+       if (!has_file_id(id))
+               return 0;
+@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+               }
+@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+       write_unlock(&ft->lock);
+       if (!fp)
+-              return -EINVAL;
++              return closed ? 0 : -EINVAL;
+       __put_fd_final(work, fp);
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch b/queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch
new file mode 100644 (file)
index 0000000..b49ae5c
--- /dev/null
@@ -0,0 +1,121 @@
+From 31608e5104db439ca7e1ad0631b51a11950c3b6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:03:54 +0100
+Subject: KVM: arm64: Reject guest_memfd memslots when the VM has MTE
+
+From: Alexandru Elisei <alexandru.elisei@arm.com>
+
+[ Upstream commit 679d7201c1f09e37fa1c12ce28d84079c17fc87f ]
+
+The user cannot use MTE on VMAs created by mapping a guest_memfd file,
+as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED.
+
+When creating a guest_memfd backed memslot,
+kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for
+the VM and if guest_memfd has been mapped in a VMA that intersects the
+memslot.
+
+However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly
+states that the only condition for userspace_addr is for it to be a legal
+userspace address, but the mapping is not required to be valid nor
+populated at memslot creation.
+
+If userspace sets userspace_addr to an address that hasn't been mapped, or
+if userspace_addr belongs to a VMA that isn't backed by the guest_memfd
+file, or if the VMA doesn't intersect the memslot, memslot creation is
+successful and KVM ends up with a VM with MTE and guest_memfd-backed
+memslots.
+
+The same happens if the order is reversed: when userspace enables MTE, KVM
+does not check if memslots backed by guest_memfd are already present.
+
+Fix both issues by rejecting guest_memfd-backed memslots when MTE is
+enabled, and by rejecting MTE when guest_memfd-backed memslots are already
+present.
+
+Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory")
+Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
+Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
+Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
+Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/virt/kvm/api.rst |  6 ++++++
+ arch/arm64/kvm/arm.c           | 25 +++++++++++++++++++------
+ arch/arm64/kvm/mmu.c           |  4 ++++
+ 3 files changed, 29 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
+index 9f835f68b4fb9..10d8a366b2072 100644
+--- a/Documentation/virt/kvm/api.rst
++++ b/Documentation/virt/kvm/api.rst
+@@ -8259,6 +8259,12 @@ When this capability is enabled all memory in memslots must be mapped as
+ attempts to create a memslot with an invalid mmap will result in an
+ -EINVAL return.
++``guest_memfd``, even though it is an anonymous file, is not supported with MTE.
++Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability
++is enabled, or attempting to enable the MTE capability after
++``guest_memfd``-backed memslots have been created, will result in an -EINVAL
++return.
++
+ When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to
+ perform a bulk copy of tags to/from the guest.
+diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
+index 10fc6783cd608..5d2435dd76f35 100644
+--- a/arch/arm64/kvm/arm.c
++++ b/arch/arm64/kvm/arm.c
+@@ -96,14 +96,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
+               set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
+                       &kvm->arch.flags);
+               break;
+-      case KVM_CAP_ARM_MTE:
+-              mutex_lock(&kvm->lock);
+-              if (system_supports_mte() && !kvm->created_vcpus) {
+-                      r = 0;
+-                      set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
++      case KVM_CAP_ARM_MTE: {
++              struct kvm_memory_slot *memslot;
++              int bkt;
++
++              guard(mutex)(&kvm->lock);
++              if (!system_supports_mte() || kvm->created_vcpus)
++                      break;
++
++              r = 0;
++              guard(mutex)(&kvm->slots_lock);
++              kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) {
++                      if (kvm_slot_has_gmem(memslot)) {
++                              r = -EINVAL;
++                              break;
++                      }
+               }
+-              mutex_unlock(&kvm->lock);
++              if (r == 0)
++                      set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
+               break;
++
++      }
+       case KVM_CAP_ARM_SYSTEM_SUSPEND:
+               r = 0;
+               set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags);
+diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
+index 403a635483513..b4237699705b4 100644
+--- a/arch/arm64/kvm/mmu.c
++++ b/arch/arm64/kvm/mmu.c
+@@ -2323,6 +2323,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
+       if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
+               return -EINVAL;
++      /* guest_memfd is incompatible with MTE. */
++      if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm))
++              return -EINVAL;
++
+       hva = new->userspace_addr;
+       reg_end = hva + (new->npages << PAGE_SHIFT);
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch b/queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch
new file mode 100644 (file)
index 0000000..1ac0c28
--- /dev/null
@@ -0,0 +1,124 @@
+From 619cc5b0e4ab1838b4307a76d03d30ad1cf81d93 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Nov 2025 14:25:59 +0000
+Subject: mshv: adjust interrupt control structure for ARM64
+
+From: Jinank Jain <jinankjain@microsoft.com>
+
+[ Upstream commit 9d70ef7a18e0ec1653ac63020a13a5d4dda7cc0d ]
+
+Interrupt control structure (union hv_interupt_control) has different
+fields when it comes to x86 vs ARM64. Bring in the correct structure
+from HyperV header files and adjust the existing interrupt routing
+code accordingly.
+
+Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
+Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Stable-dep-of: 0289a67cd70b ("mshv: Fix level-triggered check on uninitialized data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c      | 6 ++++++
+ drivers/hv/mshv_irq.c          | 4 ++++
+ drivers/hv/mshv_root_hv_call.c | 6 ++++++
+ include/hyperv/hvhdk.h         | 6 ++++++
+ 4 files changed, 22 insertions(+)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 586a9f99b3405..311aa9fecbc48 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -164,8 +164,10 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd)
+       if (hv_scheduler_type != HV_SCHEDULER_TYPE_ROOT)
+               return -EOPNOTSUPP;
++#if IS_ENABLED(CONFIG_X86)
+       if (irq->lapic_control.logical_dest_mode)
+               return -EOPNOTSUPP;
++#endif
+       vp = partition->pt_vp_array[irq->lapic_apic_id];
+@@ -197,8 +199,10 @@ static void mshv_assert_irq_slow(struct mshv_irqfd *irqfd)
+       unsigned int seq;
+       int idx;
++#if IS_ENABLED(CONFIG_X86)
+       WARN_ON(irqfd->irqfd_resampler &&
+               !irq->lapic_control.level_triggered);
++#endif
+       idx = srcu_read_lock(&partition->pt_irq_srcu);
+       if (irqfd->irqfd_girq_ent.guest_irq_num) {
+@@ -469,6 +473,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+       init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
+       spin_lock_irq(&pt->pt_irqfds_lock);
++#if IS_ENABLED(CONFIG_X86)
+       if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
+           !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
+               /*
+@@ -479,6 +484,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+               ret = -EINVAL;
+               goto fail;
+       }
++#endif
+       ret = 0;
+       hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
+               if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
+diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c
+index 743777b4886d3..7422a36199b40 100644
+--- a/drivers/hv/mshv_irq.c
++++ b/drivers/hv/mshv_irq.c
+@@ -119,6 +119,10 @@ void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent,
+       lirq->lapic_vector = ent->girq_irq_data & 0xFF;
+       lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF;
+       lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8;
++#if IS_ENABLED(CONFIG_X86)
+       lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1;
+       lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1;
++#elif IS_ENABLED(CONFIG_ARM64)
++      lirq->lapic_control.asserted = 1;
++#endif
+ }
+diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
+index c9c274f29c3c6..a23b5ba09fb40 100644
+--- a/drivers/hv/mshv_root_hv_call.c
++++ b/drivers/hv/mshv_root_hv_call.c
+@@ -388,7 +388,13 @@ int hv_call_assert_virtual_interrupt(u64 partition_id, u32 vector,
+       memset(input, 0, sizeof(*input));
+       input->partition_id = partition_id;
+       input->vector = vector;
++      /*
++       * NOTE: dest_addr only needs to be provided while asserting an
++       * interrupt on x86 platform
++       */
++#if IS_ENABLED(CONFIG_X86)
+       input->dest_addr = dest_addr;
++#endif
+       input->control = control;
+       status = hv_do_hypercall(HVCALL_ASSERT_VIRTUAL_INTERRUPT, input, NULL);
+       local_irq_restore(flags);
+diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
+index 1057455b84f28..cdc74199ba3fb 100644
+--- a/include/hyperv/hvhdk.h
++++ b/include/hyperv/hvhdk.h
+@@ -540,9 +540,15 @@ union hv_interrupt_control {
+       u64 as_uint64;
+       struct {
+               u32 interrupt_type; /* enum hv_interrupt_type */
++#if IS_ENABLED(CONFIG_X86)
+               u32 level_triggered : 1;
+               u32 logical_dest_mode : 1;
+               u32 rsvd : 30;
++#elif IS_ENABLED(CONFIG_ARM64)
++              u32 rsvd1 : 2;
++              u32 asserted : 1;
++              u32 rsvd2 : 29;
++#endif
+       } __packed;
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch b/queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch
new file mode 100644 (file)
index 0000000..67662cd
--- /dev/null
@@ -0,0 +1,51 @@
+From a4b2b99effa4d7a31472d88ca325d24abfd1fcd6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:49 +0000
+Subject: mshv: Fix duplicate GSI detection for GSI 0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 649dd135491945afa544351e3e6d4a727de87020 ]
+
+The duplicate routing entry check in mshv_update_routing_table() uses
+guest_irq_num != 0 to detect whether a GSI slot is already occupied.
+This fails for GSI 0 because its guest_irq_num is 0 both when the slot
+is unused (zero-initialized) and when legitimately assigned. As a
+result, duplicate entries for GSI 0 are silently accepted, with the
+second entry overwriting the first — corrupting the routing table
+without any error reported to userspace.
+
+While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing
+in practice, the check is semantically wrong — it conflates
+"uninitialized" with "GSI number 0." Use girq_entry_valid instead,
+which is explicitly set to true when an entry is populated and remains
+zero for unused slots regardless of the GSI number.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_irq.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c
+index d0fb9ef734f42..743777b4886d3 100644
+--- a/drivers/hv/mshv_irq.c
++++ b/drivers/hv/mshv_irq.c
+@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition,
+               /*
+                * Allow only one to one mapping between GSI and MSI routing.
+                */
+-              if (girq->guest_irq_num != 0) {
++              if (girq->girq_entry_valid) {
+                       r = -EINVAL;
+                       goto out;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch b/queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch
new file mode 100644 (file)
index 0000000..bd9a1de
--- /dev/null
@@ -0,0 +1,84 @@
+From f1567ca264d3562fa0b14b588223a75d4b92b823 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:43 +0000
+Subject: mshv: Fix level-triggered check on uninitialized data
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 ]
+
+In mshv_irqfd_assign(), the level-triggered validation for resample
+irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before
+mshv_irqfd_update() has populated the field. Since the irqfd struct is
+zero-allocated, level_triggered is always 0 at that point, causing the
+check to always reject resample irqfds with -EINVAL. This makes
+level-triggered interrupt resampling — used to avoid interrupt storms
+with assigned devices — completely non-functional.
+
+Move the check after the mshv_irqfd_update() call, which resolves the
+IRQ routing entry and populates irqfd_lapic_irq with the actual trigger
+mode.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c | 25 ++++++++++++++-----------
+ 1 file changed, 14 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 311aa9fecbc48..65b969b82fb97 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+       init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
+       spin_lock_irq(&pt->pt_irqfds_lock);
++      ret = 0;
++      hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
++              if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
++                      continue;
++              /* This fd is used for another irq already. */
++              ret = -EBUSY;
++              spin_unlock_irq(&pt->pt_irqfds_lock);
++              goto fail;
++      }
++
++      idx = srcu_read_lock(&pt->pt_irq_srcu);
++      mshv_irqfd_update(pt, irqfd);
++
+ #if IS_ENABLED(CONFIG_X86)
+       if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
+           !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
+@@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+                * Otherwise return with failure
+                */
+               spin_unlock_irq(&pt->pt_irqfds_lock);
++              srcu_read_unlock(&pt->pt_irq_srcu, idx);
+               ret = -EINVAL;
+               goto fail;
+       }
+ #endif
+-      ret = 0;
+-      hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
+-              if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
+-                      continue;
+-              /* This fd is used for another irq already. */
+-              ret = -EBUSY;
+-              spin_unlock_irq(&pt->pt_irqfds_lock);
+-              goto fail;
+-      }
+-      idx = srcu_read_lock(&pt->pt_irq_srcu);
+-      mshv_irqfd_update(pt, irqfd);
+       hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list);
+       spin_unlock_irq(&pt->pt_irqfds_lock);
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch b/queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch
new file mode 100644 (file)
index 0000000..42206c9
--- /dev/null
@@ -0,0 +1,68 @@
+From b166e91ba1baf83ffc256893f58015988b0e0227 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:15 +0000
+Subject: mshv: Fix race in mshv_irqfd_deassign
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 0762262ac3e70f65b3bb843fe892f8bac1562d08 ]
+
+mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list
+require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign()
+omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(),
+which does take the lock before calling mshv_irqfd_deactivate().
+
+Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons
+the node pointers rather than resetting them. Since
+mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev ==
+NULL), a poisoned node still appears active. If a concurrent path calls
+mshv_irqfd_deactivate() again on the same irqfd, the guard fails to
+prevent a double hlist_del() on poisoned pointers.
+
+Fix both issues:
+- Add the missing spin_lock_irq/spin_unlock_irq around the list
+  traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release().
+- Use hlist_del_init() instead of hlist_del() so the node is properly
+  marked as unhashed after removal, making the is_active guard reliable.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 40b7179fee805..586a9f99b3405 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -280,7 +280,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd)
+       if (!mshv_irqfd_is_active(irqfd))
+               return;
+-      hlist_del(&irqfd->irqfd_hnode);
++      hlist_del_init(&irqfd->irqfd_hnode);
+       queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown);
+ }
+@@ -535,13 +535,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt,
+       if (IS_ERR(eventfd))
+               return PTR_ERR(eventfd);
++      spin_lock_irq(&pt->pt_irqfds_lock);
+       hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list,
+                                 irqfd_hnode) {
+               if (irqfd->irqfd_eventfd_ctx == eventfd &&
+                   irqfd->irqfd_irqnum == args->gsi)
+-
+                       mshv_irqfd_deactivate(irqfd);
+       }
++      spin_unlock_irq(&pt->pt_irqfds_lock);
+       eventfd_ctx_put(eventfd);
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch b/queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch
new file mode 100644 (file)
index 0000000..3143fa7
--- /dev/null
@@ -0,0 +1,50 @@
+From 563e09c11ebb1d0e028a256f4cf5ec6752fe5e43 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:59 +0000
+Subject: mshv: Fix sleeping under spinlock in mshv_portid_alloc
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit a9708e550d11a53b22d932cdbfaa10c26346a2d0 ]
+
+idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a
+spinlock. GFP_KERNEL allows the allocator to sleep, triggering a
+sleeping-while-atomic bug.
+
+Fix by using idr_preload(GFP_KERNEL) before taking the lock to
+pre-allocate memory in a sleepable context, then idr_alloc() with
+GFP_NOWAIT inside the spinlock-protected section.
+
+Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_portid_table.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c
+index c349af1f0aaac..6f59b3e376247 100644
+--- a/drivers/hv/mshv_portid_table.c
++++ b/drivers/hv/mshv_portid_table.c
+@@ -40,12 +40,14 @@ mshv_port_table_fini(void)
+ int
+ mshv_portid_alloc(struct port_table_info *info)
+ {
+-      int ret = 0;
++      int ret;
++      idr_preload(GFP_KERNEL);
+       idr_lock(&port_table_idr);
+       ret = idr_alloc(&port_table_idr, info, PORTID_MIN,
+-                      PORTID_MAX, GFP_KERNEL);
++                      PORTID_MAX, GFP_NOWAIT);
+       idr_unlock(&port_table_idr);
++      idr_preload_end();
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch b/queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch
new file mode 100644 (file)
index 0000000..06e42a0
--- /dev/null
@@ -0,0 +1,94 @@
+From 786f40bff5e01cd7c5313ab9ce3462380c3602c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 00:28:11 +0000
+Subject: mshv: Order pt_vp_array publish against irqfd assertion path
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit b098dc869219c15dc49bf9cf63fb5fc1481d3373 ]
+
+mshv_partition_ioctl_create_vp() initialises a VP struct (allocations,
+mutex_init, init_waitqueue_head, page mappings) and then publishes the
+pointer into partition->pt_vp_array.  Several ISR paths read this array
+locklessly: the intercept ISR, the two scheduler ISRs, and
+mshv_try_assert_irq_fast() on the irqfd fast path.
+
+Of these, only mshv_try_assert_irq_fast() can structurally race the
+publish.  It runs from an eventfd waker without holding pt_mutex, and
+MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to
+refer to an existing VP at registration time.  A user can therefore
+register an irqfd targeting a yet-to-be-created VP, then trigger
+mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the
+same index.  On weakly-ordered architectures the reader can observe a
+non-NULL pointer in pt_vp_array before the initialising stores to the
+VP struct become visible, leading to use of partially-initialised
+fields (e.g. vp_register_page).
+
+The other ISR readers cannot reach this race: the hypervisor will not
+generate intercept or scheduler messages for a VP that has never been
+told to run, and the user can only call MSHV_RUN_VP on the VP fd
+returned by MSHV_CREATE_VP, which by construction is returned after
+the publish.  Leave those readers as plain loads.
+
+Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish
+the pointer, and pair it with smp_load_acquire() in
+mshv_try_assert_irq_fast().  On x86 these compile to plain accesses
+under TSO; on ARM64 they emit one-instruction acquire/release barriers,
+acceptable on this fast path.
+
+The destroy-side path (destroy_partition() clearing pt_vp_array[i] to
+NULL after kfree(vp)) has a separate ordering and lifetime concern
+that is out of scope here.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c   | 9 ++++++++-
+ drivers/hv/mshv_root_main.c | 8 +++++++-
+ 2 files changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 65b969b82fb97..161a9655aa0b1 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd)
+               return -EOPNOTSUPP;
+ #endif
+-      vp = partition->pt_vp_array[irq->lapic_apic_id];
++      /*
++       * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp().
++       * MSHV_IRQFD does not require the target lapic_apic_id to refer to an
++       * existing VP, so this read can race a concurrent VP creation; the
++       * acquire ensures that a non-NULL pointer implies the VP's
++       * initialising stores are visible.
++       */
++      vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]);
+       if (!vp->vp_register_page)
+               return -EOPNOTSUPP;
+diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
+index 4e04bef544379..3a356b34774bd 100644
+--- a/drivers/hv/mshv_root_main.c
++++ b/drivers/hv/mshv_root_main.c
+@@ -979,7 +979,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+       /* already exclusive with the partition mutex for all ioctls */
+       partition->pt_vp_count++;
+-      partition->pt_vp_array[args.vp_index] = vp;
++      /*
++       * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which
++       * can run concurrently from an irqfd waker without holding pt_mutex.
++       * The release ensures the VP's initialising stores are visible to any
++       * reader that observes a non-NULL pointer in pt_vp_array.
++       */
++      smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..392216b
--- /dev/null
@@ -0,0 +1,40 @@
+From afe1126da59a312be82e7804770a3630884f43a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index 3c36fa24bc05a..92a0debf4a4a6 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
new file mode 100644 (file)
index 0000000..4257d61
--- /dev/null
@@ -0,0 +1,78 @@
+From da68af48919f93c0c3c2d3bec22dfcacbc22ecd7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 07:29:01 +0000
+Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
+
+When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry
+under memory pressure or tree size caps, redirect handlers previously fell
+back to sending un-rate-limited ICMP/NDISC Redirect messages.
+
+In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL.
+In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into
+inet_peer_xrlim_allow(), which returned true when peer == NULL.
+
+Because ICMP/NDISC Redirects are not part of the default global rate limit
+mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates
+an un-rate-limited ICMP packet storm.
+
+Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and
+ndisc_send_redirect() when peer is NULL.
+
+Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c      | 2 --
+ net/ipv6/ip6_output.c | 2 +-
+ net/ipv6/ndisc.c      | 2 ++
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 11d990703d31a..778467bea476a 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
+       peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
+       if (!peer) {
+               rcu_read_unlock();
+-              icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
+-                        rt_nexthop(rt, ip_hdr(skb)->daddr));
+               return;
+       }
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 8f37c9cc868b0..9ef6581168f0f 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -630,7 +630,7 @@ int ip6_forward(struct sk_buff *skb)
+               /* Limit redirects both by destination (here)
+                  and by source (inside ndisc_send_redirect)
+                */
+-              if (inet_peer_xrlim_allow(peer, 1*HZ))
++              if (peer && inet_peer_xrlim_allow(peer, 1*HZ))
+                       ndisc_send_redirect(skb, target);
+               rcu_read_unlock();
+       } else {
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index 9df1fba967c6e..7262356de10d9 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1706,6 +1706,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       }
+       peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
++      if (!peer)
++              goto release;
+       ret = inet_peer_xrlim_allow(peer, 1*HZ);
+       if (!ret)
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
new file mode 100644 (file)
index 0000000..794c4d3
--- /dev/null
@@ -0,0 +1,53 @@
+From e72cae329e8d92865c4428c997e168428cd2e473 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:14 +0100
+Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ]
+
+bus->read() returns a negative errno on failure, but
+mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT
+into 0xff92, and returns success. The garbage word is then consumed as
+register data, and read-modify-write cycles write it back to the
+switch. Check both reads and propagate their errors.
+
+The same defect existed in mt7530_mii_read() since the driver was
+introduced and moved into the regmap backend unchanged.
+
+Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530-mdio.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c
+index 0286a6cecb6f5..6cc2c8975af66 100644
+--- a/drivers/net/dsa/mt7530-mdio.c
++++ b/drivers/net/dsa/mt7530-mdio.c
+@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
+       if (ret < 0)
+               return ret;
+-      lo = bus->read(bus, priv->mdiodev->addr, r);
+-      hi = bus->read(bus, priv->mdiodev->addr, 0x10);
++      ret = bus->read(bus, priv->mdiodev->addr, r);
++      if (ret < 0)
++              return ret;
++      lo = ret;
++
++      ret = bus->read(bus, priv->mdiodev->addr, 0x10);
++      if (ret < 0)
++              return ret;
++      hi = ret;
+       *val = (hi << 16) | (lo & 0xffff);
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
new file mode 100644 (file)
index 0000000..a4b2d76
--- /dev/null
@@ -0,0 +1,191 @@
+From 6bd91df9e53d5482d28a24bd7e2615abf7225073 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:29 +0100
+Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ]
+
+The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through
+a helper which returns 0 when the underlying read fails, so a failed
+bus transaction clears MT7531_PHY_ACS_ST and the access carries on,
+returning garbage PHY register data to phylib.
+
+Poll using regmap_read_poll_timeout(), which stops on read errors and
+propagates them. These functions hold the MDIO bus lock across the
+whole sequence, so the unlocked regmap accesses remain correct. Remove
+the now-unused _mt7530_unlocked_read().
+
+Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 58 ++++++++++++++--------------------------
+ 1 file changed, 20 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index 4571da0d7a8f6..2a70fa4b5fa3e 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -188,12 +188,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+       mt7530_mutex_unlock(priv);
+ }
+-static u32
+-_mt7530_unlocked_read(struct mt7530_dummy_poll *p)
+-{
+-      return mt7530_mii_read(p->priv, p->reg);
+-}
+-
+ static u32
+ _mt7530_read(struct mt7530_dummy_poll *p)
+ {
+@@ -546,16 +540,13 @@ static int
+ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+                       int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 reg, val;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -565,8 +556,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -576,8 +567,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad);
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -594,16 +585,13 @@ static int
+ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+                        int regnum, u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 val, reg;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -613,8 +601,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -624,8 +612,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | data;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -640,16 +628,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+ static int
+ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 val;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -660,8 +645,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+       mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -678,16 +663,13 @@ static int
+ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+                        u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 reg;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -698,8 +680,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
new file mode 100644 (file)
index 0000000..233e671
--- /dev/null
@@ -0,0 +1,40 @@
+From 3a8c733299c67ed5d305a65c72450b345cdf9283 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 13:57:35 +0800
+Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in
+ poll_controller
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ]
+
+mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq
+cookie), but mtk_poll_controller incorrectly passed the net_device *.
+Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled
+would then crash.
+
+Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 065f969ee44ef..98cf11a706e14 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3446,7 +3446,7 @@ static void mtk_poll_controller(struct net_device *dev)
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+-      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev);
++      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth);
+       mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
new file mode 100644 (file)
index 0000000..9670893
--- /dev/null
@@ -0,0 +1,48 @@
+From 7c5591882aeafab6b5e8612fcb70df57fb1aca09 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:46:57 +0800
+Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets
+
+From: Jiawen Wu <jiawenwu@trustnetic.com>
+
+[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ]
+
+When TX VLAN hardware offload is disabled, VLAN tags are embedded in
+the packet payload (software VLAN). Previously, the driver failed to
+set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission.
+
+This missing flag caused the txgbe FDIR ATR logic to fall through to the
+default hash calculation path. This resulted in asymmetric hash values
+for Tx and Rx flows, preventing return packets from being steered to the
+same queue as the transmit packets.
+
+Fix this by detecting software VLANs via eth_type_vlan(skb->protocol)
+and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects
+the correct hashing algorithm to maintain Tx/Rx queue symmetry.
+
+Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support")
+Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+index 678dce480f156..380edc64e45e4 100644
+--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+@@ -1549,6 +1549,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
+       if (skb_vlan_tag_present(skb)) {
+               tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT;
+               tx_flags |= WX_TX_FLAGS_HW_VLAN;
++      } else if (eth_type_vlan(skb->protocol)) {
++              tx_flags |= WX_TX_FLAGS_SW_VLAN;
+       }
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..eb1e088
--- /dev/null
@@ -0,0 +1,92 @@
+From a9b24e2fb20c4e59c61267471f7f1bcbd75b3209 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 9182443082158..1b7fc17bf3919 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1861,8 +1861,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops);
+@@ -1895,28 +1895,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->req_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..77d98ef
--- /dev/null
@@ -0,0 +1,48 @@
+From 8d473c088527dda530755b0249415c293e09a77d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 5eb8e7c232f8b..848a0578cea1f 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..3f76629
--- /dev/null
@@ -0,0 +1,50 @@
+From 59ab621edb1cef2e59580d2e0b5686232d736581 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 849c5a6c2af1e..5eb8e7c232f8b 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
new file mode 100644 (file)
index 0000000..780c667
--- /dev/null
@@ -0,0 +1,150 @@
+From 847afdba1315ad16fbd4747aed21f9a661d1441a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 09:11:37 +0000
+Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ]
+
+syzbot reported a memory leak [1] in the UDP tunnel NIC offload code.
+
+When device registration fails (e.g. in register_netdevice()), netdev core
+unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued
+during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister()
+returns early:
+
+       if (utn->work_pending)
+               return;
+
+Because failed registrations do not enter netdev_wait_allrefs_any(), no
+subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the
+struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked
+permanently.
+
+Fix this by removing the early return. Instead, synchronously cancel any
+pending work with cancel_delayed_work_sync() before freeing @utn.
+
+To be able to call cancel_delayed_work_sync() while holding RTNL (the work also
+needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL
+is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work())
+to prevent high CPU contention while waiting for RTNL lock.
+
+The utn->work_pending bookkeeping is no longer needed and is removed, as
+the workqueue core already tracks the pending/running state of the work.
+
+[1]
+BUG: memory leak
+unreferenced object 0xffff888127d5f840 (size 96):
+  comm "syz-executor", pid 5806, jiffies 4294942188
+  backtrace (crc 99fdb6c8):
+    __kmalloc_noprof+0x3bf/0x550
+    udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline]
+    udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline]
+    udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931
+    notifier_call_chain+0x59/0x160 kernel/notifier.c:85
+    call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250
+    register_netdevice+0xc10/0xeb0 net/core/dev.c:11478
+
+Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure")
+Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++---------------
+ 1 file changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
+index b13e8f7092f46..4360d159ee8a7 100644
+--- a/net/ipv4/udp_tunnel_nic.c
++++ b/net/ipv4/udp_tunnel_nic.c
+@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry {
+  * @lock:     protects all fields
+  * @need_sync:        at least one port start changed
+  * @need_replay: space was freed, we need a replay of all ports
+- * @work_pending: @work is currently scheduled
+  * @n_tables: number of tables under @entries
+  * @missed:   bitmap of tables which overflown
+  * @entries:  table of tables of ports currently offloaded
+  */
+ struct udp_tunnel_nic {
+-      struct work_struct work;
++      struct delayed_work work;
+       struct net_device *dev;
+@@ -46,7 +45,6 @@ struct udp_tunnel_nic {
+       u8 need_sync:1;
+       u8 need_replay:1;
+-      u8 work_pending:1;
+       unsigned int n_tables;
+       unsigned long missed;
+@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void
+ udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ {
+-      if (!utn->need_sync || utn->work_pending)
++      if (!utn->need_sync)
+               return;
+-      queue_work(udp_tunnel_nic_workqueue, &utn->work);
+-      utn->work_pending = 1;
++      queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0);
+ }
+ static bool
+@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void udp_tunnel_nic_device_sync_work(struct work_struct *work)
+ {
+       struct udp_tunnel_nic *utn =
+-              container_of(work, struct udp_tunnel_nic, work);
++              container_of(work, struct udp_tunnel_nic, work.work);
+-      rtnl_lock();
++      /* We cannot block on RTNL here, otherwise we would deadlock with
++       * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync()
++       * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended.
++       */
++      if (!rtnl_trylock()) {
++              queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1);
++              return;
++      }
+       mutex_lock(&utn->lock);
+-
+-      utn->work_pending = 0;
+       __udp_tunnel_nic_device_sync(utn->dev, utn);
+       if (utn->need_replay)
+@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info,
+       if (!utn)
+               return NULL;
+       utn->n_tables = n_tables;
+-      INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
++      INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
+       mutex_init(&utn->lock);
+       for (i = 0; i < n_tables; i++) {
+@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
+       udp_tunnel_nic_flush(dev, utn);
+       udp_tunnel_nic_unlock(dev);
+-      /* Wait for the work to be done using the state, netdev core will
+-       * retry unregister until we give up our reference on this device.
++      /* Make sure no work is running or queued before freeing @utn.
++       * The work handler uses rtnl_trylock(), so it will not deadlock
++       * against the RTNL we are holding here.
+        */
+-      if (utn->work_pending)
+-              return;
++      cancel_delayed_work_sync(&utn->work);
+       udp_tunnel_nic_free(utn);
+ release_dev:
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..113df09
--- /dev/null
@@ -0,0 +1,96 @@
+From fb13068c94e776a46cbfbc6b248b97cc4fa39d4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index dbc614dfe0d56..aafa0c04f917e 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 4bebf66410ea3..f5f9bd47d2889 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index f7be30c69b5c8..a1c41defaf22d 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
new file mode 100644 (file)
index 0000000..1266d2b
--- /dev/null
@@ -0,0 +1,205 @@
+From 0ae320e7ef723790d764d23f55f17809406889de Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 10:13:37 +0200
+Subject: netfilter: nf_tables: make nft_object rhltable per table
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ]
+
+The nft_object rhltable is global, this allows for accessing objects
+that are being dismangled from lookup path by other existing netns.
+Given the nft_obj_destroy() releases the object inmediately, this might
+lead to use-after-free of these objects that are being released.
+Make the existing rhltable per table to address this issue to deal with
+with the nft_rcv_nl_event() path too.
+
+Update nft_obj_lookup() to take the table as non-const, otherwise,
+compiler complains when passing the objname_ht to rhltable_lookup().
+
+Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable")
+Suggested-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h |  4 +++-
+ net/netfilter/nf_tables_api.c     | 34 +++++++++++++++----------------
+ 2 files changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index fda5bc82dd504..e6b31aa9d6b7d 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1288,6 +1288,7 @@ static inline void nft_use_inc_restore(u32 *use)
+  *    @sets: sets in the table
+  *    @objects: stateful objects in the table
+  *    @flowtables: flow tables in the table
++ *    @objname_ht: hashtable for objects lookup by name
+  *    @hgenerator: handle generator state
+  *    @handle: table handle
+  *    @use: number of chain references to this table
+@@ -1307,6 +1308,7 @@ struct nft_table {
+       struct list_head                sets;
+       struct list_head                objects;
+       struct list_head                flowtables;
++      struct rhltable                 objname_ht;
+       u64                             hgenerator;
+       u64                             handle;
+       u32                             use;
+@@ -1394,7 +1396,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)
+ #define nft_expr_obj(expr)    *((struct nft_object **)nft_expr_priv(expr))
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask);
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 51d3d4d44918c..ca6d2041eee66 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -44,8 +44,6 @@ enum {
+       NFT_VALIDATE_DO,
+ };
+-static struct rhltable nft_objname_ht;
+-
+ static u32 nft_chain_hash(const void *data, u32 len, u32 seed);
+ static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed);
+ static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *);
+@@ -1600,6 +1598,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err)
+               goto err_chain_ht;
++      err = rhltable_init(&table->objname_ht, &nft_objname_ht_params);
++      if (err < 0)
++              goto err_obj_ht;
++
+       INIT_LIST_HEAD(&table->chains);
+       INIT_LIST_HEAD(&table->sets);
+       INIT_LIST_HEAD(&table->objects);
+@@ -1618,6 +1620,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       list_add_tail_rcu(&table->list, &nft_net->tables);
+       return 0;
+ err_trans:
++      rhltable_destroy(&table->objname_ht);
++err_obj_ht:
+       rhltable_destroy(&table->chains_ht);
+ err_chain_ht:
+       kfree(table->udata);
+@@ -1784,6 +1788,7 @@ static void nf_tables_table_destroy(struct nft_table *table)
+               return;
+       rhltable_destroy(&table->chains_ht);
++      rhltable_destroy(&table->objname_ht);
+       kfree(table->name);
+       kfree(table->udata);
+       kfree(table);
+@@ -7957,7 +7962,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type)
+ EXPORT_SYMBOL_GPL(nft_unregister_obj);
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask)
+ {
+@@ -7973,7 +7978,7 @@ struct nft_object *nft_obj_lookup(const struct net *net,
+                    !lockdep_commit_lock_is_held(net));
+       rcu_read_lock();
+-      list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params);
++      list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params);
+       if (!list)
+               goto out;
+@@ -8252,7 +8257,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err < 0)
+               goto err_trans;
+-      err = rhltable_insert(&nft_objname_ht, &obj->rhlhead,
++      err = rhltable_insert(&table->objname_ht, &obj->rhlhead,
+                             nft_objname_ht_params);
+       if (err < 0)
+               goto err_obj_ht;
+@@ -8437,8 +8442,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       u8 family = info->nfmsg->nfgen_family;
+-      const struct nft_table *table;
+       struct net *net = info->net;
++      struct nft_table *table;
+       struct nft_object *obj;
+       struct sk_buff *skb2;
+       u32 objtype;
+@@ -10270,9 +10275,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
+               nf_tables_commit_chain_free_rules_old(g0);
+ }
+-static void nft_obj_del(struct nft_object *obj)
++static void nft_obj_del(struct nft_table *table, struct nft_object *obj)
+ {
+-      rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params);
++      rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);
+       list_del_rcu(&obj->list);
+ }
+@@ -10960,7 +10965,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
+                       break;
+               case NFT_MSG_DELOBJ:
+               case NFT_MSG_DESTROYOBJ:
+-                      nft_obj_del(nft_trans_obj(trans));
++                      nft_obj_del(table, nft_trans_obj(trans));
+                       nf_tables_obj_notify(&ctx, nft_trans_obj(trans),
+                                            trans->msg_type);
+                       break;
+@@ -11251,7 +11256,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
+                               nft_trans_destroy(trans);
+                       } else {
+                               nft_use_dec_restore(&table->use);
+-                              nft_obj_del(nft_trans_obj(trans));
++                              nft_obj_del(table, nft_trans_obj(trans));
+                       }
+                       break;
+               case NFT_MSG_DELOBJ:
+@@ -11877,7 +11882,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
+               nft_set_destroy(&ctx, set);
+       }
+       list_for_each_entry_safe(obj, ne, &table->objects, list) {
+-              nft_obj_del(obj);
++              nft_obj_del(table, obj);
+               nft_use_dec(&table->use);
+               nft_obj_destroy(&ctx, obj);
+       }
+@@ -12059,10 +12064,6 @@ static int __init nf_tables_module_init(void)
+       if (err < 0)
+               goto err_netdev_notifier;
+-      err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params);
+-      if (err < 0)
+-              goto err_rht_objname;
+-
+       err = nft_offload_init();
+       if (err < 0)
+               goto err_offload;
+@@ -12085,8 +12086,6 @@ static int __init nf_tables_module_init(void)
+ err_netlink_notifier:
+       nft_offload_exit();
+ err_offload:
+-      rhltable_destroy(&nft_objname_ht);
+-err_rht_objname:
+       unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
+ err_netdev_notifier:
+       nf_tables_core_module_exit();
+@@ -12108,7 +12107,6 @@ static void __exit nf_tables_module_exit(void)
+       unregister_pernet_subsys(&nf_tables_net_ops);
+       cancel_work_sync(&trans_gc_work);
+       rcu_barrier();
+-      rhltable_destroy(&nft_objname_ht);
+       nf_tables_core_module_exit();
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..d7b3aab
--- /dev/null
@@ -0,0 +1,69 @@
+From 71464e314457c57d016aa259a4045340b8d5b58d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index 86a21c2b954f8..e07888aaf1475 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb,
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..b79ff0e
--- /dev/null
@@ -0,0 +1,83 @@
+From 09cb2046366a3e1eea86254be066e7a0a59a9ac6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 3b507694e81e5..b3f15bbf259e2 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -117,6 +117,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               kvfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -870,7 +872,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -903,6 +908,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch b/queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch
new file mode 100644 (file)
index 0000000..ab64831
--- /dev/null
@@ -0,0 +1,44 @@
+From 078a2717a0c231166e88977aafffefbae3d8ec86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:12 +0100
+Subject: netfs: clear PG_private_2 on copy-to-cache append failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 ]
+
+netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before
+netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling
+buffer.
+
+If the append fails, the folio is not queued for cache writeback, so
+the PG_private_2 state and its reference must be released immediately.
+
+Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/read_pgpriv2.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c
+index a1489aa29f782..7eacc58abadb7 100644
+--- a/fs/netfs/read_pgpriv2.c
++++ b/fs/netfs/read_pgpriv2.c
+@@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
+       /* Attach the folio to the rolling buffer. */
+       if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) {
++              folio_end_private_2(folio);
+               clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags);
+               return;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch b/queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch
new file mode 100644 (file)
index 0000000..28cb9ad
--- /dev/null
@@ -0,0 +1,57 @@
+From 3f432e9f8e6a2aca4e81eaad02b29c402e7b64e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:13 +0100
+Subject: netfs: handle single writeback rolling buffer allocation failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit 37a1c535c80c67d98668d190c7432f9ebda43310 ]
+
+netfs_write_folio_single() takes an extra folio reference before
+appending the folio to the rolling buffer.
+
+rolling_buffer_append() can fail if it cannot allocate another
+folio_queue. Check the return value and drop the extra folio reference
+before returning the error.
+
+Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/write_issue.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
+index 76614339554ea..5f216907b1e50 100644
+--- a/fs/netfs/write_issue.c
++++ b/fs/netfs/write_issue.c
+@@ -730,6 +730,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
+       size_t iter_off = 0;
+       size_t fsize = folio_size(folio), flen;
+       loff_t fpos = folio_pos(folio);
++      ssize_t ret;
+       bool to_eof = false;
+       bool no_debug = false;
+@@ -758,7 +759,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
+       /* Attach the folio to the rolling buffer. */
+       folio_get(folio);
+-      rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
++      ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
++      if (ret < 0) {
++              folio_put(folio);
++              return ret;
++      }
+       /* Move the submission point forward to allow for write-streaming data
+        * not starting at the front of the page.  We don't do write-streaming
+-- 
+2.53.0
+
diff --git a/queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch b/queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch
new file mode 100644 (file)
index 0000000..436c563
--- /dev/null
@@ -0,0 +1,49 @@
+From 890f89db290ed7245e3a9cb54b4b29b6c587073c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:14 +0100
+Subject: netfs: release readahead folios on iterator preparation failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 ]
+
+netfs_prepare_read_iterator() batches readahead folios in put_batch so that
+the folio references can be dropped after the I/O iterator has been
+prepared.
+
+If rolling_buffer_load_from_ra() fails after earlier folios have been
+batched, the function returns immediately and leaves those references held.
+Release the batch before returning the error.
+
+Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/buffered_read.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
+index fab3181c7f869..221287f8925a9 100644
+--- a/fs/netfs/buffered_read.c
++++ b/fs/netfs/buffered_read.c
+@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq,
+                       added = rolling_buffer_load_from_ra(&rreq->buffer, ractl,
+                                                           &put_batch);
+-                      if (added < 0)
++                      if (added < 0) {
++                              folio_batch_release(&put_batch);
+                               return added;
++                      }
+                       rreq->submitted += added;
+               }
+               folio_batch_release(&put_batch);
+-- 
+2.53.0
+
diff --git a/queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..d31d704
--- /dev/null
@@ -0,0 +1,42 @@
+From 691cda9c02a80909a051d37dc1bc7579c9d90169 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index ae7bded777688..7e42d3291a9fd 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
new file mode 100644 (file)
index 0000000..7d59188
--- /dev/null
@@ -0,0 +1,66 @@
+From 8c30ff3fa549b112dbc795fd1cf5cad1db888ca5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 14 Jun 2026 22:38:06 +0900
+Subject: of: reserved_mem: prevent OOB when too many dynamic regions are
+ defined
+
+From: Sang-Heon Jeon <ekffu200098@gmail.com>
+
+[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ]
+
+On boot, fdt_scan_reserved_mem() saves each dynamically-placed
+/reserved-memory subnode into a local array of size
+MAX_RESERVED_REGIONS.
+
+If the device tree defines more than MAX_RESERVED_REGIONS
+dynamically-placed regions, fdt_scan_reserved_mem() writes past the
+end of the local array.
+
+Add a bounds check that logs an error and skips the excess regions,
+restoring the original behavior.
+
+Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed")
+Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
+Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com
+Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/of/of_reserved_mem.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+index 39d8305a16800..56c2a1e4e86c1 100644
+--- a/drivers/of/of_reserved_mem.c
++++ b/drivers/of/of_reserved_mem.c
+@@ -326,6 +326,7 @@ int __init fdt_scan_reserved_mem(void)
+               err = __reserved_mem_reserve_reg(child, uname);
+               if (!err)
+                       count++;
++
+               /*
+                * Save the nodes for the dynamically-placed regions
+                * into an array which will be used for allocation right
+@@ -333,10 +334,17 @@ int __init fdt_scan_reserved_mem(void)
+                * or marked as no-map. This is done to avoid dynamically
+                * allocating from one of the statically-placed regions.
+                */
+-              if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
+-                      dynamic_nodes[dynamic_nodes_cnt] = child;
+-                      dynamic_nodes_cnt++;
++              if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL))
++                      continue;
++
++              if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) {
++                      pr_err("too many defined dynamic regions, skip '%s'\n",
++                             uname);
++                      continue;
+               }
++
++              dynamic_nodes[dynamic_nodes_cnt] = child;
++              dynamic_nodes_cnt++;
+       }
+       for (int i = 0; i < dynamic_nodes_cnt; i++) {
+               const char *uname;
+-- 
+2.53.0
+
diff --git a/queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch b/queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch
new file mode 100644 (file)
index 0000000..01c6666
--- /dev/null
@@ -0,0 +1,38 @@
+From 143fbde27d9979f9eba76c7b7b4dae3137431886 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 15:01:31 +0530
+Subject: phy: qcom: m31-eusb2: Fix return value of init call
+
+From: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
+
+[ Upstream commit 361f533a2dce2c2841fe4dc0c9d85a67117edf95 ]
+
+The init call currently returns success irrespective of any failures
+during repeater init or clock enablement. Return appropriate error value
+in the init call failure path.
+
+Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver")
+Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+index 68f1ba8fec4ad..9434bd22ef32d 100644
+--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
++++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+@@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy)
+ disable_vreg:
+       regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs);
+-      return 0;
++      return ret;
+ }
+ static int m31eusb2_phy_exit(struct phy *uphy)
+-- 
+2.53.0
+
diff --git a/queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..0376166
--- /dev/null
@@ -0,0 +1,76 @@
+From 242e8f806b680aaea46cceb10a41de2eb3a47329 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index fe6b4925d1662..c8230f2bda629 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..4adfa60
--- /dev/null
@@ -0,0 +1,56 @@
+From a2241cf6da5da1651013c97323ddf20fc0f428e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index c8230f2bda629..2138f5399821a 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
new file mode 100644 (file)
index 0000000..35ea7b6
--- /dev/null
@@ -0,0 +1,58 @@
+From a5ada19dc297b2ccd33f967454364e0b60b73ff1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:28:44 -0500
+Subject: pinctrl-amd: Don't clear S4 wake bits at probe
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
+
+commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+introduced a regression where Wake-on-LAN no longer works after suspend
+or shutdown on some AMD platforms.
+
+Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI
+PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake
+sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME
+does not use GPIO IRQ infrastructure and relies on firmware configuration.
+
+The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake
+bits on probe again") was to clear spurious wake bits left by firmware
+to prevent unwanted wakeups. However, S4 wake bits are used for
+hardware-level wake sources like WoL that bypass the kernel's IRQ wake
+API.
+
+Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
+- Firmware-configured S4 wake sources (WoL) continue working
+- Kernel maintains control of S3/S0i3 wake policy via set_wake()
+- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
+  amd: Take suspend type into consideration which pins are non-wake")
+
+The trade-off is that firmware-programmed spurious S4 wake bits remain
+set, but this is less problematic than breaking WoL.
+
+Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/pinctrl-amd.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index 2dac5c71eb008..d962f2e1d2acd 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -886,8 +886,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
+       u32 pin_reg, mask;
+       int i;
+-      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
+-              BIT(WAKE_CNTRL_OFF_S4);
++      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
+       for (i = 0; i < desc->npins; i++) {
+               int pin = desc->pins[i].number;
+-- 
+2.53.0
+
diff --git a/queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
new file mode 100644 (file)
index 0000000..a2ba3ad
--- /dev/null
@@ -0,0 +1,58 @@
+From 694588d90c9094e7fdd8e9733e8e5d86bda0d1b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 15:08:05 +0200
+Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
+
+From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+
+[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
+
+Pins 143 and 151 were not included in the PDC wakeup map. They are
+normally used for PCIe2A and PCIe3a PERST# respectively, so they're
+unlikely to be excercised in practice, but still add them for the sake
+of completeness.
+
+Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver")
+Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+index cf8297e8b8f8c..d6e31b00e1c5e 100644
+--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = {
+       { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 },
+       { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 },
+       { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 },
+-      { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 },
+-      { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 },
+-      { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 },
+-      { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 },
+-      { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 },
+-      { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 },
+-      { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 },
+-      { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 },
+-      { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 },
+-      { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 },
++      { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 },
++      { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 },
++      { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 },
++      { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 },
++      { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 },
++      { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 },
++      { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 },
++      { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 },
++      { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 },
++      { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 },
++      { 225, 196 }, { 227, 197 },
+ };
+ static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
+-- 
+2.53.0
+
diff --git a/queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
new file mode 100644 (file)
index 0000000..8891655
--- /dev/null
@@ -0,0 +1,70 @@
+From 2481796581b3b549aafaa53065afc55653ea1dd4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 17:24:53 +0530
+Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable
+
+From: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+
+[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ]
+
+GPIO interrupts that are wakeup capable need to be forwarded to wakeup
+capable parent irqchip. This is done via writing to it's wakeup_enable bit.
+
+Currently the bit is set only for PDC irqchip by checking skip_wake_irqs.
+skip_wake_irqs is set to differentiate between parent irqchips MPM and
+PDC. It is set when the parent irqchip is PDC to inform pinctrl about
+skipping the IRQ setting up at TLMM.
+
+However, the functionality to forward GPIO interrupts during SoC low
+power mode is needed regardless of which parent irqchip it is.
+Without the functionality it is impossible for MPM irqchip to detect the
+GPIO interrupt during SoC low power mode since for MPM irqchip the
+skip_wake_irqs is always false.
+
+Remove skip_wake_irqs condition when setting wakeup enable bit to allow
+forwarding GPIO interrupts for SoCs using MPM irqchip too.
+
+Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits")
+Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+Reviewed-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
+index e99871b90ab9c..424b14bc29e96 100644
+--- a/drivers/pinctrl/qcom/pinctrl-msm.c
++++ b/drivers/pinctrl/qcom/pinctrl-msm.c
+@@ -1226,12 +1226,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d)
+       /*
+        * If the wakeup_enable bit is present and marked as available for the
+        * requested GPIO, it should be enabled when the GPIO is marked as
+-       * wake irq in order to allow the interrupt event to be transfered to
+-       * the PDC HW.
++       * wake irq in order to allow the interrupt event to be transferred to
++       * the PDC/MPM HW.
+        * While the name implies only the wakeup event, it's also required for
+        * the interrupt event.
+        */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+@@ -1259,7 +1259,7 @@ static void msm_gpio_irq_relres(struct irq_data *d)
+       unsigned long flags;
+       /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+-- 
+2.53.0
+
diff --git a/queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..c709883
--- /dev/null
@@ -0,0 +1,38 @@
+From 96245c6a61e7b219c9389df07b3d4abccac6a66b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..5d380f8
--- /dev/null
@@ -0,0 +1,38 @@
+From 95ebe6555c4b94cffb4394710dc6414e7b20952f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..085376f
--- /dev/null
@@ -0,0 +1,38 @@
+From 49cece124bd19e2b42bb8221dd506db7dfaebc33 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch b/queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch
new file mode 100644 (file)
index 0000000..aea5e5e
--- /dev/null
@@ -0,0 +1,121 @@
+From 585cae9201bc3cd2d48e3da5d3c30ff0009e9823 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:03:48 +0800
+Subject: ptp: netc: fix potential interrupt storm caused by incorrect unbind
+ order
+
+From: Wei Fang <wei.fang@nxp.com>
+
+[ Upstream commit 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea ]
+
+In netc_timer_remove(), hardware interrupts are disabled by clearing
+TMR_TEMASK before ptp_clock_unregister() is called. This may cause a
+race condition during driver unbind that could leave hardware interrupts
+active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable
+TMR_TEMASK after it has been cleared, leaving a pending hardware
+interrupt when the driver unbinds.
+
+Since the NETC Timer does not support PCIe FLR, hardware state is not
+reset during probe. When the driver is rebound and the IRQ is registered,
+the pending interrupt fires immediately. At that point priv->tmr_emask
+is still zero, so netc_timer_isr() does not clear the interrupt status
+and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible
+infinite interrupt storm.
+
+Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so
+it is not enabled when request_irq() runs, and clear TMR_TEMASK in
+netc_timer_init() before enabling it. The IRQ is only enabled at the end
+of probe once the timer has been reprogrammed and the PTP clock has been
+registered. This ensures a stale pending interrupt from a previous unbind
+or an unclean shutdown cannot be delivered before the driver is fully
+initialized.
+
+Second, in netc_timer_remove() call disable_irq() before
+ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after
+it. disable_irq() masks the line and waits for any in-flight
+netc_timer_isr() to finish, so no ISR can dereference priv->clock after
+ptp_clock_unregister() has freed it. Unregistering the PTP clock before
+clearing the mask also guarantees that no in-flight or concurrent ioctl
+can re-enable hardware interrupts.
+
+Finally, return IRQ_NONE from netc_timer_isr() when the masked event
+status is zero, so the kernel's spurious interrupt detection can disable
+a stuck line instead of looping forever.
+
+Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com
+Signed-off-by: Wei Fang <wei.fang@nxp.com>
+Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ptp/ptp_netc.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
+index 5e381c354d746..1c20d7efab929 100644
+--- a/drivers/ptp/ptp_netc.c
++++ b/drivers/ptp/ptp_netc.c
+@@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv)
+                  TMR_CTRL_TE | TMR_CTRL_FS;
+       netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+       netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
++      netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
+       /* Disable FIPER by default */
+       fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+@@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
+       /* Clear interrupts status */
+       netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
++      if (!tmr_event) {
++              spin_unlock(&priv->lock);
++              return IRQ_NONE;
++      }
++
+       if (tmr_event & TMR_TEVENT_ALMEN(0))
+               netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+@@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv)
+       }
+       priv->irq = pci_irq_vector(pdev, 0);
+-      err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv);
++      err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN,
++                        priv->irq_name, priv);
+       if (err) {
+               dev_err(&pdev->dev, "request_irq() failed\n");
+               pci_free_irq_vectors(pdev);
+@@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv)
+ {
+       struct pci_dev *pdev = priv->pdev;
+-      disable_irq(priv->irq);
+       free_irq(priv->irq, priv);
+       pci_free_irq_vectors(pdev);
+ }
+@@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev,
+               goto free_msix_irq;
+       }
++      enable_irq(priv->irq);
++
+       return 0;
+ free_msix_irq:
+@@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev)
+ {
+       struct netc_timer *priv = pci_get_drvdata(pdev);
++      disable_irq(priv->irq);
++      ptp_clock_unregister(priv->clock);
+       netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
+       netc_timer_wr(priv, NETC_TMR_CTRL, 0);
+-      ptp_clock_unregister(priv->clock);
+       netc_timer_free_msix_irq(priv);
+       netc_timer_pci_remove(pdev);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..a7b8953
--- /dev/null
@@ -0,0 +1,169 @@
+From dd31f5d9a636880f7194230895c8f1a09fa4f58f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index b5d744d2586f7..59a80f7193723 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..929c1a7
--- /dev/null
@@ -0,0 +1,118 @@
+From 87ad45ccf6060d3c5c38dfa9459fe75c3f9bccd0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ce5be43c5fbac..1061bcf7d1315 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index 5289afbb61aa7..e50e01abb0799 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 06a2d8d48bbac..87e6ab0e93b71 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -342,9 +342,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..5e34294
--- /dev/null
@@ -0,0 +1,85 @@
+From 42edbed84b0e91de66949cbf61e0c7063e4c4aa1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 87e6ab0e93b71..1980a197034ba 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -331,23 +331,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch b/queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch
new file mode 100644 (file)
index 0000000..d1ee924
--- /dev/null
@@ -0,0 +1,80 @@
+From cb8b9accfa9d14551459440c7a46d1f2bccc03f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Jun 2026 11:24:43 -0500
+Subject: riscv: drop __init from vec_check_unaligned_access_speed_all_cpus
+
+From: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
+
+[ Upstream commit f51fed61eea0daba2f95f1a6074085e4cd513c7b ]
+
+This function runs within a kthread and need not necessarily finish
+before system finishes boot and free_initmem() unmaps the .init.text
+section. This function makes calls to SBI for probing unaligned access
+speed, and if this is slow for some reason (say some debug prints were
+added to SBI), the kthread can still be running at this point and result
+in an instruction page fault when trying to fetch from the freed region.
+
+[   25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8
+[   25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000
+[   25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3
+[   25.661244] Oops [#1]
+[   25.662997] Modules linked in:
+[   25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY
+[   25.674669] Hardware name: Tenstorrent Blackhole (DT)
+[   25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.683458]  ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20
+[   25.693874]  gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000
+[   25.699401]  t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30
+[   25.704912]  s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002
+[   25.710407]  a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000
+[   25.715917]  a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30
+[   25.721419]  s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0
+[   25.726937]  s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000
+[   25.732450]  s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000
+[   25.737944]  s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402
+[   25.743481]  t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000
+[   25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c
+[   25.755060] [<ffffffff80a04ef8>] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.760964] [<ffffffff80047a10>] kthread+0xd8/0xfc
+[   25.764660] [<ffffffff80010c48>] ret_from_fork_kernel+0x18/0x1c4
+[   25.769220] [<ffffffff80895fe6>] ret_from_fork_kernel_asm+0x16/0x18
+[   25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc
+
+Drop __init from its signature so that this doesn't happen.
+
+Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions")
+Signed-off-by: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
+Assisted-by: Claude:claude-opus-4-6
+Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/kernel/unaligned_access_speed.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c
+index 70b5e69276209..78fc74a416787 100644
+--- a/arch/riscv/kernel/unaligned_access_speed.c
++++ b/arch/riscv/kernel/unaligned_access_speed.c
+@@ -376,7 +376,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus
+ }
+ /* Measure unaligned access speed on all CPUs present at boot in parallel. */
+-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+ {
+       schedule_on_each_cpu(check_vector_unaligned_access);
+       riscv_hwprobe_complete_async_probe();
+@@ -384,7 +384,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway
+       return 0;
+ }
+ #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */
+-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+ {
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
new file mode 100644 (file)
index 0000000..179f91d
--- /dev/null
@@ -0,0 +1,63 @@
+From c07e09a36240b899ff9fe9954cb1c6fbc243ee7e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 03:21:32 +0200
+Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove
+
+From: Karl Mehltretter <kmehltretter@gmail.com>
+
+[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ]
+
+remove_pud_mapping() and remove_p4d_mapping() obtain a child table base
+with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for
+addr.
+
+RISC-V folds page-table levels at runtime. When a level is folded, its
+offset helper returns the parent entry itself, but the index can still be
+nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39
+folds both P4D and PUD, so memory hot-remove can descend into unrelated
+memory and pass an invalid page to __free_pages(). This can trigger:
+
+  kernel BUG at include/linux/mm.h:1810!
+  VM_BUG_ON_PAGE(page_ref_count(page) == 0)
+  arch_remove_memory+0x1e/0x5c
+  try_remove_memory+0x15e/0x200
+  remove_memory+0x24/0x3c
+
+Only add the index when the corresponding page-table level is enabled,
+matching p4d_offset() and pud_offset().
+
+Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
+Assisted-by: Claude:claude-fable-5
+Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
+Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/mm/init.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index ee40ca01ac663..557bf160b503a 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -1738,7 +1738,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = pud_addr_end(addr, end);
+-              pudp = pud_base + pud_index(addr);
++              pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base;
+               pud = pudp_get(pudp);
+               if (!pud_present(pud))
+                       continue;
+@@ -1769,7 +1769,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = p4d_addr_end(addr, end);
+-              p4dp = p4d_base + p4d_index(addr);
++              p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base;
+               p4d = p4dp_get(p4dp);
+               if (!p4d_present(p4d))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
new file mode 100644 (file)
index 0000000..7671075
--- /dev/null
@@ -0,0 +1,47 @@
+From d7d06feda0304a17cd659c4eba74c10c004ed8e1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 10:38:36 +0800
+Subject: rtase: fix double free of multi-frag skb on DMA map failure
+
+From: Yun Lu <luyun@kylinos.cn>
+
+[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ]
+
+In rtase_start_xmit(), when the head buffer DMA mapping fails after
+rtase_xmit_frags() has mapped all fragments, the error path clears
+the fragment descriptors with rtase_tx_clear_range(), which frees
+the skb through the last-frag slot and accounts tx_dropped. Control
+then falls through to the common error label, which frees the same
+skb a second time and counts it again.
+
+Return right after clearing the fragments when the skb owns frags;
+the no-frag case still drops through and frees the head skb once.
+
+Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function")
+Signed-off-by: Yun Lu <luyun@kylinos.cn>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Reviewed-by: Justin Lai <justinlai0215@realtek.com>
+Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+index a57a525327a3b..bc9b14614f7a7 100644
+--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+@@ -1620,6 +1620,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+ err_dma_1:
+       ring->skbuff[entry] = NULL;
+       rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
++      if (frags)
++              /* the frags were cleared above, along with the skb */
++              return NETDEV_TX_OK;
+ err_dma_0:
+       tp->stats.tx_dropped++;
+-- 
+2.53.0
+
diff --git a/queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
new file mode 100644 (file)
index 0000000..99ab2fb
--- /dev/null
@@ -0,0 +1,46 @@
+From ff8e24ef41809e5ccb090b1366134142919dedf1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 14:58:33 +0200
+Subject: sched/deadline: Use revised wakeup rule only for running dl_server
+
+From: Gabriele Monaco <gmonaco@redhat.com>
+
+[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ]
+
+Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for
+dl_server") applies the revised wakeup rule to any server, as a result
+servers that are not running (dl_defer_running == 0) and start with a
+deadline overflow get enqueued and can boost tasks as if they were
+running, invalidating the defer rule and the documented state model.
+
+Apply the revised wakeup rule only for deferrable servers that are
+marked as running.
+
+Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server")
+Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Acked-by: Juri Lelli <juri.lelli@redhat.com>
+Tested-by: Andrea Righi <arighi@nvidia.com>
+Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/sched/deadline.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
+index d5052f238adf7..e8b9cf0767b27 100644
+--- a/kernel/sched/deadline.c
++++ b/kernel/sched/deadline.c
+@@ -1036,7 +1036,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se)
+       if (dl_time_before(dl_se->deadline, rq_clock(rq)) ||
+           dl_entity_overflow(dl_se, rq_clock(rq))) {
+-              if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) &&
++              if (unlikely((!dl_is_implicit(dl_se) ||
++                            (dl_se->dl_defer && dl_se->dl_defer_running)) &&
+                            !dl_time_before(dl_se->deadline, rq_clock(rq)) &&
+                            !is_dl_boosted(dl_se))) {
+                       update_dl_revised_wakeup(dl_se, rq);
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..3a71045
--- /dev/null
@@ -0,0 +1,56 @@
+From aeee4f59a3ae7570d82f87b4fe6cf54a6a696572 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index c9f410c509783..e2adebc80b3a9 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..21a9861
--- /dev/null
@@ -0,0 +1,71 @@
+From 342da4b6b3e693585633d0320efdc1598cc3902d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index e90805ba868fb..7223bb18b0480 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
new file mode 100644 (file)
index 0000000..5f0c24f
--- /dev/null
@@ -0,0 +1,165 @@
+From 8f63a0f98cdc2c5f72399be4cc55262c9ddb0891 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 16:11:45 +0800
+Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
+
+From: Xingui Yang <yangxingui@huawei.com>
+
+[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
+
+Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue
+for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
+the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue,
+calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI
+devices and waits for the host to become runtime-active. But the host
+cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the
+drain is blocked on that very handler.
+
+However skipping the drain reintroduces a race: hisi_sas returns from
+resume before all PHY UP work and libsas discovery work finish. The
+controller may then autosuspend while disks are still waking up. The
+disks issue IO to a suspended controller, the IO fails, and the disks
+get disabled.
+
+Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT
+notification to after sas_drain_work(). By then the host resume is about
+to complete, so device removal through device_link no longer blocks on
+the resume and the cycle is broken.
+
+With the deadlock gone, restore sas_resume_ha() (the draining variant)
+in hisi_sas and remove sas_resume_ha_no_sync().
+
+The reorder is safe for the other libsas consumers (isci, pm8001,
+aic94xx, mvsas). During suspend, sas_suspend_devices() calls
+sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to
+NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a
+timed-out phy's disk is immediately rejected by the LLDD before reaching
+hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET),
+and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both
+complete directly via scsi_done() without entering SCSI EH. This is
+identical in both the old and new ordering since lldd_dev_gone runs
+during suspend, before resume. The reorder only affects when the
+PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work()
+vs. asynchronous after resume returns), not whether I/O can reach the
+device. aic94xx and mvsas do not register any PM ops and never reach
+this code path.
+
+Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume")
+Signed-off-by: Xingui Yang <yangxingui@huawei.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------
+ drivers/scsi/libsas/sas_init.c         | 37 +++++++++++++-------------
+ include/scsi/libsas.h                  |  1 -
+ 3 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index f30d4a58d7ab5..16256a0529253 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device)
+               return rc;
+       }
+       phys_init_v3_hw(hisi_hba);
+-
+-      /*
+-       * If a directly-attached disk is removed during suspend, a deadlock
+-       * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+-       * hisi_hba->device to be active, which can only happen when resume
+-       * completes. So don't wait for the HA event workqueue to drain upon
+-       * resume.
+-       */
+-      sas_resume_ha_no_sync(sha);
++      sas_resume_ha(sha);
+       clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+       dev_warn(dev, "end of resuming controller\n");
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index 8566bb1208a05..ac157ab6a3011 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+       }
+ }
+-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
++void sas_resume_ha(struct sas_ha_struct *ha)
+ {
+       const unsigned long tmo = msecs_to_jiffies(25000);
+       int i;
+@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+                        i, i > 1 ? "s" : "");
+       wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
++
++      /*
++       * All phys are back up or timed out. Turn on I/O and drain
++       * pending work.
++       */
++      scsi_unblock_requests(ha->shost);
++      sas_drain_work(ha);
++
++      /*
++       * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
++       * calls sas_deform_port() -> sas_destruct_devices(), which removes
++       * SCSI devices and, for LLDDs using device_link() PM sync, waits
++       * for the host to be runtime-active. Sending it before the drain
++       * would deadlock: the drain waits for the handler, the handler
++       * waits for host resume, and host resume waits for the drain to
++       * finish.
++       */
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_phy *phy = ha->sas_phy[i];
+@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               }
+       }
+-      /* all phys are back up or timed out, turn on i/o so we can
+-       * flush out disks that did not return
+-       */
+-      scsi_unblock_requests(ha->shost);
+-      if (drain)
+-              sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+       sas_queue_deferred_work(ha);
+@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+        */
+       sas_resume_insert_broadcast_ha(ha);
+ }
+-
+-void sas_resume_ha(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, true);
+-}
+ EXPORT_SYMBOL(sas_resume_ha);
+-/* A no-sync variant, which does not call sas_drain_ha(). */
+-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, false);
+-}
+-EXPORT_SYMBOL(sas_resume_ha_no_sync);
+-
+ void sas_suspend_ha(struct sas_ha_struct *ha)
+ {
+       int i;
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index a0635b128d7ab..cc0a1f320f17d 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -684,7 +684,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
+ extern int sas_unregister_ha(struct sas_ha_struct *);
+ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
+ extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
+-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
+ extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
+ int sas_phy_reset(struct sas_phy *phy, int hard_reset);
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
new file mode 100644 (file)
index 0000000..72bb5f1
--- /dev/null
@@ -0,0 +1,80 @@
+From 34c67fc4eacbb0b3c7eb3f2d389e6cd87250a89b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:30:10 +0300
+Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
+
+When target_get_sess_cmd() fails during session shutdown because
+percpu_ref_tryget_live() returns false, the command keeps the
+se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier
+without owning a reference. Final release through
+target_release_cmd_kref() then issues an unmatched percpu_ref_put().
+
+Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during
+cmd setup") moved the cmd_cnt assignment ahead of the reference
+acquisition.  Clear se_cmd->cmd_cnt whenever the initial
+target_get_sess_cmd() fails in target_init_cmd() and
+target_submit_tmr(), so release performs exactly one matching put per
+acquired reference.
+
+Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup")
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_transport.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
+index 88544c911949c..5ad2dcf730cd2 100644
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -1691,6 +1691,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+                   u32 data_length, int task_attr, int data_dir, int flags)
+ {
+       struct se_portal_group *se_tpg;
++      int ret;
+       se_tpg = se_sess->se_tpg;
+       BUG_ON(!se_tpg);
+@@ -1720,7 +1721,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
+        * kref_put() to happen during fabric packet acknowledgement.
+        */
+-      return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      if (ret)
++              se_cmd->cmd_cnt = NULL;
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(target_init_cmd);
+@@ -1996,8 +2001,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * allocation failure.
+        */
+       ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+-      if (ret < 0)
++      if (ret < 0) {
++              se_cmd->cmd_cnt = NULL;
+               return -ENOMEM;
++      }
+       if (tm_type == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = tag;
+@@ -2005,6 +2012,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+       /* See target_submit_cmd for commentary */
+       ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+       if (ret) {
++              se_cmd->cmd_cnt = NULL;
+               core_tmr_release_req(se_cmd->se_tmr_req);
+               return ret;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
new file mode 100644 (file)
index 0000000..06602bc
--- /dev/null
@@ -0,0 +1,54 @@
+From 0ed6afaf6955668266d6e051f8e7cac8c223b1e8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:58:50 +0800
+Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE
+
+From: TanZheng <tanzheng@kylinos.cn>
+
+[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ]
+
+In the iblock_execute_pr_out() function, PRO_PREEMPT,
+PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability
+checks through ops->pr_clear. The error check allows unimplemented hooks
+to pass through the gate, resulting dereferencing a NULL function
+pointer.
+
+Check whether the hooks that need to be called are supported.
+
+Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock")
+Signed-off-by: TanZheng <tanzheng@kylinos.cn>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_iblock.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
+index 66c292b7d74bc..4ec322f371bc2 100644
+--- a/drivers/target/target_core_iblock.c
++++ b/drivers/target/target_core_iblock.c
+@@ -902,7 +902,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+               break;
+       case PRO_PREEMPT:
+       case PRO_PREEMPT_AND_ABORT:
+-              if (!ops->pr_clear) {
++              if (!ops->pr_preempt) {
+                       pr_err("block_device does not support pr_preempt.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+@@ -912,8 +912,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+                                     sa == PRO_PREEMPT_AND_ABORT);
+               break;
+       case PRO_RELEASE:
+-              if (!ops->pr_clear) {
+-                      pr_err("block_device does not support pr_pclear.\n");
++              if (!ops->pr_release) {
++                      pr_err("block_device does not support pr_release.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch b/queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch
new file mode 100644 (file)
index 0000000..f5cd074
--- /dev/null
@@ -0,0 +1,46 @@
+From 25b22498d927209842878d3199fe52c395e8ca25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Mar 2026 13:43:02 +0800
+Subject: scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command
+
+From: Peter Wang <peter.wang@mediatek.com>
+
+[ Upstream commit 6475cfb81fc4f6175b6d15d1c205a5168dc10b46 ]
+
+Only return IRQ_WAKE_THREAD when MCQ and ESI are not enabled and no UIC
+command is active. The default UIC command timeout is 500ms, Using threaded
+IRQs during an active UIC command increases the risk of timeout due to
+possible preemption by other system IRQs.
+
+Signed-off-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260306054419.3816557-1-peter.wang@mediatek.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 8a309036f557 ("scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler"")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 2545f7a51211b..482a9299a5894 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -7190,8 +7190,12 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
+       struct ufs_hba *hba = __hba;
+       u32 intr_status, enabled_intr_status;
+-      /* Move interrupt handling to thread when MCQ & ESI are not enabled */
+-      if (!hba->mcq_enabled || !hba->mcq_esi_enabled)
++      /*
++       * Handle interrupt in thread if MCQ or ESI is disabled,
++       * and no active UIC command.
++       */
++      if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) &&
++          !hba->active_uic_cmd)
+               return IRQ_WAKE_THREAD;
+       intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
new file mode 100644 (file)
index 0000000..ab07520
--- /dev/null
@@ -0,0 +1,70 @@
+From 552ad6257264e1eeb52f2ea4471d1742a906810f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 01:27:26 +0800
+Subject: scsi: ufs: core: Cancel RTC work in active-active suspend
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ]
+
+UFS RTC support schedules ufs_rtc_update_work to periodically update the
+device RTC. The work can issue query commands and access the UFS host
+controller.
+
+A previous change moved the RTC work cancellation before the PRE_CHANGE
+vendor suspend callback to close a race in the common suspend path.
+However, the active-active path jumps directly to vops_suspend after
+flushing exception handling work and therefore bypasses the
+cancellation.
+
+If the RTC work runs while the vendor suspend callback is gating or
+otherwise changing hardware state, it can access the controller during
+suspend and trigger an SError.
+
+Cancel the RTC work before entering the vendor suspend callback in the
+active-active path. Since this path now cancels the work, move the RTC
+work scheduling outside the device and link state restoration block in
+the resume path. This restarts RTC updates after an active-active
+suspend and resume cycle.
+
+Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend")
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 8de3d0886a121..2545f7a51211b 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -9873,6 +9873,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+                       req_link_state == UIC_LINK_ACTIVE_STATE) {
+               ufshcd_disable_auto_bkops(hba);
+               flush_work(&hba->eeh_work);
++              cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
+               goto vops_suspend;
+       }
+@@ -10082,10 +10083,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+               if (ret)
+                       goto set_old_link_state;
+               ufshcd_set_timestamp_attr(hba);
+-              schedule_delayed_work(&hba->ufs_rtc_update_work,
+-                                    msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+       }
++      schedule_delayed_work(&hba->ufs_rtc_update_work,
++                            msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
++
+       if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+               ufshcd_enable_auto_bkops(hba);
+       else
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch b/queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch
new file mode 100644 (file)
index 0000000..5b47eaf
--- /dev/null
@@ -0,0 +1,123 @@
+From aabb7f1b10c169074903a3a36c37702c73ab3f58 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 10:12:28 -0700
+Subject: scsi: ufs: core: Revert "Delegate the interrupt service routine to a
+ threaded IRQ handler"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ Upstream commit 8a309036f557d3ff4efb2beea5132ba91172d934 ]
+
+There have been multiple reports of performance regressions caused by
+commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service
+routine to a threaded IRQ handler"). Hence this revert.
+
+This patch reverts most of the following commits:
+
+ * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service
+   routine to a threaded IRQ handler")
+
+ * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active
+   UIC command")
+
+This patch preserves the following commits:
+
+ * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode")
+
+ * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI
+   host lock")
+
+Cc: Neil Armstrong <neil.armstrong@linaro.org>
+Cc: 孙魁 (Kui Sun) <kui.sun@unisoc.com>
+Cc: André Draszik <andre.draszik@linaro.org>
+Cc: Gregory CLEMENT <gregory.clement@bootlin.com>
+Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler")
+Signed-off-by: Bart Van Assche <bvanassche@acm.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Tested-by: André Draszik <andre.draszik@linaro.org> # on Pixel 6
+Reviewed-by: André Draszik <andre.draszik@linaro.org>
+Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 39 +++------------------------------------
+ 1 file changed, 3 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 482a9299a5894..504600f1e08cd 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -7128,7 +7128,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+ }
+ /**
+- * ufshcd_threaded_intr - Threaded interrupt service routine
++ * ufshcd_intr - Main interrupt service routine
+  * @irq: irq number
+  * @__hba: pointer to adapter instance
+  *
+@@ -7136,7 +7136,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+  *  IRQ_HANDLED - If interrupt is valid
+  *  IRQ_NONE    - If invalid interrupt
+  */
+-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
++static irqreturn_t ufshcd_intr(int irq, void *__hba)
+ {
+       u32 last_intr_status, intr_status, enabled_intr_status = 0;
+       irqreturn_t retval = IRQ_NONE;
+@@ -7175,38 +7175,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
+       return retval;
+ }
+-/**
+- * ufshcd_intr - Main interrupt service routine
+- * @irq: irq number
+- * @__hba: pointer to adapter instance
+- *
+- * Return:
+- *  IRQ_HANDLED     - If interrupt is valid
+- *  IRQ_WAKE_THREAD - If handling is moved to threaded handled
+- *  IRQ_NONE        - If invalid interrupt
+- */
+-static irqreturn_t ufshcd_intr(int irq, void *__hba)
+-{
+-      struct ufs_hba *hba = __hba;
+-      u32 intr_status, enabled_intr_status;
+-
+-      /*
+-       * Handle interrupt in thread if MCQ or ESI is disabled,
+-       * and no active UIC command.
+-       */
+-      if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) &&
+-          !hba->active_uic_cmd)
+-              return IRQ_WAKE_THREAD;
+-
+-      intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
+-      enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+-
+-      ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
+-
+-      /* Directly handle interrupts since MCQ ESI handlers does the hard job */
+-      return ufshcd_sl_intr(hba, enabled_intr_status);
+-}
+-
+ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
+ {
+       int err = 0;
+@@ -10835,8 +10803,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
+       ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+       /* IRQ registration */
+-      err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr,
+-                                      IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba);
++      err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+       if (err) {
+               dev_err(hba->dev, "request irq failed\n");
+               goto out_disable;
+-- 
+2.53.0
+
diff --git a/queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..2925de1
--- /dev/null
@@ -0,0 +1,66 @@
+From 1b1a0106d20fecf8a2d2ee0a2211e3793296ab6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index dc2265ebb11b8..03f723042434b 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
diff --git a/queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch b/queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch
new file mode 100644 (file)
index 0000000..6de704b
--- /dev/null
@@ -0,0 +1,86 @@
+From 4d0fbcf75936d884dad94ab2b62aedee97dbb0dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Nov 2025 21:26:39 +0000
+Subject: selftest: af_unix: Create its own .gitignore.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ Upstream commit adb6b68c50604f3113d62758e839cc0186b94ce8 ]
+
+Somehow AF_UNIX tests have reused ../.gitignore,
+but now NIPA warns about it.
+
+Let's create .gitignore under af_unix/.
+
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20251124212805.486235-2-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/net/.gitignore         | 8 --------
+ tools/testing/selftests/net/af_unix/.gitignore | 8 ++++++++
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+ create mode 100644 tools/testing/selftests/net/af_unix/.gitignore
+
+diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
+index f627f1a2a2b8c..e2e2063399675 100644
+--- a/tools/testing/selftests/net/.gitignore
++++ b/tools/testing/selftests/net/.gitignore
+@@ -4,7 +4,6 @@ bind_timewait
+ bind_wildcard
+ busy_poller
+ cmsg_sender
+-diag_uid
+ epoll_busy_poll
+ fin_ack_lat
+ gro
+@@ -18,7 +17,6 @@ ipv6_flowlabel
+ ipv6_flowlabel_mgr
+ ipv6_fragmentation
+ log.txt
+-msg_oob
+ msg_zerocopy
+ netlink-dumps
+ nettest
+@@ -35,9 +33,6 @@ reuseport_bpf_numa
+ reuseport_dualstack
+ rxtimestamp
+ sctp_hello
+-scm_inq
+-scm_pidfd
+-scm_rights
+ sk_bind_sendto_listen
+ sk_connect_zero_addr
+ sk_so_peek_off
+@@ -45,7 +40,6 @@ skf_net_off
+ socket
+ so_incoming_cpu
+ so_netns_cookie
+-so_peek_off
+ so_txtime
+ so_rcv_listener
+ stress_reuseport_listen
+@@ -65,5 +59,3 @@ txtimestamp
+ udpgso
+ udpgso_bench_rx
+ udpgso_bench_tx
+-unix_connect
+-unix_connreset
+diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
+new file mode 100644
+index 0000000000000..240b26740c9e0
+--- /dev/null
++++ b/tools/testing/selftests/net/af_unix/.gitignore
+@@ -0,0 +1,8 @@
++diag_uid
++msg_oob
++scm_inq
++scm_pidfd
++scm_rights
++so_peek_off
++unix_connect
++unix_connreset
+-- 
+2.53.0
+
diff --git a/queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch b/queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch
new file mode 100644 (file)
index 0000000..695fe1b
--- /dev/null
@@ -0,0 +1,245 @@
+From 908104d927810c5929f96d2d265b840b9d09f00c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Nov 2025 12:28:02 +0100
+Subject: selftests: af_unix: Add tests for ECONNRESET and EOF semantics
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
+
+[ Upstream commit 45a1cd8346ca245a1ca475b26eb6ceb9d8b7c6f0 ]
+
+Add selftests to verify and document Linux’s intended behaviour for
+UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes.
+The tests verify that:
+
+ 1. SOCK_STREAM returns EOF when the peer closes normally.
+ 2. SOCK_STREAM returns ECONNRESET if the peer closes with unread data.
+ 3. SOCK_SEQPACKET returns EOF when the peer closes normally.
+ 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data.
+ 5. SOCK_DGRAM does not return ECONNRESET when the peer closes.
+
+This follows up on review feedback suggesting a selftest to clarify
+Linux’s semantics.
+
+Suggested-by: Kuniyuki Iwashima <kuniyu@google.com>
+Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
+Link: https://patch.msgid.link/20251113112802.44657-1-adelodunolaoluwa@yahoo.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/net/.gitignore        |   1 +
+ tools/testing/selftests/net/af_unix/Makefile  |   1 +
+ .../selftests/net/af_unix/unix_connreset.c    | 177 ++++++++++++++++++
+ 3 files changed, 179 insertions(+)
+ create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c
+
+diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
+index 8f9850a71f542..f627f1a2a2b8c 100644
+--- a/tools/testing/selftests/net/.gitignore
++++ b/tools/testing/selftests/net/.gitignore
+@@ -66,3 +66,4 @@ udpgso
+ udpgso_bench_rx
+ udpgso_bench_tx
+ unix_connect
++unix_connreset
+diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
+index 2889403e35468..4c0375e28bbee 100644
+--- a/tools/testing/selftests/net/af_unix/Makefile
++++ b/tools/testing/selftests/net/af_unix/Makefile
+@@ -13,6 +13,7 @@ TEST_GEN_PROGS := \
+       scm_rights \
+       so_peek_off \
+       unix_connect \
++      unix_connreset \
+ # end of TEST_GEN_PROGS
+ include ../../lib.mk
+diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c
+new file mode 100644
+index 0000000000000..bffef2b54bfd1
+--- /dev/null
++++ b/tools/testing/selftests/net/af_unix/unix_connreset.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Selftest for AF_UNIX socket close and ECONNRESET behaviour.
++ *
++ * This test verifies:
++ *  1. SOCK_STREAM returns EOF when the peer closes normally.
++ *  2. SOCK_STREAM returns ECONNRESET if peer closes with unread data.
++ *  3. SOCK_SEQPACKET returns EOF when the peer closes normally.
++ *  4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data.
++ *  5. SOCK_DGRAM does not return ECONNRESET when the peer closes.
++ *
++ * These tests document the intended Linux behaviour.
++ *
++ */
++
++#define _GNU_SOURCE
++#include <stdlib.h>
++#include <string.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include "../../kselftest_harness.h"
++
++#define SOCK_PATH "/tmp/af_unix_connreset.sock"
++
++static void remove_socket_file(void)
++{
++      unlink(SOCK_PATH);
++}
++
++FIXTURE(unix_sock)
++{
++      int server;
++      int client;
++      int child;
++};
++
++FIXTURE_VARIANT(unix_sock)
++{
++      int socket_type;
++      const char *name;
++};
++
++FIXTURE_VARIANT_ADD(unix_sock, stream) {
++      .socket_type = SOCK_STREAM,
++      .name = "SOCK_STREAM",
++};
++
++FIXTURE_VARIANT_ADD(unix_sock, dgram) {
++      .socket_type = SOCK_DGRAM,
++      .name = "SOCK_DGRAM",
++};
++
++FIXTURE_VARIANT_ADD(unix_sock, seqpacket) {
++      .socket_type = SOCK_SEQPACKET,
++      .name = "SOCK_SEQPACKET",
++};
++
++FIXTURE_SETUP(unix_sock)
++{
++      struct sockaddr_un addr = {};
++      int err;
++
++      addr.sun_family = AF_UNIX;
++      strcpy(addr.sun_path, SOCK_PATH);
++      remove_socket_file();
++
++      self->server = socket(AF_UNIX, variant->socket_type, 0);
++      ASSERT_LT(-1, self->server);
++
++      err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr));
++      ASSERT_EQ(0, err);
++
++      if (variant->socket_type == SOCK_STREAM ||
++          variant->socket_type == SOCK_SEQPACKET) {
++              err = listen(self->server, 1);
++              ASSERT_EQ(0, err);
++      }
++
++      self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0);
++      ASSERT_LT(-1, self->client);
++
++      err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr));
++      ASSERT_EQ(0, err);
++}
++
++FIXTURE_TEARDOWN(unix_sock)
++{
++      if (variant->socket_type == SOCK_STREAM ||
++          variant->socket_type == SOCK_SEQPACKET)
++              close(self->child);
++
++      close(self->client);
++      close(self->server);
++      remove_socket_file();
++}
++
++/* Test 1: peer closes normally */
++TEST_F(unix_sock, eof)
++{
++      char buf[16] = {};
++      ssize_t n;
++
++      if (variant->socket_type == SOCK_STREAM ||
++          variant->socket_type == SOCK_SEQPACKET) {
++              self->child = accept(self->server, NULL, NULL);
++              ASSERT_LT(-1, self->child);
++
++              close(self->child);
++      } else {
++              close(self->server);
++      }
++
++      n = recv(self->client, buf, sizeof(buf), 0);
++
++      if (variant->socket_type == SOCK_STREAM ||
++          variant->socket_type == SOCK_SEQPACKET) {
++              ASSERT_EQ(0, n);
++      } else {
++              ASSERT_EQ(-1, n);
++              ASSERT_EQ(EAGAIN, errno);
++      }
++}
++
++/* Test 2: peer closes with unread data */
++TEST_F(unix_sock, reset_unread_behavior)
++{
++      char buf[16] = {};
++      ssize_t n;
++
++      /* Send data that will remain unread */
++      send(self->client, "hello", 5, 0);
++
++      if (variant->socket_type == SOCK_DGRAM) {
++              /* No real connection, just close the server */
++              close(self->server);
++      } else {
++              self->child = accept(self->server, NULL, NULL);
++              ASSERT_LT(-1, self->child);
++
++              /* Peer closes before client reads */
++              close(self->child);
++      }
++
++      n = recv(self->client, buf, sizeof(buf), 0);
++      ASSERT_EQ(-1, n);
++
++      if (variant->socket_type == SOCK_STREAM ||
++          variant->socket_type == SOCK_SEQPACKET) {
++              ASSERT_EQ(ECONNRESET, errno);
++      } else {
++              ASSERT_EQ(EAGAIN, errno);
++      }
++}
++
++/* Test 3: closing unaccepted (embryo) server socket should reset client. */
++TEST_F(unix_sock, reset_closed_embryo)
++{
++      char buf[16] = {};
++      ssize_t n;
++
++      if (variant->socket_type == SOCK_DGRAM)
++              SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET");
++
++      /* Close server without accept()ing */
++      close(self->server);
++
++      n = recv(self->client, buf, sizeof(buf), 0);
++
++      ASSERT_EQ(-1, n);
++      ASSERT_EQ(ECONNRESET, errno);
++}
++
++TEST_HARNESS_MAIN
++
+-- 
+2.53.0
+
diff --git a/queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch b/queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch
new file mode 100644 (file)
index 0000000..a844fb3
--- /dev/null
@@ -0,0 +1,42 @@
+From 27e2c8d668078157378a0c4f8f8bf0b53f15c8c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 16:02:10 +0800
+Subject: selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE
+
+From: Haofeng Li <lihaofeng@kylinos.cn>
+
+[ Upstream commit b3a7aa9c0020ae549a0d4964867ff66d2bd61709 ]
+
+Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE")
+renamed the LKDTM crash type and selftest configuration but missed the
+entry in tests.txt.
+
+As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh
+skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename
+the test entry so the generated runner uses the registered crash type.
+
+Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE")
+Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
+Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/lkdtm/tests.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt
+index cff124c1eddd3..665a13a37363c 100644
+--- a/tools/testing/selftests/lkdtm/tests.txt
++++ b/tools/testing/selftests/lkdtm/tests.txt
+@@ -74,7 +74,7 @@ USERCOPY_STACK_FRAME_TO
+ USERCOPY_STACK_FRAME_FROM
+ USERCOPY_STACK_BEYOND
+ USERCOPY_KERNEL
+-STACKLEAK_ERASING OK: the rest of the thread stack is properly erased
++KSTACK_ERASE OK: the rest of the thread stack is properly erased
+ CFI_FORWARD_PROTO
+ CFI_BACKWARD call trace:|ok: control flow unchanged
+ FORTIFY_STRSCPY detected buffer overflow
+-- 
+2.53.0
+
diff --git a/queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch b/queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch
new file mode 100644 (file)
index 0000000..0cf3f3c
--- /dev/null
@@ -0,0 +1,253 @@
+From e6e933ffdc705984f642563ddeba5a58068d20e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 14:29:02 -0400
+Subject: selftests/net/af_unix: test listen() rejects wrong socket states
+
+From: John Ericson <mail@johnericson.me>
+
+[ Upstream commit 7f57c650d08b8793bb551bdb33ad876535ef9fe8 ]
+
+Add a regression test for the unix_listen() state check. The key case is
+listen() on a bound socket that has already been connected: it is no
+longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A
+prepare_peercred() call slipped in ahead of that check once left err at 0
+and made listen() silently succeed there instead; this guards against a
+repeat.
+
+The neighbouring outcomes are covered too so they cannot regress the same
+way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a
+socket already in TCP_LISTEN is allowed, and an unbound socket fails with
+EINVAL.
+
+Each case runs for both listenable socket types (SOCK_STREAM and
+SOCK_SEQPACKET) and both pathname and abstract addresses.
+
+Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
+Signed-off-by: John Ericson <mail@johnericson.me>
+Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../testing/selftests/net/af_unix/.gitignore  |   1 +
+ tools/testing/selftests/net/af_unix/Makefile  |   1 +
+ .../selftests/net/af_unix/unix_listen.c       | 187 ++++++++++++++++++
+ 3 files changed, 189 insertions(+)
+ create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c
+
+diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
+index 240b26740c9e0..9731766441036 100644
+--- a/tools/testing/selftests/net/af_unix/.gitignore
++++ b/tools/testing/selftests/net/af_unix/.gitignore
+@@ -6,3 +6,4 @@ scm_rights
+ so_peek_off
+ unix_connect
+ unix_connreset
++unix_listen
+diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
+index 4c0375e28bbee..57d159803a3ab 100644
+--- a/tools/testing/selftests/net/af_unix/Makefile
++++ b/tools/testing/selftests/net/af_unix/Makefile
+@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \
+       so_peek_off \
+       unix_connect \
+       unix_connreset \
++      unix_listen \
+ # end of TEST_GEN_PROGS
+ include ../../lib.mk
+diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c
+new file mode 100644
+index 0000000000000..416fa3e5bfe9b
+--- /dev/null
++++ b/tools/testing/selftests/net/af_unix/unix_listen.c
+@@ -0,0 +1,187 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Tests for the state checks in AF_UNIX listen().
++ *
++ * The central case is a regression test: listen() on a bound socket that
++ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must
++ * fail with EINVAL.  A prior change accidentally let it return success
++ * without doing anything, because a helper called in between reset the
++ * error code to 0.  The neighbouring checks (unbound, already listening)
++ * are tested too so they cannot silently regress the same way.
++ *
++ * Every case runs for both listenable socket types (SOCK_STREAM and
++ * SOCK_SEQPACKET) and both pathname and abstract addresses.
++ */
++#define _GNU_SOURCE
++
++#include <errno.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <sys/socket.h>
++#include <sys/un.h>
++
++#include "kselftest_harness.h"
++
++#define SK_NAME               "unix_listen_sk"
++#define SRV_NAME      "unix_listen_srv"
++
++FIXTURE(unix_listen)
++{
++      int sk;                 /* socket under test */
++      int server;             /* a listening peer, when a test needs one */
++      struct sockaddr_un addr, srv_addr;
++      socklen_t addrlen, srv_addrlen;
++};
++
++FIXTURE_VARIANT(unix_listen)
++{
++      int type;
++      int abstract;
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, stream_pathname)
++{
++      .type = SOCK_STREAM,
++      .abstract = 0,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, stream_abstract)
++{
++      .type = SOCK_STREAM,
++      .abstract = 1,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname)
++{
++      .type = SOCK_SEQPACKET,
++      .abstract = 0,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract)
++{
++      .type = SOCK_SEQPACKET,
++      .abstract = 1,
++};
++
++/* Fill @addr with a pathname or abstract address named @name. */
++static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name,
++                             int abstract)
++{
++      size_t len = strlen(name);
++
++      memset(addr, 0, sizeof(*addr));
++      addr->sun_family = AF_UNIX;
++      /* An abstract address leads with a NUL and has no filesystem entry. */
++      memcpy(addr->sun_path + (abstract ? 1 : 0), name, len);
++
++      return offsetof(struct sockaddr_un, sun_path) + len + 1;
++}
++
++FIXTURE_SETUP(unix_listen)
++{
++      self->sk = -1;
++      self->server = -1;
++      self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract);
++      self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME,
++                                        variant->abstract);
++}
++
++FIXTURE_TEARDOWN(unix_listen)
++{
++      if (self->sk >= 0)
++              close(self->sk);
++      if (self->server >= 0)
++              close(self->server);
++
++      /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */
++      if (!variant->abstract) {
++              remove(SK_NAME);
++              remove(SRV_NAME);
++      }
++}
++
++/* A bound socket in TCP_CLOSE is the normal, allowed case. */
++TEST_F(unix_listen, bound_is_ok)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(0, err);
++}
++
++/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */
++TEST_F(unix_listen, relisten_is_ok)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 16);
++      EXPECT_EQ(0, err);
++}
++
++/* listen() on an unbound socket fails: there is nothing to listen on. */
++TEST_F(unix_listen, unbound_is_einval)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(-1, err);
++      EXPECT_EQ(EINVAL, errno);
++}
++
++/*
++ * The regression: a bound socket that has already been connected is not in
++ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather
++ * than quietly succeeding.
++ */
++TEST_F(unix_listen, connected_is_einval)
++{
++      int err;
++
++      self->server = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->server);
++
++      err = bind(self->server, (struct sockaddr *)&self->srv_addr,
++                 self->srv_addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->server, 8);
++      ASSERT_EQ(0, err);
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      /* Bind first so the unbound check does not mask the state check. */
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = connect(self->sk, (struct sockaddr *)&self->srv_addr,
++                    self->srv_addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(-1, err);
++      EXPECT_EQ(EINVAL, errno);
++}
++
++TEST_HARNESS_MAIN
+-- 
+2.53.0
+
diff --git a/queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch b/queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch
new file mode 100644 (file)
index 0000000..a133fbd
--- /dev/null
@@ -0,0 +1,57 @@
+From 8b280c9c66aa3ed4f83a67be337338f81641bf97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:35:52 +0800
+Subject: selftests/seccomp: Fix pointer type mismatch build error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Kuan-Ying Lee <kuan-ying.lee@canonical.com>
+
+[ Upstream commit 3421b9b056a6576d0ebac1030eafb48ad0544092 ]
+
+We hit the following build error while running the seccomp selftests in
+our testing.
+
+CC seccomp_bpf
+seccomp_bpf.c: In function ‘UPROBE_setup’:
+seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types]
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^
+seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^~~~~~~~~~~~~~~~
+seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^~~~~~~~~~~~~
+
+get_uprobe_offset() takes a 'const void *' argument, so cast both
+operands to 'void *'.
+
+Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp")
+Signed-off-by: Kuan-Ying Lee <kuan-ying.lee@canonical.com>
+Acked-by: Jiri Olsa <jolsa@kernel.org>
+Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
+index 874f17763536b..19c9afa0be719 100644
+--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
++++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
+@@ -5172,7 +5172,8 @@ FIXTURE_SETUP(UPROBE)
+               ASSERT_GE(bit, 0);
+       }
+-      offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
++      offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe
++                                                    : (void *)probed_uprobe);
+       ASSERT_GE(offset, 0);
+       if (variant->uretprobe)
+-- 
+2.53.0
+
index aca90a1feec54196dca4ea8af1657e252d904794..9969c904ffba726ae7b240c3a711a75106096d1a 100644 (file)
@@ -11,3 +11,149 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch
 mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch
 thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
+pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
+pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
+iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch
+gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
+selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch
+selftests-seccomp-fix-pointer-type-mismatch-build-er.patch
+ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
+ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
+phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
+btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
+btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
+btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch
+btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+arch-x86-mshyperv-discover-confidential-vmbus-availa.patch
+drivers-hv-rename-fields-for-synic-message-and-event.patch
+drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch
+drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
+mshv-fix-duplicate-gsi-detection-for-gsi-0.patch
+mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch
+kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch
+keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+ipvs-fix-the-checksum-validations.patch
+ipvs-fix-places-with-wrong-packet-offsets.patch
+ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+asoc-sdca-ensure-that-control-range-is-large-enough-.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch
+selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch
+selftest-af_unix-create-its-own-.gitignore.patch
+selftests-net-af_unix-test-listen-rejects-wrong-sock.patch
+xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch
+xsk-use-a-smaller-new-lock-for-shared-pool-case.patch
+xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch
+pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+asoc-tas2781-use-correct-calibration-data-for-sinega.patch
+spi-spi-cadence-supports-transmission-with-bits_per_.patch
+spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
+hwmon-nct6775-core-fix-number-of-temperature-registe.patch
+hwmon-ina2xx-make-it-easier-to-add-more-devices.patch
+hwmon-ina2xx-add-support-for-ina234.patch
+hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch
+hwmon-ina2xx-fix-various-overflow-issues.patch
+hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
+hwmon-sht3x-fix-unaligned-accesses.patch
+hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
+hwmon-nzxt-smart2-dma-align-output-buffer.patch
+net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch
+netfs-handle-single-writeback-rolling-buffer-allocat.patch
+netfs-release-readahead-folios-on-iterator-preparati.patch
+net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+idpf-adjust-txq-ring-count-minimum.patch
+idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
+ice-suppress-dpll-errors-during-reset-recovery.patch
+bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+bluetooth-iso-lock-sk-in-iso_sock_getname.patch
+bluetooth-hci-add-initial-support-for-past.patch
+bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch
+bluetooth-iso-lock-sk-in-iso_connect_ind.patch
+bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
+bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
+bluetooth-iso-fix-not-updating-bis-sender-source-add.patch
+bluetooth-iso-fix-connected-closed-transition-on-shu.patch
+bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch
+bluetooth-iso-fix-leaking-sk-after-socket-release.patch
+bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
+bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch
+bluetooth-iso-fix-refcounting-of-iso_conn.patch
+bluetooth-btintel-validate-length-before-parsing-dia.patch
+bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
+bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
+bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
+bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
+x86-boot-add-volatile-clobbers-and-zero-length-test-.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
+scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
+scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch
+scsi-ufs-core-revert-delegate-the-interrupt-service-.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch
+fprobe-fix-module-reference-count-leak-on-error-in-r.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
+riscv-drop-__init-from-vec_check_unaligned_access_sp.patch
+accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
+riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
+net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
+net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
+ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch
+net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
+spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
+drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch
+ksmbd-return-success-for-deferred-final-close.patch
+ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
+iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch
+mshv-fix-race-in-mshv_irqfd_deassign.patch
+mshv-adjust-interrupt-control-structure-for-arm64.patch
+mshv-fix-level-triggered-check-on-uninitialized-data.patch
+mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch
diff --git a/queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..9c2fb6f
--- /dev/null
@@ -0,0 +1,85 @@
+From 3dd1d2ed8c1807dfb94dd90e2fd4600664e39e4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 7694ca283fdc3..db9d9a1983c73 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -1515,8 +1515,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1628,8 +1630,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1908,8 +1912,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
new file mode 100644 (file)
index 0000000..48dbfe2
--- /dev/null
@@ -0,0 +1,111 @@
+From 7eb6f327258307f67000105d044d953e68bb7d4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 18:25:10 +0530
+Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Srikanth Boyapally <srikanth.boyapally@amd.com>
+
+[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ]
+
+SPI host transfers could intermittently stall with spi_transfer timeouts.
+The TXFULL condition was checked only once in cdns_transfer_one() before
+cdns_spi_process_fifo(), so if the FIFO became full again during refill,
+writes could be dropped and the transfer would never complete.
+
+Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so
+the 10µs back-off is applied per FIFO entry during filling, ensuring
+forward progress and eliminating spurious timeouts.
+
+Restrict the delay to host mode using spi_controller_is_target(), the
+controller is passed into cdns_spi_process_fifo() so the check is made at
+the point of use. In target mode this delay must not run as it causes the
+target to miss its transfer window and corrupt data.
+
+Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()")
+Signed-off-by: Srikanth Boyapally <srikanth.boyapally@amd.com>
+Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index 75803a7f4cb11..403e8141dff89 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi)
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
++ * @ctlr:     Pointer to the spi_controller structure
+  * @xspi:     Pointer to the cdns_spi structure
+  * @ntx:      Number of bytes to pack into the TX FIFO
+  * @nrx:      Number of bytes to drain from the RX FIFO
+  */
+-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
++static void cdns_spi_process_fifo(struct spi_controller *ctlr,
++                                struct cdns_spi *xspi, int ntx, int nrx)
+ {
+       ntx = clamp(ntx, 0, xspi->tx_bytes);
+       nrx = clamp(nrx, 0, xspi->rx_bytes);
+@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+               }
+               if (ntx) {
++                      /* When xspi in busy condition, bytes may send failed,
++                       * then spi control didn't work thoroughly, add one byte
++                       * delay. Only in host mode; in target mode this delay
++                       * causes data corruption as the target fails to prepare
++                       * data in time.
++                       */
++                      if (!spi_controller_is_target(ctlr) &&
++                          (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL))
++                              udelay(10);
++
+                       cdns_spi_writer(xspi);
+                       ntx--;
+               }
+@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, 1);
+               if (xspi->tx_bytes) {
+-                      cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt);
+               } else {
+                       /* Fixed delay due to controller limitation with
+                        * RX_NEMPTY incorrect status
+                        * Xilinx AR:65885 contains more details
+                        */
+                       udelay(10);
+-                      cdns_spi_process_fifo(xspi, 0, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt);
+                       cdns_spi_write(xspi, CDNS_SPI_IDR,
+                                      CDNS_SPI_IXR_DEFAULT);
+                       spi_finalize_current_transfer(ctlr);
+@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
+       }
+-      /* When xspi in busy condition, bytes may send failed,
+-       * then spi control didn't work thoroughly, add one byte delay
+-       */
+-      if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+-              udelay(10);
+-
+       xspi->n_bytes = cdns_spi_n_bytes(transfer);
+       xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+       xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+-      cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
++      cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+       return transfer->len;
+-- 
+2.53.0
+
diff --git a/queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch
new file mode 100644 (file)
index 0000000..1e24cd7
--- /dev/null
@@ -0,0 +1,200 @@
+From 3a6c0b176e5b4cf6ab7a71262a06ccf9850722b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Oct 2025 15:30:02 +0800
+Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and
+ 32
+
+From: Jun Guo <jun.guo@cixtech.com>
+
+[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ]
+
+The default FIFO data width of the Cadence SPI IP is 8 bits, but
+the hardware supports configurations of 16 bits and 32 bits.
+This patch enhances the driver to support communication with both
+16-bits and 32-bits FIFO data widths.
+
+Signed-off-by: Jun Guo <jun.guo@cixtech.com>
+Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 93 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index 7a745c2e0892c..75803a7f4cb11 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -109,6 +109,7 @@
+  * @rxbuf:            Pointer to the RX buffer
+  * @tx_bytes:         Number of bytes left to transfer
+  * @rx_bytes:         Number of bytes requested
++ * @n_bytes:          Number of bytes per word
+  * @dev_busy:         Device busy flag
+  * @is_decoded_cs:    Flag for decoder property set or not
+  * @tx_fifo_depth:    Depth of the TX FIFO
+@@ -120,16 +121,24 @@ struct cdns_spi {
+       struct clk *pclk;
+       unsigned int clk_rate;
+       u32 speed_hz;
+-      const u8 *txbuf;
+-      u8 *rxbuf;
++      const void *txbuf;
++      void *rxbuf;
+       int tx_bytes;
+       int rx_bytes;
++      u8 n_bytes;
+       u8 dev_busy;
+       u32 is_decoded_cs;
+       unsigned int tx_fifo_depth;
+       struct reset_control *rstc;
+ };
++enum cdns_spi_frame_n_bytes {
++      CDNS_SPI_N_BYTES_NULL = 0,
++      CDNS_SPI_N_BYTES_U8 = 1,
++      CDNS_SPI_N_BYTES_U16 = 2,
++      CDNS_SPI_N_BYTES_U32 = 4
++};
++
+ /* Macros for the SPI controller read/write */
+ static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
+ {
+@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
+       return 0;
+ }
++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
++{
++      if (transfer->bits_per_word <= 8)
++              return CDNS_SPI_N_BYTES_U8;
++      else if (transfer->bits_per_word <= 16)
++              return CDNS_SPI_N_BYTES_U16;
++      else
++              return CDNS_SPI_N_BYTES_U32;
++}
++
++static inline void cdns_spi_reader(struct cdns_spi *xspi)
++{
++      u32 rxw = 0;
++
++      if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
++              pr_err("%s: rxbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
++      if (xspi->rxbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      *(u8 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      *(u16 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      *(u32 *)xspi->rxbuf = rxw;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
++      }
++}
++
++static inline void cdns_spi_writer(struct cdns_spi *xspi)
++{
++      u32 txw = 0;
++
++      if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
++              pr_err("%s: txbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      if (xspi->txbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      txw = *(u8 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      txw = *(u16 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      txw = *(u32 *)xspi->txbuf;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
++              xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
++      }
++}
++
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
+  * @xspi:     Pointer to the cdns_spi structure
+@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+       while (ntx || nrx) {
+               if (nrx) {
+-                      u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
+-
+-                      if (xspi->rxbuf)
+-                              *xspi->rxbuf++ = data;
+-
++                      cdns_spi_reader(xspi);
+                       nrx--;
+               }
+               if (ntx) {
+-                      if (xspi->txbuf)
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
+-                      else
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
+-
++                      cdns_spi_writer(xspi);
+                       ntx--;
+               }
+-
+       }
+ }
+@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+       if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+               udelay(10);
++      xspi->n_bytes = cdns_spi_n_bytes(transfer);
++      xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
++      xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
++
+       cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
+       ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
+       ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
++      if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
++              ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
++
+       if (!spi_controller_is_target(ctlr)) {
+               ctlr->mode_bits |=  SPI_CS_HIGH;
+               ctlr->set_cs = cdns_spi_chipselect;
+@@ -812,6 +891,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
+ static const struct of_device_id cdns_spi_of_match[] = {
+       { .compatible = "xlnx,zynq-spi-r1p6" },
++      { .compatible = "cix,sky1-spi-r1p6" },
+       { .compatible = "cdns,spi-r1p6" },
+       { /* end of table */ }
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch b/queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch
new file mode 100644 (file)
index 0000000..f334418
--- /dev/null
@@ -0,0 +1,197 @@
+From 6abf8c5ac564f93d22f22f1ea594ac49f17a300d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 18:18:08 +0800
+Subject: spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all
+ supported SoCs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Haibo Chen <haibo.chen@nxp.com>
+
+[ Upstream commit 9c19d60fea9f46ed0c3394653ef4941f1a459968 ]
+
+The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for
+different sample clock source selection") introduced a global 166MHz
+cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing
+specification (Section 3.9.9, page 65).
+
+After reviewing the FlexSPI timing parameters in the datasheets for all
+supported SoCs, the following corrections and additions are needed:
+
+1. SDR mode (RXCLKSRC=0) limits vary per SoC:
+   - i.MX8MN/MM/MP/95: 66MHz  (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10,
+                                IMX8MPCEC, IMX95CEC Rev.8 §4.11.7)
+   - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC,
+                                   IMX8ULPCEC §7.3.1 ND mode)
+   - LX2160A: 100MHz            (LX2160ACEC FlexSPI timing parameters)
+
+2. DTR mode (RXCLKSRC=3) limits vary per SoC:
+   - i.MX8MN/MM/MP/ULP: 166MHz
+   - i.MX8QXP/QM/DXL: 200MHz   (same FlexSPI IP across this family)
+   - i.MX95: 200MHz             (IMX95CEC §4.11.7.3.2.3 Table 106)
+   - LX2160A: DTR disabled      (FSPI_QUIRK_DISABLE_DTR)
+
+Update related platform data with correct speed limation according
+to datasheet.
+
+Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection")
+Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
+Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 80 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
+index 95ccb1f7dfafa..b504a5c86692a 100644
+--- a/drivers/spi/spi-nxp-fspi.c
++++ b/drivers/spi/spi-nxp-fspi.c
+@@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data {
+       unsigned int quirks;
+       unsigned int lut_num;
+       bool little_endian;
++      /*
++       * The max clock rate (Hz) that FlexSPI can output to the device
++       * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero.
++       * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode.
++       */
++      unsigned long max_sdr_rate;
++      /*
++       * The max clock rate (Hz) that FlexSPI can output to the device
++       * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero.
++       * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz.
++       */
++      unsigned long max_dtr_rate;
+ };
+ static struct nxp_fspi_devtype_data lx2160a_data = {
+@@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
+       .quirks = FSPI_QUIRK_DISABLE_DTR,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk.
++       */
++      .max_sdr_rate = 100000000,
+ };
+ static struct nxp_fspi_devtype_data imx8mm_data = {
+@@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
+       .quirks = 0,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 166000000,
++};
++
++static struct nxp_fspi_devtype_data imx8mp_data = {
++      .rxfifo = SZ_512,       /* (64  * 64 bits)  */
++      .txfifo = SZ_1K,        /* (128 * 64 bits)  */
++      .ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
++      .quirks = 0,
++      .lut_num = 32,
++      .little_endian = true,  /* little-endian    */
++      /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 166000000,
+ };
+ static struct nxp_fspi_devtype_data imx8qxp_data = {
+@@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = {
+       .quirks = 0,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz.
++       * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 200000000,
+ };
+ static struct nxp_fspi_devtype_data imx8dxl_data = {
+@@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = {
+       .quirks = FSPI_QUIRK_USE_IP_ONLY,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz,
++       * DDR RXCLKSRC=3 max 200MHz.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 200000000,
+ };
+ static struct nxp_fspi_devtype_data imx8ulp_data = {
+@@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = {
+       .quirks = 0,
+       .lut_num = 16,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode:
++       * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz.
++       * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR
++       * but is not the default use case.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 166000000,
++};
++
++static struct nxp_fspi_devtype_data imx95_data = {
++      .rxfifo = SZ_512,       /* (64  * 64 bits)  */
++      .txfifo = SZ_1K,        /* (128 * 64 bits)  */
++      .ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
++      .quirks = 0,
++      .lut_num = 32,
++      .little_endian = true,  /* little-endian    */
++      /*
++       * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz,
++       * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode).
++       */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 200000000,
+ };
+ struct nxp_fspi {
+@@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f,
+       reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+       if (op_is_dtr) {
+               reg |= FSPI_MCR0_RXCLKSRC(3);
+-              f->max_rate = 166000000;
++              /*
++               * Use the SoC-specific DTR max rate if provided, otherwise
++               * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9).
++               */
++              f->max_rate = f->devtype_data->max_dtr_rate ?
++                            f->devtype_data->max_dtr_rate : 166000000;
+       } else {        /*select mode 0 */
+               reg &= ~FSPI_MCR0_RXCLKSRC(3);
+-              f->max_rate = 66000000;
++              /*
++               * Use the SoC-specific SDR max rate if provided, otherwise
++               * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9).
++               */
++              f->max_rate = f->devtype_data->max_sdr_rate ?
++                            f->devtype_data->max_sdr_rate : 66000000;
+       }
+       fspi_writel(f, reg, f->iobase + FSPI_MCR0);
+ }
+@@ -1446,10 +1522,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = {
+ static const struct of_device_id nxp_fspi_dt_ids[] = {
+       { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
+       { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
+-      { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
++      { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, },
+       { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
+       { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
+       { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, },
++      { .compatible = "nxp,imx95-fspi",   .data = (void *)&imx95_data, },
+       { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
+-- 
+2.53.0
+
diff --git a/queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
new file mode 100644 (file)
index 0000000..51c7be0
--- /dev/null
@@ -0,0 +1,70 @@
+From e15976c4b381bb1ff44850bd2027ac8c7b5e9e49 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:50:00 +0900
+Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging
+ functions
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ]
+
+mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into
+tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map().
+If these functions are invoked while mmio_trace_array is NULL (e.g. before
+initialization or after disabled), accessing tr->array_buffer.buffer will
+result in a NULL pointer dereference crash.
+
+Fix this by adding an explicit NULL check for tr at the beginning of
+__trace_mmiotrace_rw() and __trace_mmiotrace_map().
+
+Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2
+Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index e16b15d7c84aa..32b7e1d972a47 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -294,11 +294,15 @@ device_initcall(init_mmio_trace);
+ static void __trace_mmiotrace_rw(struct trace_array *tr,
+                               struct mmiotrace_rw *rw)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_rw *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
+                                         sizeof(*entry), trace_ctx);
+@@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw)
+ static void __trace_mmiotrace_map(struct trace_array *tr,
+                               struct mmiotrace_map *map)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_map *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
+                                         sizeof(*entry), trace_ctx);
+-- 
+2.53.0
+
diff --git a/queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..a13c061
--- /dev/null
@@ -0,0 +1,43 @@
+From 1a6fa17180dc1c6fef68d5ed07fcdf40e414860a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index dc8a1eafbe574..e16b15d7c84aa 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..b14ff47
--- /dev/null
@@ -0,0 +1,52 @@
+From b40b87bee9c2ab1cdd77f70d9b37631b432b93fc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index 1f68df6e80670..7702cff84d546 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch b/queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch
new file mode 100644 (file)
index 0000000..3c0a06b
--- /dev/null
@@ -0,0 +1,54 @@
+From 0b77e35c536802d17ecf8a832c5072ebb9745796 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 20:08:04 -0300
+Subject: x86/boot: Add volatile, clobbers and zero-length test in memcmp()
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit a8c171c107c0b61a5e7e10cedab0fb72aeaf640d ]
+
+Add the volatile qualifier and clobbers parameter to prevent bugs with
+instruction reordering and optimization.
+
+Also add TEST for the zero-length case to set ZF, as, if the count register
+is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF
+flag undetermined.
+
+  [ bp: Add a comment about the len==0 case. ]
+
+Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code")
+Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com
+Suggested-by: Borislav Petkov <bp@alien8.de>
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/boot/string.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
+index b25c6a9303b73..3a2bba7c25e9d 100644
+--- a/arch/x86/boot/string.c
++++ b/arch/x86/boot/string.c
+@@ -32,8 +32,15 @@
+ int memcmp(const void *s1, const void *s2, size_t len)
+ {
+       bool diff;
+-      asm("repe cmpsb"
+-          : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len));
++
++      /*
++       * Make sure ZF is properly set in the len==0 case because in it,
++       * RCX==0 and the REPE; CMPSB won't get executed.
++       */
++      asm volatile("test %3, %3\n\t"
++                   "repe cmpsb"
++                   : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)
++                   : : "cc", "memory");
+       return diff;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch b/queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch
new file mode 100644 (file)
index 0000000..fe0610a
--- /dev/null
@@ -0,0 +1,144 @@
+From 151f8bb7e7ef893b15830d5e8e9fed4c947ae401 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:05 +0200
+Subject: xsk: drain continuation descs after overflow in xsk_build_skb()
+
+From: Jason Xing <kernelxing@tencent.com>
+
+[ Upstream commit bd44a6dcd4248883de90f5dad53ae80066e27096 ]
+
+Fix generic xmit path multi-buffer logic when packets are either too big
+(count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is
+included in fragmented packet. Introduce xdp_sock::drain_cont and act
+upon this flag - when it is set, keep on consuming descriptors from
+AF_XDP Tx ring and put them directly onto Cq. Previously these
+descriptors were silently lost and could never be reached again.
+
+Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
+Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/
+Reviewed-by: Jason Xing <kernelxing@tencent.com>
+Co-developed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com> # wrapped cq addr submission onto routine
+Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Signed-off-by: Jason Xing <kernelxing@tencent.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xdp_sock.h |  1 +
+ net/xdp/xsk.c          | 45 +++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
+index 7c2bc46c67050..8a0967af8f0c4 100644
+--- a/include/net/xdp_sock.h
++++ b/include/net/xdp_sock.h
+@@ -80,6 +80,7 @@ struct xdp_sock {
+        * call of __xsk_generic_xmit().
+        */
+       struct sk_buff *skb;
++      bool drain_cont;
+       struct list_head map_list;
+       /* Protects map_list */
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 72d83b671f844..70c57c5d8e1da 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -623,6 +623,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
+       spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
+ }
++static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool,
++                                           struct xdp_desc *desc)
++{
++      unsigned long flags;
++      u32 idx;
++
++      spin_lock_irqsave(&pool->cq_prod_lock, flags);
++      idx = xskq_get_prod(pool->cq);
++      xskq_prod_write_addr(pool->cq, idx, desc->addr);
++      xskq_prod_submit_n(pool->cq, 1);
++      spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
++}
++
+ static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n)
+ {
+       spin_lock(&pool->cq_cached_prod_lock);
+@@ -913,13 +926,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+ static int __xsk_generic_xmit(struct sock *sk)
+ {
+       struct xdp_sock *xs = xdp_sk(sk);
+-      bool sent_frame = false;
+       struct xdp_desc desc;
+       struct sk_buff *skb;
++      u32 cached_cons;
+       u32 max_batch;
+       int err = 0;
+       mutex_lock(&xs->mutex);
++      cached_cons = xs->tx->cached_cons;
+       /* Since we dropped the RCU read lock, the socket state might have changed. */
+       if (unlikely(!xsk_is_bound(xs))) {
+@@ -948,11 +962,21 @@ static int __xsk_generic_xmit(struct sock *sk)
+                       goto out;
+               }
++              if (unlikely(xs->drain_cont)) {
++                      xsk_cq_submit_addr_single_locked(xs->pool, &desc);
++                      xs->tx->invalid_descs++;
++                      xskq_cons_release(xs->tx);
++                      xs->drain_cont = xp_mb_desc(&desc);
++                      continue;
++              }
++
+               skb = xsk_build_skb(xs, &desc);
+               if (IS_ERR(skb)) {
+                       err = PTR_ERR(skb);
+                       if (err != -EOVERFLOW)
+                               goto out;
++                      if (xp_mb_desc(&desc))
++                              xs->drain_cont = true;
+                       err = 0;
+                       continue;
+               }
+@@ -981,18 +1005,33 @@ static int __xsk_generic_xmit(struct sock *sk)
+                       goto out;
+               }
+-              sent_frame = true;
+               xs->skb = NULL;
+       }
+       if (xskq_has_descs(xs->tx)) {
++              bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc);
++
++              err = xsk_cq_reserve_locked(xs->pool);
++              if (err) {
++                      xs->tx->invalid_descs--;
++                      if (xs->skb)
++                              xsk_drop_skb(xs->skb);
++                      xs->drain_cont = drain;
++                      err = -EAGAIN;
++                      goto out;
++              }
++
+               if (xs->skb)
+                       xsk_drop_skb(xs->skb);
++
++              xsk_cq_submit_addr_single_locked(xs->pool, &desc);
++
+               xskq_cons_release(xs->tx);
++              xs->drain_cont = xp_mb_desc(&desc);
+       }
+ out:
+-      if (sent_frame)
++      if (xs->tx->cached_cons != cached_cons)
+               __xsk_tx_release(xs);
+       mutex_unlock(&xs->mutex);
+-- 
+2.53.0
+
diff --git a/queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch b/queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch
new file mode 100644 (file)
index 0000000..f4f1982
--- /dev/null
@@ -0,0 +1,106 @@
+From 60b806e3ba83daad331ac31bc6f2db88c211accb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:04 +0200
+Subject: xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx
+
+From: Jason Xing <kernelxing@tencent.com>
+
+[ Upstream commit a3c8382ebce4780c6b3ace2c09bc342313ac0186 ]
+
+This patch is inspired by the check[1] from sashiko. It says when
+overflow happens, the address of cq to be published is invalid.
+Actually the severer thing is the whole process of publishing the
+address of cq in this particular case is not right: it should truely
+publish the address and advance the cached_prod in cq as long as it
+reads descriptors from txq.
+
+The following is the full analysis.
+xsk_drop_skb() is called in three places, which all discard a partially
+built multi-buffer skb:
+1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS
+2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in
+   the TX ring prevents the partial packet from completing
+3) xsk_release(): socket close while xs->skb holds an incomplete packet
+
+In all three cases, the TX descriptors for the already-processed frags
+have been consumed from the TX ring (xskq_cons_release), and CQ slots
+have been reserved. However, xsk_drop_skb() calls xsk_consume_skb()
+which cancels the CQ reservations via xsk_cq_cancel_locked(). Since
+the buffer addresses never appear in the completion queue, userspace
+permanently loses track of these buffers.
+
+Fix this by letting consume_skb() trigger the existing xsk_destruct_skb
+destructor, which already submits buffer addresses to the CQ via
+xsk_cq_submit_addr_locked().
+
+Note that cancelling the descriptors back to the TX ring (via
+xskq_cons_cancel_n) is not a appropriate option because an oversized
+packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely,
+which is an obviously deadlock bug in the TX path.
+
+Also move the desc->addr assignment in xsk_build_skb() above the
+overflow check so that the current descriptor's address is recorded
+before a potential -EOVERFLOW jump to free_err, consistent with the
+zerocopy path in xsk_build_skb_zerocopy().
+
+[1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/
+
+Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
+Acked-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Signed-off-by: Jason Xing <kernelxing@tencent.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xdp/xsk.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 9e0a486d54fb3..024e23c08b8db 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -676,8 +676,11 @@ static void xsk_consume_skb(struct sk_buff *skb)
+ static void xsk_drop_skb(struct sk_buff *skb)
+ {
+-      xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb);
+-      xsk_consume_skb(skb);
++      struct xdp_sock *xs = xdp_sk(skb->sk);
++
++      xs->tx->invalid_descs += xsk_get_num_desc(skb);
++      consume_skb(skb);
++      xs->skb = NULL;
+ }
+ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
+@@ -768,7 +771,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
+               }
+               /* in case of -EOVERFLOW that could happen below,
+-               * xsk_consume_skb() will release this node as whole skb
++               * xsk_drop_skb() will release this node as whole skb
+                * would be dropped, which implies freeing all list elements
+                */
+               xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
+@@ -867,6 +870,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+                               xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg;
+                       }
++                      xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
++
+                       if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) {
+                               err = -EOVERFLOW;
+                               goto free_err;
+@@ -884,8 +889,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+                       skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE);
+                       refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc);
+-
+-                      xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
+               }
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch b/queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch
new file mode 100644 (file)
index 0000000..17fc36b
--- /dev/null
@@ -0,0 +1,140 @@
+From 97d412bcd0e47617f99fa79c6da94ce616b18c02 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Oct 2025 08:06:46 +0800
+Subject: xsk: use a smaller new lock for shared pool case
+
+From: Jason Xing <kernelxing@tencent.com>
+
+[ Upstream commit 30ed05adca4a05c50594384cff18910858dd1d35 ]
+
+- Split cq_lock into two smaller locks: cq_prod_lock and
+  cq_cached_prod_lock
+- Avoid disabling/enabling interrupts in the hot xmit path
+
+In either xsk_cq_cancel_locked() or xsk_cq_reserve_locked() function,
+the race condition is only between multiple xsks sharing the same
+pool. They are all in the process context rather than interrupt context,
+so now the small lock named cq_cached_prod_lock can be used without
+handling interrupts.
+
+While cq_cached_prod_lock ensures the exclusive modification of
+@cached_prod, cq_prod_lock in xsk_cq_submit_addr_locked() only cares
+about @producer and corresponding @desc. Both of them don't necessarily
+be consistent with @cached_prod protected by cq_cached_prod_lock.
+That's the reason why the previous big lock can be split into two
+smaller ones. Please note that SPSC rule is all about the global state
+of producer and consumer that can affect both layers instead of local
+or cached ones.
+
+Frequently disabling and enabling interrupt are very time consuming
+in some cases, especially in a per-descriptor granularity, which now
+can be avoided after this optimization, even when the pool is shared by
+multiple xsks.
+
+With this patch, the performance number[1] could go from 1,872,565 pps
+to 1,961,009 pps. It's a minor rise of around 5%.
+
+[1]: taskset -c 1 ./xdpsock -i enp2s0f1 -q 0 -t -S -s 64
+
+Signed-off-by: Jason Xing <kernelxing@tencent.com>
+Acked-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Link: https://patch.msgid.link/20251030000646.18859-3-kerneljasonxing@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Stable-dep-of: bd44a6dcd424 ("xsk: drain continuation descs after overflow in xsk_build_skb()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xsk_buff_pool.h | 13 +++++++++----
+ net/xdp/xsk.c               | 15 ++++++---------
+ net/xdp/xsk_buff_pool.c     |  3 ++-
+ 3 files changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h
+index cac56e6b0869b..92a2358c6ce34 100644
+--- a/include/net/xsk_buff_pool.h
++++ b/include/net/xsk_buff_pool.h
+@@ -85,11 +85,16 @@ struct xsk_buff_pool {
+       bool unaligned;
+       bool tx_sw_csum;
+       void *addrs;
+-      /* Mutual exclusion of the completion ring in the SKB mode. Two cases to protect:
+-       * NAPI TX thread and sendmsg error paths in the SKB destructor callback and when
+-       * sockets share a single cq when the same netdev and queue id is shared.
++      /* Mutual exclusion of the completion ring in the SKB mode.
++       * Protect: NAPI TX thread and sendmsg error paths in the SKB
++       * destructor callback.
+        */
+-      spinlock_t cq_lock;
++      spinlock_t cq_prod_lock;
++      /* Mutual exclusion of the completion ring in the SKB mode.
++       * Protect: when sockets share a single cq when the same netdev
++       * and queue id is shared.
++       */
++      spinlock_t cq_cached_prod_lock;
+       struct xdp_buff_xsk *free_heads[];
+ };
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 024e23c08b8db..72d83b671f844 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -547,12 +547,11 @@ static int xsk_wakeup(struct xdp_sock *xs, u8 flags)
+ static int xsk_cq_reserve_locked(struct xsk_buff_pool *pool)
+ {
+-      unsigned long flags;
+       int ret;
+-      spin_lock_irqsave(&pool->cq_lock, flags);
++      spin_lock(&pool->cq_cached_prod_lock);
+       ret = xskq_prod_reserve(pool->cq);
+-      spin_unlock_irqrestore(&pool->cq_lock, flags);
++      spin_unlock(&pool->cq_cached_prod_lock);
+       return ret;
+ }
+@@ -603,7 +602,7 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
+       unsigned long flags;
+       u32 idx, i;
+-      spin_lock_irqsave(&pool->cq_lock, flags);
++      spin_lock_irqsave(&pool->cq_prod_lock, flags);
+       idx = xskq_get_prod(pool->cq);
+       if (unlikely(num_descs > 1)) {
+@@ -621,16 +620,14 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
+               descs_processed++;
+       }
+       xskq_prod_submit_n(pool->cq, descs_processed);
+-      spin_unlock_irqrestore(&pool->cq_lock, flags);
++      spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
+ }
+ static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n)
+ {
+-      unsigned long flags;
+-
+-      spin_lock_irqsave(&pool->cq_lock, flags);
++      spin_lock(&pool->cq_cached_prod_lock);
+       xskq_prod_cancel_n(pool->cq, n);
+-      spin_unlock_irqrestore(&pool->cq_lock, flags);
++      spin_unlock(&pool->cq_cached_prod_lock);
+ }
+ static void xsk_destruct_skb(struct sk_buff *skb)
+diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
+index a129ce6f1c25f..c9688735b643c 100644
+--- a/net/xdp/xsk_buff_pool.c
++++ b/net/xdp/xsk_buff_pool.c
+@@ -96,7 +96,8 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
+       INIT_LIST_HEAD(&pool->xskb_list);
+       INIT_LIST_HEAD(&pool->xsk_tx_list);
+       spin_lock_init(&pool->xsk_tx_list_lock);
+-      spin_lock_init(&pool->cq_lock);
++      spin_lock_init(&pool->cq_prod_lock);
++      spin_lock_init(&pool->cq_cached_prod_lock);
+       refcount_set(&pool->users, 1);
+       pool->fq = xs->fq_tmp;
+-- 
+2.53.0
+
diff --git a/queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
new file mode 100644 (file)
index 0000000..1eae6d0
--- /dev/null
@@ -0,0 +1,49 @@
+From e303128a63a05229222959e84ca65a3b46047b52 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 02:25:20 +0500
+Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ]
+
+In encode_message() the per-transaction lower-bound check compares
+trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer,
+instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr.
+
+Every other length check in this file (encode_message() at the loop
+guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so
+this is an inconsistency. On 64-bit builds the pointer and the struct
+are both 8 bytes, so the check is correct by coincidence and there is
+no behavioural change. On 32-bit builds the pointer is 4 bytes, which
+weakens the minimum-length check below the 8-byte header size.
+
+Use sizeof(*trans_hdr) so the check validates against the actual
+transaction header size on all builds.
+
+Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()")
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accel/qaic/qaic_control.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
+index de8b17e2b29e1..e59acc39c3fa5 100644
+--- a/drivers/accel/qaic/qaic_control.c
++++ b/drivers/accel/qaic/qaic_control.c
+@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+                       break;
+               }
+               trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
+-              if (trans_hdr->len < sizeof(trans_hdr) ||
++              if (trans_hdr->len < sizeof(*trans_hdr) ||
+                   size_add(user_len, trans_hdr->len) > user_msg->len) {
+                       ret = -EINVAL;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch
new file mode 100644 (file)
index 0000000..ab66ff3
--- /dev/null
@@ -0,0 +1,135 @@
+From 020404ef0cef2393357e64e59ce97f0a0d519ae8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jan 2025 14:14:47 +0900
+Subject: ahci: Introduce ahci_ignore_port() helper
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ]
+
+libahci and AHCI drivers may ignore some ports if the port is invalid
+(its ID does not correspond to a valid physical port) or if the user
+explicitly requested the port to be ignored with the mask_port_map
+ahci module parameter. Such port that shall be ignored can be identified
+by checking that the bit corresponding to the port ID is not set in the
+mask_port_map field of struct ahci_host_priv. E.g. code such as:
+"if (!(hpriv->mask_port_map & (1 << portid)))".
+
+Replace all direct use of the mask_port_map field to detect such port
+with the new helper inline function ahci_ignore_port() to make the code
+more readable/easier to understand.
+
+The comment describing the mask_port_map field of struct ahci_host_priv
+is also updated to be more accurate.
+
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Niklas Cassel <cassel@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci.h             | 13 ++++++++++++-
+ drivers/ata/ahci_brcm.c        |  2 +-
+ drivers/ata/ahci_ceva.c        |  4 ++--
+ drivers/ata/libahci_platform.c |  6 +++---
+ 4 files changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
+index df8f8a1a3a34c..ef791ab8d9d11 100644
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -330,7 +330,7 @@ struct ahci_port_priv {
+ struct ahci_host_priv {
+       /* Input fields */
+       unsigned int            flags;          /* AHCI_HFLAG_* */
+-      u32                     mask_port_map;  /* mask out particular bits */
++      u32                     mask_port_map;  /* Mask of valid ports */
+       void __iomem *          mmio;           /* bus-independent mem map */
+       u32                     cap;            /* cap to use */
+@@ -381,6 +381,17 @@ struct ahci_host_priv {
+                                                 int port);
+ };
++/*
++ * Return true if a port should be ignored because it is excluded from
++ * the host port map.
++ */
++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv,
++                                  unsigned int portid)
++{
++      return portid >= hpriv->nports ||
++              !(hpriv->mask_port_map & (1 << portid));
++}
++
+ extern int ahci_ignore_sss;
+ extern const struct attribute_group *ahci_shost_groups[];
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index 0411bad231475..481aab4d9b96f 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 6e115da23d3f0..93275b1b48898 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_power_on(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index 2174a3e6e9574..bfb103ca610dd 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -49,7 +49,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               rc = phy_init(hpriv->phys[i]);
+@@ -73,7 +73,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+@@ -94,7 +94,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
+-              if (!(hpriv->mask_port_map & (1 << i)))
++              if (ahci_ignore_port(hpriv, i))
+                       continue;
+               phy_power_off(hpriv->phys[i]);
+-- 
+2.53.0
+
diff --git a/queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..a01e8f5
--- /dev/null
@@ -0,0 +1,61 @@
+From 7dc8420bef992376983b564a243fe97b60f2936a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index 4023b88e7bc13..9253707bccde9 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2388,8 +2388,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..bccd663
--- /dev/null
@@ -0,0 +1,61 @@
+From 01d7b44388959bf1e15cdcb0680cc3ace21fa593 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index 7e525d49328d2..3b37f4c4235e6 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..4993fb3
--- /dev/null
@@ -0,0 +1,66 @@
+From a16964aa125016e60dfdd822d48b5713aa678e3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index ca0b4f360c1a0..65409f0d2e0e5 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
new file mode 100644 (file)
index 0000000..3e3f21c
--- /dev/null
@@ -0,0 +1,76 @@
+From 41c03d77ca79d75abab06d59392fb01bf1ad648d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 23:55:26 +0530
+Subject: ata: ahci_ceva: fix error paths in
+ ceva_ahci_platform_enable_resources()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
+
+On phy_init() failure the error path fallsthrough to disable_rsts, which
+deasserts the controller reset and then enters disable_phys calling
+phy_power_off() on PHYs that were never powered on. That corrupts the PHY
+power_count and triggers an extra runtime PM put.
+
+Use a separate exit_phys path that unwinds with phy_exit() only and falls
+through to disable_clks while the controller remains in reset.  Reserve
+phy_power_off() for the phy_power_on() failure path only, and skip
+masked-out ports in both unwind loops.
+
+On phy_power_on() failure re-assert the controller reset before disabling
+clocks and regulators, matching the teardown order used by
+ahci_platform_enable_resources() and ahci_platform_disable_resources().
+
+Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_ceva.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 93275b1b48898..b2918ad8e7e05 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+-                      goto disable_rsts;
++                      goto exit_phys;
+       }
+       /* De-assert the controller reset */
+@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       return 0;
+-disable_rsts:
+-      ahci_platform_deassert_rsts(hpriv);
+-
+ disable_phys:
+       while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
++      ahci_platform_assert_rsts(hpriv);
++      goto disable_clks;
++
++exit_phys:
++      while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
++              phy_exit(hpriv->phys[i]);
++      }
+ disable_clks:
+       ahci_platform_disable_clks(hpriv);
+-- 
+2.53.0
+
diff --git a/queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch
new file mode 100644 (file)
index 0000000..ea92c3d
--- /dev/null
@@ -0,0 +1,178 @@
+From 6d5e9f4c9e355bd9e8d066eefcd6f5ffb2543f2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jan 2025 13:13:33 +0100
+Subject: ata: libahci_platform: support non-consecutive port numbers
+
+From: Josua Mayer <josua@solid-run.com>
+
+[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ]
+
+So far ahci_platform relied on number of child nodes in firmware to
+allocate arrays and expected port numbers to start from 0 without holes.
+This number of ports is then set in private structure for use when
+configuring phys and regulators.
+
+Some platforms may not use every port of an ahci controller.
+E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading
+to the following errors during boot:
+[    1.719476] ahci f2540000.sata: invalid port number 1
+[    1.724562] ahci f2540000.sata: No port enabled
+
+Update all accessesors of ahci_host_priv phys and target_pwrs arrays to
+support holes. Access is gated by hpriv->mask_port_map which has a bit
+set for each enabled port.
+
+Update ahci_platform_get_resources to ignore holes in the port numbers
+and enable ports defined in firmware by their reg property only.
+
+When firmware does not define children it is assumed that there is
+exactly one port, using index 0.
+
+Signed-off-by: Josua Mayer <josua@solid-run.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_brcm.c        |  3 +++
+ drivers/ata/ahci_ceva.c        |  6 +++++
+ drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++-----
+ 3 files changed, 43 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
+index 70c3a33eee6f2..0411bad231475 100644
+--- a/drivers/ata/ahci_brcm.c
++++ b/drivers/ata/ahci_brcm.c
+@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
+       /* Re-initialize and calibrate the PHY */
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 11a2c199a7c24..6e115da23d3f0 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               goto disable_clks;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_rsts;
+@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       ahci_platform_deassert_rsts(hpriv);
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_power_on(hpriv->phys[i]);
+               if (rc) {
+                       phy_exit(hpriv->phys[i]);
+diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
+index 581704e61f286..2174a3e6e9574 100644
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -49,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+       int rc, i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+                       goto disable_phys;
+@@ -70,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+ disable_phys:
+       while (--i >= 0) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -88,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+       int i;
+       for (i = 0; i < hpriv->nports; i++) {
++              if (!(hpriv->mask_port_map & (1 << i)))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
+@@ -435,6 +444,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,
+       return 0;
+ }
++static u32 ahci_platform_find_max_port_id(struct device *dev)
++{
++      u32 max_port = 0;
++
++      for_each_child_of_node_scoped(dev->of_node, child) {
++              u32 port;
++
++              if (!of_property_read_u32(child, "reg", &port))
++                      max_port = max(max_port, port);
++      }
++
++      return max_port;
++}
++
+ /**
+  * ahci_platform_get_resources - Get platform resources
+  * @pdev: platform device to get resources for
+@@ -462,6 +485,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+       struct ahci_host_priv *hpriv;
+       struct device_node *child;
+       u32 mask_port_map = 0;
++      u32 max_port;
+       if (!devres_open_group(dev, NULL, GFP_KERNEL))
+               return ERR_PTR(-ENOMEM);
+@@ -553,15 +577,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+               goto err_out;
+       }
++      /* find maximum port id for allocating structures */
++      max_port = ahci_platform_find_max_port_id(dev);
+       /*
+-       * If no sub-node was found, we still need to set nports to
+-       * one in order to be able to use the
++       * Set nports according to maximum port id. Clamp at
++       * AHCI_MAX_PORTS, warning message for invalid port id
++       * is generated later.
++       * When DT has no sub-nodes max_port is 0, nports is 1,
++       * in order to be able to use the
+        * ahci_platform_[en|dis]able_[phys|regulators] functions.
+        */
+-      if (child_nodes)
+-              hpriv->nports = child_nodes;
+-      else
+-              hpriv->nports = 1;
++      hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1);
+       hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL);
+       if (!hpriv->phys) {
+@@ -634,6 +660,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
+                * If no sub-node was found, keep this for device tree
+                * compatibility
+                */
++              hpriv->mask_port_map |= BIT(0);
++
+               rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node);
+               if (rc)
+                       goto err_out;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
new file mode 100644 (file)
index 0000000..8809de4
--- /dev/null
@@ -0,0 +1,44 @@
+From 3633682b60e01fbc5549b6e99687e6e3a7cb82c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 15:31:37 -0700
+Subject: ata: sata_mv: accept 1 or 2 resources in platform probe
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
+
+Board files in arch/arm/plat-orion, arch/arm/mach-dove,
+arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the
+"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ).
+Those devices are rejected with -EINVAL, so SATA no longer probes on
+legacy Marvell Orion/Kirkwood-style boards.
+
+Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2
+resources (legacy, IRQ supplied as a second resource) so both probing
+paths work.
+
+Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone")
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/sata_mv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
+index 80a45e11fb5b6..8700bc2fdade8 100644
+--- a/drivers/ata/sata_mv.c
++++ b/drivers/ata/sata_mv.c
+@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
+       /*
+        * Simple resource validation ..
+        */
+-      if (unlikely(pdev->num_resources != 1)) {
++      if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
+               dev_err(&pdev->dev, "invalid number of resources\n");
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch
new file mode 100644 (file)
index 0000000..80a7a0a
--- /dev/null
@@ -0,0 +1,40 @@
+From 20fd548bd4024dfbe4ea532a6f898a95b724098c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 01:54:40 -0700
+Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV
+
+From: Zijun Hu <zijun.hu@oss.qualcomm.com>
+
+[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ]
+
+btintel_diagnostics() accesses tlv->val[0] without first validating
+that the diagnostics VSE is long enough to contain that field, so
+may cause reading data beyond the received frame.
+
+Fix by validating the length before access.
+
+Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support")
+Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bluetooth/btintel.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
+index 25fcd00c6a174..4784da07dd8a1 100644
+--- a/drivers/bluetooth/btintel.c
++++ b/drivers/bluetooth/btintel.c
+@@ -2922,6 +2922,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+       struct intel_tlv *tlv = (void *)&skb->data[5];
++      if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
++              goto recv_frame;
++
+       /* The first event is always an event type TLV */
+       if (tlv->type != INTEL_TLV_TYPE_ID)
+               goto recv_frame;
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
new file mode 100644 (file)
index 0000000..2584e25
--- /dev/null
@@ -0,0 +1,54 @@
+From cee5b7df719747e459fc919044ed844e9fad1776 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:17 +0300
+Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that.
+
+Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index 7625461dff7ae..e4364a64ffd57 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2896,6 +2896,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+       return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
+ }
++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
++{
++      struct hci_conn *conn = data;
++
++      hci_conn_put(conn);
++}
++
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
+@@ -2921,6 +2928,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
++                                  abort_conn_destroy);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
new file mode 100644 (file)
index 0000000..68f450f
--- /dev/null
@@ -0,0 +1,43 @@
+From 7a8dfa508bb97798acfa63f4d55353d18121e819 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:22 +0300
+Subject: Bluetooth: hci_sync: fix hci_conn_del() use in
+ hci_le_create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ]
+
+hci_conn_del() caller must hold hdev->lock, check the conn was not
+concurrently deleted, and usually inform socket the conn is going to be
+deleted.
+
+Use hci_abort_conn_sync() instead of calling hci_conn_del() without
+locks etc.
+
+Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index df207c3f1c5c3..6a5ec74eaf25a 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6582,7 +6582,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE &&
+                   !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
+-                      hci_conn_del(conn);
++                      conn->state = BT_OPEN;
++                      hci_abort_conn_sync(hdev, conn,
++                                          HCI_ERROR_REJ_LIMITED_RESOURCES);
+                       hci_conn_put(conn);
+                       return -EBUSY;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
new file mode 100644 (file)
index 0000000..bd4f2e9
--- /dev/null
@@ -0,0 +1,64 @@
+From c0abf50a5452f2f358fb287c1590da4b615af584 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 25 Mar 2026 21:07:45 +0200
+Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if
+ exists
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ]
+
+hci_cmd_sync_run_once() needs to indicate whether a queue item was
+added, so caller can know if callbacks are called, so it can avoid
+leaking resources.
+
+Change the function to return -EEXIST if queue item already exists.
+
+Modify all callsites vs. the changes.  The only callsite is
+hci_abort_conn().
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 4 +++-
+ net/bluetooth/hci_sync.c | 2 +-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index 19ca5016372ac..7625461dff7ae 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -2899,6 +2899,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
++      int err;
+       /* If abort_reason has already been set it means the connection is
+        * already being aborted so don't attempt to overwrite it.
+@@ -2920,5 +2921,6 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      return (err == -EEXIST) ? 0 : err;
+ }
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 4b1fcea37941f..df207c3f1c5c3 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -849,7 +849,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                         void *data, hci_cmd_sync_work_destroy_t destroy)
+ {
+       if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
+-              return 0;
++              return -EEXIST;
+       return hci_cmd_sync_run(hdev, func, data, destroy);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
new file mode 100644 (file)
index 0000000..2091373
--- /dev/null
@@ -0,0 +1,80 @@
+From 3d33c799bf91a0c2937cfcb3b384c76745a9159c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:23 +0300
+Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in
+ create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ]
+
+hci_conn_get() without already held reference is data race against
+concurrent deletion.
+
+In previous patches, the refcount has been changed to be taken before
+starting the hci_sync task, so remove these extra get() + put() as they
+are not needed.
+
+Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 6a5ec74eaf25a..9dec36467fead 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6566,11 +6566,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       bt_dev_dbg(hdev, "conn %p", conn);
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() at done.
+-       */
+-      hci_conn_get(conn);
+-
+       clear_bit(HCI_CONN_SCANNING, &conn->flags);
+       conn->state = BT_CONNECT;
+@@ -6585,7 +6580,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+                       conn->state = BT_OPEN;
+                       hci_abort_conn_sync(hdev, conn,
+                                           HCI_ERROR_REJ_LIMITED_RESOURCES);
+-                      hci_conn_put(conn);
+                       return -EBUSY;
+               }
+@@ -6679,7 +6673,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       /* Re-enable advertising after the connection attempt is finished. */
+       hci_resume_advertising_sync(hdev);
+-      hci_conn_put(conn);
+       return err;
+ }
+@@ -6958,11 +6951,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+       else
+               cp.role_switch = 0x00;
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() below.
+-       */
+-      hci_conn_get(conn);
+-
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+@@ -6974,7 +6962,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+                                      conn->conn_timeout, NULL);
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+-      hci_conn_put(conn);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
new file mode 100644 (file)
index 0000000..08edadc
--- /dev/null
@@ -0,0 +1,40 @@
+From db8ac8c0eebfb786bd49cff126637cd2295982b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 17:53:33 +0300
+Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ]
+
+When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is
+necessary, otherwise later iso_conn_free() will UAF.
+
+Fix clearing of iso_data in iso_sock_disconn()
+
+Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on
+iso_sock_release() followed by hci_abort_conn_sync().
+
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 189b719669924..51d0065f058c1 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -767,6 +767,7 @@ static void iso_sock_disconn(struct sock *sk)
+       iso_sock_set_timer(sk, ISO_DISCONN_TIMEOUT);
+       iso_conn_lock(iso_pi(sk)->conn);
+       hci_conn_drop(iso_pi(sk)->conn->hcon);
++      iso_pi(sk)->conn->hcon->iso_data = NULL;
+       iso_pi(sk)->conn->hcon = NULL;
+       iso_conn_unlock(iso_pi(sk)->conn);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
new file mode 100644 (file)
index 0000000..1d04628
--- /dev/null
@@ -0,0 +1,38 @@
+From ed11b1deae5a73cc00a26663dd0bcbe373a3dd36 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:27 +0300
+Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ]
+
+In iso.c check_bcast_qos(), missing bcast.timeout is not set to its
+default value, and appears typoed as bcast.sync_timeout.
+
+Fix the typo.
+
+Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 51d0065f058c1..a67bce151aaff 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1439,7 +1439,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
+               return false;
+       if (!qos->bcast.timeout)
+-              qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
++              qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
+       if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..73aa7b2
--- /dev/null
@@ -0,0 +1,60 @@
+From 117e5f41e9f3f83cf7eb9c4e285c145e5f78b2c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 792974eef47c0..9643a8aacb96b 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -4779,6 +4779,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       if (!chan)
+               return -EBADSLT;
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -4824,6 +4828,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
new file mode 100644 (file)
index 0000000..3bc53fe
--- /dev/null
@@ -0,0 +1,77 @@
+From 8b0763779df5af54d34312f51e3589b567165f7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:40 +0200
+Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction
+ commit
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ]
+
+When writing out metadata extent buffers in a zoned filesystem,
+btree_writepages() holds fs_info->zoned_meta_io_lock across the whole
+writeback loop, including the call to btrfs_check_meta_write_pointer() ->
+check_bg_is_active().
+
+For the tree-log block group, check_bg_is_active() may fail to activate
+the zone and fall back to btrfs_zone_finish_one_bg() to free an active
+zone. That path waits for the running transaction to commit while still
+holding zoned_meta_io_lock, but the committer needs that same lock to
+write out the tree extents, so the two tasks deadlock:
+
+  Task A (kworker, metadata writeback)      Task B (fsstress, transaction commit)
+  ------------------------------------      -------------------------------------
+  wb_workfn()                               btrfs_commit_transaction(T)
+   btree_writepages()                        btrfs_write_and_wait_transaction()
+    btrfs_zoned_meta_io_lock()                btrfs_write_marked_extents()
+    btrfs_check_meta_write_pointer()           btree_writepages()
+     check_bg_is_active() [treelog_bg]          btrfs_zoned_meta_io_lock()
+      btrfs_zone_finish_one_bg()               <blocks on zoned_meta_io_lock,
+       btrfs_zone_finish()                      held by Task A>
+        do_zone_finish()
+         btrfs_inc_block_group_ro()
+          btrfs_wait_for_commit()
+           <blocks waiting for commit
+            of transaction T, done by
+            Task B>
+
+The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock
+around do_zone_finish() for this exact reason. Do the same in the tree-log
+branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire
+it afterwards. The lock only protects fs_info->active_{meta,system}_bg,
+which this branch does not touch, and ctx->zoned_bg keeps a reference to
+the block group across the unlock, so nothing is lost while the lock
+is dropped.
+
+This hang occasionally reproduces with fstests generic/475 on a zoned
+btrfs filesystem.
+
+Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index dabbfc3b7d776..9962ae292b128 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -1775,7 +1775,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx,
+       if (fs_info->treelog_bg == block_group->start) {
+               if (!btrfs_zone_activate(block_group)) {
+-                      int ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      int ret_fin;
++
++                      btrfs_zoned_meta_io_unlock(fs_info);
++                      ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      btrfs_zoned_meta_io_lock(fs_info);
+                       if (ret_fin != 1 || !btrfs_zone_activate(block_group))
+                               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..a1572d0
--- /dev/null
@@ -0,0 +1,57 @@
+From fb80dcd526d3eed4c6890dd7d5d05a413ac51504 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index efc5eeac7c886..062b7bbf1b098 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1883,13 +1883,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
new file mode 100644 (file)
index 0000000..5a89e28
--- /dev/null
@@ -0,0 +1,65 @@
+From 5533a5654a25ef9b1932837db1f355164faabf99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 May 2026 10:15:50 -0400
+Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open()
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ]
+
+The failed_dev_add and failed_dev_name paths drop the file-device
+reference while wq->wq_lock is still held. If put_device(fdev) drops the
+last reference, idxd_file_dev_release() runs synchronously and tries to
+take wq->wq_lock again, deadlocking.
+
+Those paths also fall through into the later ctx cleanup labels even
+though idxd_file_dev_release() owns that cleanup and frees ctx. This can
+make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context.
+
+Move idxd_wq_get() before file-device setup can fail, since the release
+callback always calls idxd_wq_put(). Then unlock wq->wq_lock before
+put_device(fdev) and return directly from the file-device setup failure
+path, leaving ctx cleanup to the release callback.
+
+Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/idxd/cdev.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
+index 5ded4a0887bc8..e885d5af485b2 100644
+--- a/drivers/dma/idxd/cdev.c
++++ b/drivers/dma/idxd/cdev.c
+@@ -293,6 +293,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+       fdev->parent = cdev_dev(idxd_cdev);
+       fdev->bus = &dsa_bus_type;
+       fdev->type = &idxd_cdev_file_type;
++      idxd_wq_get(wq);
+       rc = dev_set_name(fdev, "file%d", ctx->id);
+       if (rc < 0) {
+@@ -306,13 +307,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+               goto failed_dev_add;
+       }
+-      idxd_wq_get(wq);
+       mutex_unlock(&wq->wq_lock);
+       return 0;
+ failed_dev_add:
+ failed_dev_name:
++      mutex_unlock(&wq->wq_lock);
+       put_device(fdev);
++      return rc;
+ failed_ida:
+ failed_set_pasid:
+       if (device_user_pasid_enabled(idxd))
+-- 
+2.53.0
+
diff --git a/queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..4e77072
--- /dev/null
@@ -0,0 +1,60 @@
+From 8d45d1a2e5401bb9a6f2ea98c7fcfc3d536e0c68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 6a384bd469528..f1d5af20363e9 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -934,16 +934,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..0206406
--- /dev/null
@@ -0,0 +1,51 @@
+From 78be7ac2127f21a78ee5a4322856fa8d5b2d98d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+index 659112da47b69..859dffe451372 100644
+--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+@@ -185,10 +185,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+-- 
+2.53.0
+
diff --git a/queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..0f172af
--- /dev/null
@@ -0,0 +1,47 @@
+From 41f1c155659740af4169e5862cba532b3c773310 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index 7a549b834e970..0e9afeb070ba1 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..2a8677f
--- /dev/null
@@ -0,0 +1,51 @@
+From 0eb162fce060e96fc6bc0a324f3f7acc1fca548b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 3d4b4b6bd47fd..06d60f76cabbd 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..03f9dbb
--- /dev/null
@@ -0,0 +1,83 @@
+From 6f6fd689ec260a24bcbde113fc94acb5f48c327d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 0e0d2236dc536..3d4b4b6bd47fd 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..6782d3a
--- /dev/null
@@ -0,0 +1,80 @@
+From 187f348030c9fd91015fdd52e5e6a45f7ad2e7a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 0ec0f1e4dcbc9..7b9cb44749d95 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..987234c
--- /dev/null
@@ -0,0 +1,115 @@
+From e8e3ff132e8ebb3f2b5fdd49ef3ff7a0677db9bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index c4b3a4a18670c..0e0d2236dc536 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..f0dc6fe
--- /dev/null
@@ -0,0 +1,57 @@
+From 530af8388256bccec097091207b04260ef11e6a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 7b9cb44749d95..3ced54988f549 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..229f6d8
--- /dev/null
@@ -0,0 +1,62 @@
+From 1ea0f1e6ba308752077742be6223781f14ffb5ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index b63e3a0f6c135..76f7efae54e07 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..4f6b494
--- /dev/null
@@ -0,0 +1,71 @@
+From 934a7c9959731da6f687f20b247c65d6201e2452 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 06d60f76cabbd..b63e3a0f6c135 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..03c0916
--- /dev/null
@@ -0,0 +1,111 @@
+From c7f278a739940757df68f7a6638aec10c3601c4e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 76f7efae54e07..0ec0f1e4dcbc9 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
new file mode 100644 (file)
index 0000000..7b2b0e5
--- /dev/null
@@ -0,0 +1,54 @@
+From 278ba8f3d6dbe09eca15240515e6937021d5ba9d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 15:27:28 -0700
+Subject: hwmon: (lm90) Only report alarms if driver is ready
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
+
+Userspace can read sysfs attributes before driver registration is complete,
+immediately after devm_hwmon_device_register_with_info() has been called.
+At that time, data->hwmon_dev is not yet initialized. This can trigger
+a NULL pointer access since lm90_update_device() and with it
+lm90_update_alarms_locked() will be called. This call schedules
+report_work and lm90_report_alarms(), which passes the still-NULL
+data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer
+dereference.
+
+Fix the problem by only scheduling the report and alert workers
+data->hwmon_dev is set.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/lm90.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
+index e0d7454a301cf..7be9874b3dfb1 100644
+--- a/drivers/hwmon/lm90.c
++++ b/drivers/hwmon/lm90.c
+@@ -1149,7 +1149,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+               check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+                       (data->config & 0x80);
+-              if (force || check_enable)
++              if (data->hwmon_dev && (force || check_enable))
+                       schedule_work(&data->report_work);
+               /*
+@@ -1157,7 +1157,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+                * alarms are all clear, and alerts are currently disabled.
+                * Otherwise (re)schedule worker if needed.
+                */
+-              if (check_enable) {
++              if (check_enable && data->hwmon_dev) {
+                       if (!(data->current_alarms & data->alert_alarms)) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               lm90_update_confreg(data, data->config & ~0x80);
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch
new file mode 100644 (file)
index 0000000..296417c
--- /dev/null
@@ -0,0 +1,90 @@
+From 54df4a21e0e9511f0cc77cd48bef097a405382b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:14:36 -0700
+Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
+
+Unlike NCT6106, NCT6116 only has three temperature registers, and with
+it only three temperature source and temperature source configuration
+registers. The register addresses match those of NCT6106 and can be
+re-used.
+
+The code used a separate array to list the temperature source registers
+for NCT6116, but used the size of the NCT6106 register array to set
+the number of registers. The NCT6106 register array provides six addresses,
+while the temperature source register array for NCT6116 only provides three
+addresses. This causes a KASAN report.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775]
+Read of size 2 at addr ffffffffc19561a6 by task modprobe/954
+...
+Call Trace:
+ dump_stack+0x7d/0xa7
+ print_address_description.constprop.0+0x1c/0x220
+ ? __kasan_kmalloc.constprop.0+0xc9/0xd0
+ ? __kmalloc_node_track_caller+0x194/0x5b0
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+...
+
+Fix the problem by hard-coding the number of temperature and temperature
+configuration registers to three for NCT6116. Drop the unnecessary
+NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
+
+Reported-by: Florian Bezdeka <florian.bezdeka@siemens.com>
+Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index da4c3425d2d1d..d47ba7cd97727 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+ static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+ static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+ static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+-      0xb0, 0xb1, 0xb2 };
+ static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+       0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                 = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+               data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+               data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+-              data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
++              data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
+-              num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
++              num_reg_temp = 3;
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
+               num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+-              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
++              num_reg_temp_config = 3;
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..071d063
--- /dev/null
@@ -0,0 +1,74 @@
+From d005dd7e5147d0c9f30ce487b55425b8227abb86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index d47ba7cd97727..6d28cf101f5f1 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch
new file mode 100644 (file)
index 0000000..b54e6ae
--- /dev/null
@@ -0,0 +1,53 @@
+From 160559e5a589db174ad0747414aeac0955fe9946 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 09:54:23 -0700
+Subject: hwmon: (nzxt-smart2) DMA-align output buffer
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
+
+Sashiko reports:
+
+When send_output_report() calls hid_hw_output_report(), the underlying USB
+HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
+
+When the DMA mapping flushes or invalidates the cacheline, it will corrupt
+the adjacent variables (mutex, update_interval) that were modified
+concurrently by the CPU. This causes memory corruption due to cacheline
+sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA
+API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings
+for this violation.
+
+Any operation that triggers send_output_report() (like setting a fan speed
+or updating the interval) causes the USB DMA mapping. On systems with
+non-coherent caches, this structural bug causes immediate and deterministic
+memory corruption.
+
+Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.")
+Cc: Aleksandr Mezin <mezin.alexander@gmail.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nzxt-smart2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
+index 0ce956954378d..5bbe6f3f8af48 100644
+--- a/drivers/hwmon/nzxt-smart2.c
++++ b/drivers/hwmon/nzxt-smart2.c
+@@ -203,7 +203,7 @@ struct drvdata {
+        */
+       struct mutex mutex;
+       long update_interval;
+-      u8 output_buffer[OUTPUT_REPORT_SIZE];
++      u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ };
+ static long scale_pwm_value(long val, long orig_max, long new_max)
+-- 
+2.53.0
+
diff --git a/queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..48d742b
--- /dev/null
@@ -0,0 +1,39 @@
+From 8383be2aca2eec42e2e8aee9948c2ad4bcda61ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index eafeabfd93d77..2cf5f1fe8886c 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -449,7 +449,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+-- 
+2.53.0
+
diff --git a/queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
new file mode 100644 (file)
index 0000000..9b67cdd
--- /dev/null
@@ -0,0 +1,335 @@
+From ff41a18936804d7d41f102b8e82ed388ebe979c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:17 +0300
+Subject: ipvs: do not mangle ICMP replies for non-first fragments
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
+
+Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the
+payload for embedded non-first IPv4 fragments. The problem is
+in the very old inverted pp->dont_defrag check which should not
+continue when embedded is a non-first TCP/UDP/SCTP fragment.
+
+Check for embedded non-first fragment is also missing from
+ip_vs_out_icmp_v6(), it is needed before any connection
+lookups that expect ports after the network headers.
+
+Drop the blocking code from ip_vs_in_icmp_v6() which prevents
+ICMPv6 from local clients to use non-MASQ forwarding.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h             | 11 +++---
+ net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++---------------------
+ net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++----
+ 3 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index a44979db80134..6935ec09af24d 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1627,8 +1627,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, unsigned int toff,
+-                  unsigned int wlen, unsigned int hooknum,
+-                  struct ip_vs_iphdr *ciph);
++                  unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1642,8 +1641,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, unsigned int toff,
+-                     unsigned int wlen, unsigned int hooknum,
+-                     struct ip_vs_iphdr *ciph);
++                     unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1708,12 +1706,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir, unsigned int toff);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff,
++                  bool has_ports);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph);
++                     bool has_ports, struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index d5b886ded5f11..e49a4840effb3 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout, unsigned int toff)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff,
++                  bool has_ports)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+       struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+-          IPPROTO_SCTP == ciph->protocol) {
++      if (has_ports) {
+               __be16 *ports = (void *)ciph + ciph->ihl*4;
+               if (inout)
+@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int inout, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph)
++                     bool has_ports, struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      int protocol;
+       struct icmp6hdr *icmph;
+       struct ipv6hdr *cih;
+       icmph = (struct icmp6hdr *)(skb->data + toff);
+       cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ciph->protocol;
+-
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+               cih->daddr = cp->vaddr.in6;
+@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!ciph->fragoffs &&
+-          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
+-           protocol == IPPROTO_SCTP)) {
++      if (has_ports) {
+               __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       unsigned int ctoff = ciph->len;
++      bool has_ports = false;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       }
+       if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+-          ciph->protocol == IPPROTO_SCTP)
++          ciph->protocol == IPPROTO_SCTP) {
+               ctoff += 2 * sizeof(__u16);
++              has_ports = true;
++      }
+       if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!pp)
+               return NF_ACCEPT;
++      /* Is the embedded protocol header present? */
++      if (unlikely(ciph.fragoffs && !pp->dont_defrag))
++              return NF_ACCEPT;
++
+       /* The embedded headers contain source and dest in reverse order */
+       cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
+                            ipvs, AF_INET6, skb, &ciph);
+@@ -1683,8 +1684,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       pp = pd->pp;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1692,7 +1692,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       offset2 = offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
+-      offset = ciph.len;
+       /* The embedded headers contain source and dest in reverse order.
+        * For IPIP/UDP/GRE tunnel this is error for request, not for reply.
+@@ -1786,11 +1785,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+-          IPPROTO_SCTP == cih->protocol)
+-              offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
+-                                &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1850,8 +1845,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       pp = pd->pp;
+-      /* Cannot handle fragmented embedded protocol */
+-      if (ciph.fragoffs)
++      /* Is the embedded protocol header present? */
++      if (ciph.fragoffs && !pp->dont_defrag)
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+@@ -1875,13 +1870,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               new_cp = true;
+       }
+-      /* VS/TUN, VS/DR and LOCALNODE just let it go */
+-      if ((hooknum == NF_INET_LOCAL_OUT) &&
+-          (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+-              verdict = NF_ACCEPT;
+-              goto out;
+-      }
+-
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+@@ -1897,14 +1885,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      /* Need to mangle contained IPv6 header in ICMPv6 packet */
+-      offset = ciph.len;
+-      if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+-          IPPROTO_SCTP == ciph.protocol)
+-              offset += 2 * sizeof(__u16); /* Also mangle ports */
+-
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
+-                                   &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index db562dac31c72..c214e5d05524c 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1474,13 +1474,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+       int local;
+       int rt_mode, was_input;
++      bool has_ports = false;
++      unsigned int wlen;
+       /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
+          forwarded directly here, because there is no need to
+@@ -1536,6 +1537,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1543,7 +1551,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1560,10 +1568,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
++      bool has_ports = false;
+       struct rt6_info *rt;    /* Route to the other host */
++      unsigned int wlen;
+       int rc;
+       int local;
+       int rt_mode;
+@@ -1621,6 +1630,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1628,7 +1644,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch
new file mode 100644 (file)
index 0000000..d2c9bc5
--- /dev/null
@@ -0,0 +1,624 @@
+From 6e9c0a6f87c89b1136ecabe2987f0384709b16e8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:16 +0300
+Subject: ipvs: fix places with wrong packet offsets
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
+
+The offsets we use to packet headers and payloads should be
+based on skb->data. We even already respect non-zero
+network offset in ip_vs_fill_iph_skb() but some places
+do it wrongly and support only zero offset which is expected
+for the IP layer where IPVS has hooks.
+
+Change all places that instead of skb->data use offsets based
+on the network header (skb_network_header, ip_hdr, etc) because
+this doubles the network offset as noted by Sashiko.
+
+For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header
+parsing done by the caller.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   |  15 +--
+ net/netfilter/ipvs/ip_vs_app.c        |   4 +-
+ net/netfilter/ipvs/ip_vs_core.c       | 133 +++++++++++++-------------
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_xmit.c       |  26 ++---
+ 7 files changed, 97 insertions(+), 93 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index aef042039cb00..a44979db80134 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1626,8 +1626,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                  struct ip_vs_protocol *pp, int offset,
+-                  unsigned int hooknum, struct ip_vs_iphdr *iph);
++                  struct ip_vs_protocol *pp, unsigned int toff,
++                  unsigned int wlen, unsigned int hooknum,
++                  struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1640,8 +1641,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                     struct ip_vs_protocol *pp, int offset,
+-                     unsigned int hooknum, struct ip_vs_iphdr *iph);
++                     struct ip_vs_protocol *pp, unsigned int toff,
++                     unsigned int wlen, unsigned int hooknum,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -1706,11 +1708,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                     struct ip_vs_conn *cp, int dir);
++                     struct ip_vs_conn *cp, int dir, unsigned int toff,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
+index f9132b359f0c6..0c690a30a85dc 100644
+--- a/net/netfilter/ipvs/ip_vs_app.c
++++ b/net/netfilter/ipvs/ip_vs_app.c
+@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 99b0090bdfadb..d5b886ded5f11 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+-      unsigned int icmp_offset = iph->ihl*4;
+-      struct icmphdr *icmph    = (struct icmphdr *)(skb_network_header(skb) +
+-                                                    icmp_offset);
++      struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+       struct iphdr *ciph       = (struct iphdr *)(icmph + 1);
++      unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+       if (inout) {
+               iph->saddr = cp->vaddr.ip;
+@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->checksum = 0;
+-      icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
++      icmph->checksum = ip_vs_checksum_complete(skb, toff);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered outgoing ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered outgoing ICMP");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered incoming ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered incoming ICMP");
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                     struct ip_vs_conn *cp, int inout, unsigned int toff,
++                     struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      unsigned int icmp_offset = 0;
+-      unsigned int offs        = 0; /* header offset*/
+       int protocol;
+       struct icmp6hdr *icmph;
+-      struct ipv6hdr *ciph;
+-      unsigned short fragoffs;
++      struct ipv6hdr *cih;
+-      ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+-      icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+-      offs = icmp_offset + sizeof(struct icmp6hdr);
+-      ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
++      icmph = (struct icmp6hdr *)(skb->data + toff);
++      cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
++      protocol = ciph->protocol;
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+-              ciph->daddr = cp->vaddr.in6;
++              cih->daddr = cp->vaddr.in6;
+       } else {
+               iph->daddr = cp->daddr.in6;
+-              ciph->saddr = cp->daddr.in6;
++              cih->saddr = cp->daddr.in6;
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-                        IPPROTO_SCTP == protocol)) {
+-              __be16 *ports = (void *)(skb_network_header(skb) + offs);
++      if (!ciph->fragoffs &&
++          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
++           protocol == IPPROTO_SCTP)) {
++              __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+                             ntohs(inout ? ports[1] : ports[0]),
+@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
+-                                            skb->len - icmp_offset,
++                                            skb->len - toff,
+                                             IPPROTO_ICMPV6, 0);
+-      skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
++      skb->csum_start = skb_headroom(skb) + toff;
+       skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered outgoing ICMPv6");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered incoming ICMPv6");
+ }
+ #endif
+@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+  */
+ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+-                              __u8 protocol, struct ip_vs_conn *cp,
++                              struct ip_vs_conn *cp,
+                               struct ip_vs_protocol *pp,
+-                              unsigned int offset, unsigned int ihl,
+-                              unsigned int hooknum)
++                              struct ip_vs_iphdr *ciph,
++                              unsigned int toff, unsigned int hooknum)
+ {
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
++      unsigned int ctoff = ciph->len;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
++      if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+               goto out;
+       }
+-      if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-          IPPROTO_SCTP == protocol)
+-              offset += 2 * sizeof(__u16);
+-      if (skb_ensure_writable(skb, offset))
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP)
++              ctoff += 2 * sizeof(__u16);
++      if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+  *    Currently handles error types - unreachable, quench, ttl exceeded.
+  */
+ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+-                        int *related, unsigned int hooknum)
++                        int *related, unsigned int hooknum,
++                        struct ip_vs_iphdr *ipvsh)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = ipvsh->len;
++      offset = ipvsh->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &ipvsh->saddr.ip, &ipvsh->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* Now find the contained IP header */
+       offset += sizeof(_icmph);
+       cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+-      if (cih == NULL)
++      if (!(cih && cih->version == 4 && cih->ihl >= 5))
+               return NF_ACCEPT; /* The packet looks wrong, ignore */
+       pp = ip_vs_proto_get(cih->protocol);
+@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!cp)
+               return NF_ACCEPT;
+-      snet.ip = iph->saddr;
+-      return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+-                                  pp, ciph.len, ihl, hooknum);
++      snet.ip = ipvsh->saddr.ip;
++      return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
++                                  hooknum);
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       struct ip_vs_conn *cp;
+       struct ip_vs_protocol *pp;
+       union nf_inet_addr snet;
+-      unsigned int offset;
+       *related = 1;
+       ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
+@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       snet.in6 = ciph.saddr.in6;
+-      offset = ciph.len;
+-      return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+-                                  pp, offset, ipvsh->len, hooknum);
++      return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
++                                  ipvsh->len, hooknum);
+ }
+ #endif
+@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
+ #endif
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+-                      int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
++                      int verdict = ip_vs_out_icmp(ipvs, skb, &related,
++                                                   hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -1572,9 +1568,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
+  */
+ static int
+ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+-            unsigned int hooknum)
++            unsigned int hooknum, struct ip_vs_iphdr *iph)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1584,7 +1579,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       unsigned int offset, offset2, ihl, verdict;
+       bool tunnel, new_cp = false;
+       union nf_inet_addr *raddr;
+-      char *outer_proto = "IPIP";
++      char *outer_proto __maybe_unused = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
+@@ -1594,17 +1589,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = iph->len;
++      offset = iph->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &iph->saddr.ip, &iph->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1721,7 +1718,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+           !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+-                        &iph->saddr);
++                        &iph->saddr.ip);
+               goto out;
+       }
+@@ -1792,7 +1789,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+           IPPROTO_SCTP == cih->protocol)
+               offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
++                                &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1905,7 +1903,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+           IPPROTO_SCTP == ciph.protocol)
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
++                                   &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -1984,7 +1983,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+-                                                  hooknum);
++                                                  hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -2120,6 +2119,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+                  const struct nf_hook_state *state)
+ {
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
++      struct ip_vs_iphdr iphdr;
+       int r;
+       /* ipvs enabled in this netns ? */
+@@ -2129,10 +2129,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+       if (state->pf == NFPROTO_IPV4) {
+               if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
+                       return NF_ACCEPT;
++              ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
+ #ifdef CONFIG_IP_VS_IPV6
+       } else {
+-              struct ip_vs_iphdr iphdr;
+-
+               ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
+               if (iphdr.protocol != IPPROTO_ICMPV6)
+@@ -2142,7 +2141,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+ #endif
+       }
+-      return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
++      return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
+ }
+ static const struct nf_hook_ops ip_vs_ops4[] = {
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index f6f732b7dfa86..3dbd3096e1637 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index bf31127338aa0..1ac9c233537d3 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->source = cp->vport;
+       /* Adjust TCP checksums */
+@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index 40d30649b3048..96ac882df15c1 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->source = cp->vport;
+       /*
+@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 02a2a966911ee..db562dac31c72 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1473,8 +1473,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+  */
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *iph)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+@@ -1486,7 +1487,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, iph);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1504,7 +1505,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+-                                 NULL, iph);
++                                 NULL, ciph);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
+@@ -1536,13 +1537,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1558,8 +1559,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ #ifdef CONFIG_IP_VS_IPV6
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *ipvsh)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rt6_info *rt;    /* Route to the other host */
+       int rc;
+@@ -1571,7 +1573,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, ipvsh);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1588,7 +1590,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+-                                    &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
++                                    &cp->daddr.in6, NULL, ciph, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = dst_rt6_info(skb_dst(skb));
+@@ -1620,13 +1622,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ipvs-fix-the-checksum-validations.patch b/queue-6.6/ipvs-fix-the-checksum-validations.patch
new file mode 100644 (file)
index 0000000..70f000c
--- /dev/null
@@ -0,0 +1,389 @@
+From 1b61d76c6b229826c754294fbeee6ffd009c255b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:15 +0300
+Subject: ipvs: fix the checksum validations
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
+
+ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6
+packets from clients. In fact, as for TCP/UDP we should
+validate the checksum for ICMP packets only when we
+mangle the packets on MASQ or on reply for tunnel.
+
+Also, Sashiko points out that handle_response_icmp() being
+common for IPv4 and IPv6 is missing the pseudo-header
+calculation while validating ICMPv6 messages from real
+servers which is a problem if checksum is not validated
+by the hardware.
+
+Fix the problems by creating ip_vs_checksum_common_check()
+helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6.
+Rely on the nf_checksum() for validating the ICMP messages
+but use it also for TCP and UDP.
+
+Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
+
+IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum
+validation on LOCAL_OUT (local clients or local real
+servers) and on FORWARD (traffic from servers on LAN).
+Do it only on LOCAL_IN, in case nf_checksum() is not
+called on PRE_ROUTING.
+
+Also, ip_vs_checksum_complete() can be marked static.
+
+Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6")
+Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   | 31 +++++++++++++++--
+ net/netfilter/ipvs/ip_vs_core.c       | 20 +++++++++--
+ net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++----
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  | 44 +++++------------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  | 50 ++++++---------------------
+ 5 files changed, 74 insertions(+), 86 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 08b90d33acdc6..aef042039cb00 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -24,7 +24,9 @@
+ #include <linux/netfilter.h>          /* for union nf_inet_addr */
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>                       /* for struct ipv6hdr */
++#include <net/route.h>
+ #include <net/ipv6.h>
++#include <net/ip6_fib.h>
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ #include <net/netfilter/nf_conntrack.h>
+ #endif
+@@ -1711,8 +1713,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+-
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+ {
+       __be32 diff[2] = { ~old, new };
+@@ -1738,6 +1738,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
+       return csum_partial(diff, sizeof(diff), oldsum);
+ }
++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
++{
++      /* Checksum unnecessary or already validated? */
++      if (skb_csum_unnecessary(skb))
++              return false;
++      /* LOCAL_OUT ? */
++      if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
++              return false;
++      /* !LOCAL_IN (FORWARD) ? */
++      if (af == AF_INET6) {
++              if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
++                      return false;
++      } else {
++              if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
++                      return false;
++      }
++      return true;
++}
++
++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
++                                             int offset, int proto, int af)
++{
++      if (!ip_vs_checksum_needed(skb, af))
++              return true;
++      return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
++}
++
+ /* Forget current conntrack (unconfirmed) and attach notrack entry */
+ static inline void ip_vs_notrack(struct sk_buff *skb)
+ {
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index fcd8e0857de2a..99b0090bdfadb 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+ {
+       return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
+ }
+@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               unsigned int offset, unsigned int ihl,
+                               unsigned int hooknum)
+ {
++      int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+@@ -1716,7 +1717,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
++          !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+                         &iph->saddr);
+@@ -1882,6 +1884,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               goto out;
+       }
++      verdict = NF_DROP;
++
++      /* Ensure the checksum is correct */
++      if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
++          !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
++                                       AF_INET6)) {
++              /* Failed checksum! */
++              IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
++                        &iph->saddr);
++              goto out;
++      }
++
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index c67317be17dfa..f6f732b7dfa86 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -11,7 +11,7 @@
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff);
++              struct ip_vs_iphdr *iph);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff)
++              struct ip_vs_iphdr *iph)
+ {
++      unsigned int sctphoff = iph->len;
+       struct sctphdr *sh;
+       __le32 cmp, val;
++      if (!ip_vs_checksum_needed(skb, af))
++              return 1;
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+       if (val != cmp) {
+               /* CRC failure, dump it. */
+-              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                              "Failed checksum for");
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
+               return 0;
+       }
+       return 1;
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index b382810156b2c..bf31127338aa0 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -30,7 +30,7 @@
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff)
++             struct ip_vs_iphdr *iph)
+ {
+-      switch (skb->ip_summed) {
+-      case CHECKSUM_NONE:
+-              skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+-              fallthrough;
+-      case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-              if (af == AF_INET6) {
+-                      if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                          &ipv6_hdr(skb)->daddr,
+-                                          skb->len - tcphoff,
+-                                          IPPROTO_TCP,
+-                                          skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              } else
+-#endif
+-                      if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                            ip_hdr(skb)->daddr,
+-                                            skb->len - tcphoff,
+-                                            ip_hdr(skb)->protocol,
+-                                            skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              break;
+-      default:
+-              /* No need to checksum. */
+-              break;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+-
+       return 1;
+ }
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index dbd4155bb0752..40d30649b3048 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -26,7 +26,7 @@
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff)
++             struct ip_vs_iphdr *iph)
+ {
+       struct udphdr _udph, *uh;
+-      uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
++      uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+-      if (uh->check != 0) {
+-              switch (skb->ip_summed) {
+-              case CHECKSUM_NONE:
+-                      skb->csum = skb_checksum(skb, udphoff,
+-                                               skb->len - udphoff, 0);
+-                      fallthrough;
+-              case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-                      if (af == AF_INET6) {
+-                              if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                                  &ipv6_hdr(skb)->daddr,
+-                                                  skb->len - udphoff,
+-                                                  IPPROTO_UDP,
+-                                                  skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      } else
+-#endif
+-                              if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                                    ip_hdr(skb)->daddr,
+-                                                    skb->len - udphoff,
+-                                                    ip_hdr(skb)->protocol,
+-                                                    skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      break;
+-              default:
+-                      /* No need to checksum. */
+-                      break;
+-              }
++      if (!uh->check)
++              return 1;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+       return 1;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..e3c9eeb
--- /dev/null
@@ -0,0 +1,64 @@
+From dbd84695e12b063eeaf61a57e8aa16bd62743ddc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index e105349794f23..b9ca9dc9b0c3f 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..00e07a2
--- /dev/null
@@ -0,0 +1,63 @@
+From 1d73c3eba682af319c50411905aa41a3bfbbda80 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index b9ca9dc9b0c3f..fd95a0eb7a466 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
new file mode 100644 (file)
index 0000000..45fbdf8
--- /dev/null
@@ -0,0 +1,50 @@
+From 08a57f25ee0bc05a8223a704c8b65de90a88f96a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:04:19 +0900
+Subject: ksmbd: fix use-after-free in __close_file_table_ids()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
+
+A ksmbd_file can remain alive after logical close while another session
+holds a temporary reference obtained through ksmbd_lookup_fd_inode().
+ksmbd_close_fd() currently marks the file closed and drops the idr-owned
+reference, but leaves the pointer published in the closing session's idr
+until the final reference is dropped.
+
+If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final()
+supplies the foreign session's file table to __ksmbd_close_fd(). The object
+is then freed without being removed from its owner's idr, and the owner
+session later dereferences the stale pointer during file-table teardown.
+
+Remove the volatile id from the owner's idr while ksmbd_close_fd() still
+holds that table's lock, and clear volatile_id before dropping
+the idr-owned reference. A later foreign final put then only performs
+physical destruction and cannot remove the object from the wrong table.
+
+Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp")
+Reported-by: Yunseong Kim <yunseong.kim@est.tech>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 320d467d46ca8..a15c427248494 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      idr_remove(ft->idr, id);
++                      fp->volatile_id = KSMBD_NO_FID;
+                       closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+-- 
+2.53.0
+
diff --git a/queue-6.6/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.6/ksmbd-return-success-for-deferred-final-close.patch
new file mode 100644 (file)
index 0000000..07827cc
--- /dev/null
@@ -0,0 +1,64 @@
+From ab59b591c611b18bc9a60eced2edd9d8dcbff87b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jun 2026 19:41:08 +0900
+Subject: ksmbd: return success for deferred final close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ]
+
+ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
+reference. If another in-flight request still holds a reference, the final
+close is deferred until that request drops its reference.
+
+The function currently returns -EINVAL in that deferred-final-close case
+because fp is cleared when the reference count does not reach zero.  That
+turns a valid close into STATUS_FILE_CLOSED.
+
+smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
+closes the same directory handle before receiving the find response.
+The query holds a reference while it builds the response, so close must
+mark the handle closed and return success even though final teardown is
+delayed. Track whether the handle was successfully transitioned to
+FP_CLOSED and return success when only the final close is deferred.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index d15641007d4e3..320d467d46ca8 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+ {
+       struct ksmbd_file       *fp;
+       struct ksmbd_file_table *ft;
++      bool closed = false;
+       if (!has_file_id(id))
+               return 0;
+@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+               }
+@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+       write_unlock(&ft->lock);
+       if (!fp)
+-              return -EINVAL;
++              return closed ? 0 : -EINVAL;
+       __put_fd_final(work, fp);
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..4ac9ee8
--- /dev/null
@@ -0,0 +1,40 @@
+From 8fc9a3a3a03c5a9430c95a60d92fec6908cd278d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index fd2de35ffb3cf..5fd22bb4f5b60 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
new file mode 100644 (file)
index 0000000..389bf5d
--- /dev/null
@@ -0,0 +1,78 @@
+From 5cd1a88ad1020ff51ca2a2e7cec1a77b39e66cc4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 07:29:01 +0000
+Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
+
+When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry
+under memory pressure or tree size caps, redirect handlers previously fell
+back to sending un-rate-limited ICMP/NDISC Redirect messages.
+
+In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL.
+In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into
+inet_peer_xrlim_allow(), which returned true when peer == NULL.
+
+Because ICMP/NDISC Redirects are not part of the default global rate limit
+mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates
+an un-rate-limited ICMP packet storm.
+
+Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and
+ndisc_send_redirect() when peer is NULL.
+
+Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c      | 2 --
+ net/ipv6/ip6_output.c | 2 +-
+ net/ipv6/ndisc.c      | 2 ++
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index f134c59f839e2..834ec1f34e506 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -902,8 +902,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
+       peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
+       if (!peer) {
+               rcu_read_unlock();
+-              icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
+-                        rt_nexthop(rt, ip_hdr(skb)->daddr));
+               return;
+       }
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 2e2cd0e0d48a5..5ee625d81942a 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -625,7 +625,7 @@ int ip6_forward(struct sk_buff *skb)
+               /* Limit redirects both by destination (here)
+                  and by source (inside ndisc_send_redirect)
+                */
+-              if (inet_peer_xrlim_allow(peer, 1*HZ))
++              if (peer && inet_peer_xrlim_allow(peer, 1*HZ))
+                       ndisc_send_redirect(skb, target);
+               rcu_read_unlock();
+       } else {
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index 342e7066f765f..8adb31804f01b 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1731,6 +1731,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       rcu_read_lock();
+       peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
++      if (!peer)
++              goto release;
+       ret = inet_peer_xrlim_allow(peer, 1*HZ);
+       rcu_read_unlock();
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
new file mode 100644 (file)
index 0000000..e439845
--- /dev/null
@@ -0,0 +1,191 @@
+From 19744227ad6dad7c7118e148ee491e342553d940 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:29 +0100
+Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ]
+
+The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through
+a helper which returns 0 when the underlying read fails, so a failed
+bus transaction clears MT7531_PHY_ACS_ST and the access carries on,
+returning garbage PHY register data to phylib.
+
+Poll using regmap_read_poll_timeout(), which stops on read errors and
+propagates them. These functions hold the MDIO bus lock across the
+whole sequence, so the unlocked regmap accesses remain correct. Remove
+the now-unused _mt7530_unlocked_read().
+
+Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 58 ++++++++++++++--------------------------
+ 1 file changed, 20 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index 8d889bc5f3d1b..aaa5640ad0f1c 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -233,12 +233,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+       mt7530_mutex_unlock(priv);
+ }
+-static u32
+-_mt7530_unlocked_read(struct mt7530_dummy_poll *p)
+-{
+-      return mt7530_mii_read(p->priv, p->reg);
+-}
+-
+ static u32
+ _mt7530_read(struct mt7530_dummy_poll *p)
+ {
+@@ -628,16 +622,13 @@ static int
+ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+                       int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 reg, val;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -647,8 +638,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -658,8 +649,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad);
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -676,16 +667,13 @@ static int
+ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+                        int regnum, u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 val, reg;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -695,8 +683,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -706,8 +694,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | data;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -722,16 +710,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+ static int
+ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 val;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -742,8 +727,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+       mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -760,16 +745,13 @@ static int
+ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+                        u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 reg;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -780,8 +762,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..37335be
--- /dev/null
@@ -0,0 +1,92 @@
+From 66dc33f68fd79fe30282ac061912ea9a9a285319 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 800e8b9eb4532..6d8ff73a7ebae 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1635,8 +1635,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->using_mac_select_pcs = using_mac_select_pcs;
+@@ -1660,28 +1660,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->cur_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..9e5e510
--- /dev/null
@@ -0,0 +1,48 @@
+From ba3f0ec1e1da255e7b7b7fad8a0de17cf86db153 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index fc5750396a3f0..df300493a8d9b 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..bf94700
--- /dev/null
@@ -0,0 +1,50 @@
+From 4a1578f38ccfc187c9d70bf83c2a0f13bfdda291 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index d662679c29832..fc5750396a3f0 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..d5162fe
--- /dev/null
@@ -0,0 +1,96 @@
+From 8d26964170957133ee4568cf983ba69f9321cd2a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index dbc614dfe0d56..aafa0c04f917e 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index 852c0b74b8a77..4f975b83c84f6 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index f7be30c69b5c8..a1c41defaf22d 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..7286377
--- /dev/null
@@ -0,0 +1,69 @@
+From fe95b45dd43052347fcbce979e9a2c6fe49852ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index 36c31ad2d64c1..15feb49e0b663 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -253,9 +253,7 @@ static int nft_payload_dump(struct sk_buff *skb,
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -264,15 +262,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..182eedb
--- /dev/null
@@ -0,0 +1,83 @@
+From 5b944fa0a1e9e36b7a3f3029115f0ee4f035ac3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 0859b8f767645..61813010cd319 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               vfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..f6a9ded
--- /dev/null
@@ -0,0 +1,42 @@
+From 3f5794c432321382c87a8b27628d6016568597e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index caea58f8fd86a..eb7ad5b3b629b 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -842,8 +842,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..9ea42fb
--- /dev/null
@@ -0,0 +1,76 @@
+From 2a3894dab9e76ac15ec72a111bc352658a328f48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index 559a9c26a0040..bf2a06a7f606f 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -643,12 +643,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -658,7 +659,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -672,7 +673,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -690,6 +691,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..a85e860
--- /dev/null
@@ -0,0 +1,56 @@
+From 1d5886533a4923c436ada2549d1343a823fd5037 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index bf2a06a7f606f..b6fc72287cd99 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1045,6 +1045,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1054,12 +1060,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
new file mode 100644 (file)
index 0000000..2a5c917
--- /dev/null
@@ -0,0 +1,172 @@
+From 7f5a3187e9c9037a0478b98057e353d84d53f714 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Apr 2025 08:35:47 +0200
+Subject: phy-zynqmp: Postpone getting clock rate until actually needed
+
+From: Mike Looijmans <mike.looijmans@topic.nl>
+
+[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
+
+At probe time the driver would display the following error and abort:
+  xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
+
+At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't
+initialized the clock yet, so clk_get_rate() likely returns 0 if the clock
+is programmable. So this driver only works if the clock is fixed.
+
+The PHY driver doesn't need to know the clock frequency at probe yet, so
+wait until the associated driver initializes the lane before requesting the
+clock rate setting.
+
+In addition to allowing the driver to be used with programmable clocks,
+this also reduces the driver's runtime memory footprint by removing an
+array of pointers from struct xpsgtr_phy.
+
+Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
+Acked-by: Michal Simek <michal.simek@amd.com>
+Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++----------------
+ 1 file changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index c72b52955a867..559a9c26a0040 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -227,7 +227,6 @@ struct xpsgtr_phy {
+  * @siou: siou base address
+  * @gtr_mutex: mutex for locking
+  * @phys: PHY lanes
+- * @refclk_sscs: spread spectrum settings for the reference clocks
+  * @clk: reference clocks
+  * @tx_term_fix: fix for GT issue
+  * @saved_icm_cfg0: stored value of ICM CFG0 register
+@@ -240,7 +239,6 @@ struct xpsgtr_dev {
+       void __iomem *siou;
+       struct mutex gtr_mutex; /* mutex for locking */
+       struct xpsgtr_phy phys[NUM_LANES];
+-      const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
+       struct clk *clk[NUM_LANES];
+       bool tx_term_fix;
+       unsigned int saved_icm_cfg0;
+@@ -383,13 +381,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
+       return ret;
+ }
++/* Get the spread spectrum (SSC) settings for the reference clock rate */
++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy)
++{
++      unsigned long rate;
++      struct clk *clk;
++      unsigned int i;
++
++      clk = gtr_phy->dev->clk[gtr_phy->refclk];
++      rate = clk_get_rate(clk);
++
++      for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
++              /* Allow an error of 100 ppm */
++              unsigned long error = ssc_lookup[i].refclk_rate / 10000;
++
++              if (abs(rate - ssc_lookup[i].refclk_rate) < error)
++                      return &ssc_lookup[i];
++      }
++
++      dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n",
++              rate, gtr_phy->refclk);
++
++      return NULL;
++}
++
+ /* Configure PLL and spread-sprectrum clock. */
+-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+ {
+       const struct xpsgtr_ssc *ssc;
+       u32 step_size;
+-      ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk];
++      ssc = xpsgtr_find_sscs(gtr_phy);
++      if (!ssc)
++              return -EINVAL;
++
+       step_size = ssc->step_size;
+       xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
+@@ -431,6 +456,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+       xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB,
+                          STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) |
+                          FORCE_STEP_SIZE | FORCE_STEPS);
++
++      return 0;
+ }
+ /* Configure the lane protocol. */
+@@ -643,7 +670,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+        * Configure the PLL, the lane protocol, and perform protocol-specific
+        * initialization.
+        */
+-      xpsgtr_configure_pll(gtr_phy);
++      ret = xpsgtr_configure_pll(gtr_phy);
++      if (ret)
++              goto out;
++
+       xpsgtr_lane_set_protocol(gtr_phy);
+       switch (gtr_phy->protocol) {
+@@ -854,8 +884,7 @@ static struct phy *xpsgtr_xlate(struct device *dev,
+       }
+       refclk = args->args[3];
+-      if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) ||
+-          !gtr_dev->refclk_sscs[refclk]) {
++      if (refclk >= ARRAY_SIZE(gtr_dev->clk)) {
+               dev_err(dev, "Invalid reference clock number %u\n", refclk);
+               return ERR_PTR(-EINVAL);
+       }
+@@ -931,9 +960,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+ {
+       unsigned int refclk;
+-      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
+-              unsigned long rate;
+-              unsigned int i;
++      for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) {
+               struct clk *clk;
+               char name[8];
+@@ -949,29 +976,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+                       continue;
+               gtr_dev->clk[refclk] = clk;
+-
+-              /*
+-               * Get the spread spectrum (SSC) settings for the reference
+-               * clock rate.
+-               */
+-              rate = clk_get_rate(clk);
+-
+-              for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+-                      /* Allow an error of 100 ppm */
+-                      unsigned long error = ssc_lookup[i].refclk_rate / 10000;
+-
+-                      if (abs(rate - ssc_lookup[i].refclk_rate) < error) {
+-                              gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+-                              break;
+-                      }
+-              }
+-
+-              if (i == ARRAY_SIZE(ssc_lookup)) {
+-                      dev_err(gtr_dev->dev,
+-                              "Invalid rate %lu for reference clock %u\n",
+-                              rate, refclk);
+-                      return -EINVAL;
+-              }
+       }
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
new file mode 100644 (file)
index 0000000..827a3ed
--- /dev/null
@@ -0,0 +1,58 @@
+From 13a22cfc40544c1590c407f08a3955c8b05c24d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:28:44 -0500
+Subject: pinctrl-amd: Don't clear S4 wake bits at probe
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
+
+commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+introduced a regression where Wake-on-LAN no longer works after suspend
+or shutdown on some AMD platforms.
+
+Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI
+PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake
+sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME
+does not use GPIO IRQ infrastructure and relies on firmware configuration.
+
+The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake
+bits on probe again") was to clear spurious wake bits left by firmware
+to prevent unwanted wakeups. However, S4 wake bits are used for
+hardware-level wake sources like WoL that bypass the kernel's IRQ wake
+API.
+
+Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
+- Firmware-configured S4 wake sources (WoL) continue working
+- Kernel maintains control of S3/S0i3 wake policy via set_wake()
+- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
+  amd: Take suspend type into consideration which pins are non-wake")
+
+The trade-off is that firmware-programmed spurious S4 wake bits remain
+set, but this is less problematic than breaking WoL.
+
+Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/pinctrl-amd.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index ba38173d3ed3c..9c937f6cf525c 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
+       u32 pin_reg, mask;
+       int i;
+-      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
+-              BIT(WAKE_CNTRL_OFF_S4);
++      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
+       for (i = 0; i < desc->npins; i++) {
+               int pin = desc->pins[i].number;
+-- 
+2.53.0
+
diff --git a/queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
new file mode 100644 (file)
index 0000000..a226408
--- /dev/null
@@ -0,0 +1,58 @@
+From ab3b68d14d087d6f6b1b74334c82ac2d4b1b748b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 15:08:05 +0200
+Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
+
+From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+
+[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
+
+Pins 143 and 151 were not included in the PDC wakeup map. They are
+normally used for PCIe2A and PCIe3a PERST# respectively, so they're
+unlikely to be excercised in practice, but still add them for the sake
+of completeness.
+
+Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver")
+Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+index 4b1c49697698d..67945ce867fa1 100644
+--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = {
+       { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 },
+       { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 },
+       { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 },
+-      { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 },
+-      { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 },
+-      { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 },
+-      { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 },
+-      { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 },
+-      { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 },
+-      { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 },
+-      { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 },
+-      { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 },
+-      { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 },
++      { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 },
++      { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 },
++      { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 },
++      { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 },
++      { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 },
++      { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 },
++      { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 },
++      { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 },
++      { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 },
++      { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 },
++      { 225, 196 }, { 227, 197 },
+ };
+ static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
+-- 
+2.53.0
+
diff --git a/queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..5da39c7
--- /dev/null
@@ -0,0 +1,38 @@
+From 86b58d7766eb874c5ea87469efd29e1b6d37452c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..8a66701
--- /dev/null
@@ -0,0 +1,38 @@
+From db54d6ba5ed1cf63e86a7d6a04fc36dd2dc257cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..bc50ade
--- /dev/null
@@ -0,0 +1,38 @@
+From 4d4c715751398a91487cb9a930e9b4f2d367f30b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..83eb184
--- /dev/null
@@ -0,0 +1,169 @@
+From 27e9f8ca828160b1021657a80472939985d835d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index b5d744d2586f7..59a80f7193723 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
new file mode 100644 (file)
index 0000000..70157a9
--- /dev/null
@@ -0,0 +1,118 @@
+From 46d0fe91023eefbaf1a63f9bc6849c732e02abf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 16:27:54 +0000
+Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
+
+From: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+
+[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
+
+When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst
+is never initialized because inet6_init() exits before addrconf_init()
+is called to initialize it. An attempt to bind an RDS socket to
+an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
+
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
+Call Trace:
+ <TASK>
+ ipv6_chk_addr+0x3b/0x50
+ rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
+ rds_trans_get_preferred+0x15d/0x2d0 [rds]
+ ? trace_hardirqs_on+0x2d/0x110
+ rds_bind+0x1433/0x1d60 [rds]
+ ? rds_remove_bound+0xd50/0xd50 [rds]
+ ? aa_af_perm+0x250/0x250
+ ? __might_fault+0xde/0x190
+ ? __sys_bind+0x1dc/0x210
+ __sys_bind+0x1dc/0x210
+ ? __ia32_sys_socketpair+0x100/0x100
+ ? restore_fpregs_from_fpstate+0x53/0x100
+ __x64_sys_bind+0x73/0xb0
+ ? syscall_enter_from_user_mode+0x1c/0x50
+ do_syscall_64+0x34/0x80
+ entry_SYSCALL_64_after_hwframe+0x6e/0xd8
+RIP: 0033:0x7f47f8269ea9
+ </TASK>
+
+The following code reproduces the issue:
+
+struct sockaddr_in6 addr;
+s = socket(PF_RDS, SOCK_SEQPACKET, 0);
+
+memset(&addr, 0, sizeof(addr));
+inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
+addr.sin6_family = AF_INET6;
+addr.sin6_port = htons(PORT);
+
+bind(s, &addr, sizeof(addr));
+
+Found by InfoTeCS on behalf of Linux Verification Center
+(linuxtesting.org) with Syzkaller.
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support")
+Signed-off-by: Ilia Gavrilov <Ilia.Gavrilov@infotecs.ru>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/ib.c    | 4 ++++
+ net/rds/ib_cm.c | 4 ++++
+ net/rds/tcp.c   | 8 +++++---
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/ib.c b/net/rds/ib.c
+index ce5be43c5fbac..1061bcf7d1315 100644
+--- a/net/rds/ib.c
++++ b/net/rds/ib.c
+@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
+               sa = (struct sockaddr *)&sin;
+       } else {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      ret = -EADDRNOTAVAIL;
++                      goto out;
++              }
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr = *addr;
+diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
+index 5289afbb61aa7..e50e01abb0799 100644
+--- a/net/rds/ib_cm.c
++++ b/net/rds/ib_cm.c
+@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
+       dp = event->param.conn.private_data;
+       if (isv6) {
+ #if IS_ENABLED(CONFIG_IPV6)
++              if (!ipv6_mod_enabled()) {
++                      err = -EOPNOTSUPP;
++                      goto out;
++              }
+               dp_cmn = &dp->ricp_v6.dp_cmn;
+               saddr6 = &dp->ricp_v6.dp_saddr;
+               daddr6 = &dp->ricp_v6.dp_daddr;
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index dee18da64a322..09dd862d7e9f1 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+               rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+-      ret = ipv6_chk_addr(net, addr, dev, 0);
+-      if (ret)
+-              return 0;
++      if (ipv6_mod_enabled()) {
++              ret = ipv6_chk_addr(net, addr, dev, 0);
++              if (ret)
++                      return 0;
++      }
+ #endif
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..cd8bca2
--- /dev/null
@@ -0,0 +1,85 @@
+From 57bff294ec9ea4f27ee3ec813a0686b867f77add Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 09dd862d7e9f1..997aee359025e 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch b/queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch
new file mode 100644 (file)
index 0000000..fba2bcf
--- /dev/null
@@ -0,0 +1,107 @@
+From 382f8130e3beb066598255f04254fffd343ce27e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 16 Oct 2019 15:03:50 -0400
+Subject: sched: Add task_struct->faults_disabled_mapping
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Kent Overstreet <kent.overstreet@gmail.com>
+
+[ Upstream commit 2b69987be575b92adb6c177679f3c559134f0d8f ]
+
+There has been a long standing page cache coherence bug with direct IO.
+This provides part of a mechanism to fix it, currently just used by
+bcachefs but potentially worth promoting to the VFS.
+
+Direct IO evicts the range of the pagecache being read or written to.
+
+For reads, we need dirty pages to be written to disk, so that the read
+doesn't return stale data. For writes, we need to evict that range of
+the pagecache so that it's not stale after the write completes.
+
+However, without a locking mechanism to prevent those pages from being
+re-added to the pagecache - by a buffered read or page fault - page
+cache inconsistency is still possible.
+
+This isn't necessarily just an issue for userspace when they're playing
+games; filesystems may hang arbitrary state off the pagecache, and so
+page cache inconsistency may cause real filesystem bugs, depending on
+the filesystem. This is less of an issue for iomap based filesystems,
+but e.g. buffer heads caches disk block mappings (!) and attaches them
+to the pagecache, and bcachefs attaches disk reservations to pagecache
+pages.
+
+This issue has been hard to fix, because
+ - we need to add a lock (henceforth called pagecache_add_lock), which
+   would be held for the duration of the direct IO
+ - page faults add pages to the page cache, thus need to take the same
+   lock
+ - dio -> gup -> page fault thus can deadlock
+
+And we cannot enforce a lock ordering with this lock, since userspace
+will be controlling the lock ordering (via the fd and buffer arguments
+to direct IOs), so we need a different method of deadlock avoidance.
+
+We need to tell the page fault handler that we're already holding a
+pagecache_add_lock, and since plumbing it through the entire gup() path
+would be highly impractical this adds a field to task_struct.
+
+Then the full method is:
+ - in the dio path, when we first take the pagecache_add_lock, note the
+   mapping in the current task_struct
+ - in the page fault handler, if faults_disabled_mapping is set, we
+   check if it's the same mapping as the one we're taking a page fault
+   for, and if so return an error.
+
+   Then we check lock ordering: if there's a lock ordering violation and
+   trylock fails, we'll have to cycle the locks and return an error that
+   tells the DIO path to retry: faults_disabled_mapping is also used for
+   signalling "locks were dropped, please retry".
+
+Also relevant to this patch: mapping->invalidate_lock.
+mapping->invalidate_lock provides most of the required semantics - it's
+used by truncate/fallocate to block pages being added to the pagecache.
+However, since it's a rwsem, direct IOs would need to take the write
+side in order to block page cache adds, and would then be exclusive with
+each other - we'll need a new type of lock to pair with this approach.
+
+Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
+Cc: Jan Kara <jack@suse.cz>
+Cc: Darrick J. Wong <djwong@kernel.org>
+Cc: linux-fsdevel@vger.kernel.org
+Cc: Andreas Grünbacher <andreas.gruenbacher@gmail.com>
+Stable-dep-of: e876b75b9020 ("ipvs: fix the checksum validations")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/sched.h | 1 +
+ init/init_task.c      | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index fad3aad97c7b0..c5e90874096e9 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -878,6 +878,7 @@ struct task_struct {
+       struct mm_struct                *mm;
+       struct mm_struct                *active_mm;
++      struct address_space            *faults_disabled_mapping;
+       int                             exit_state;
+       int                             exit_code;
+diff --git a/init/init_task.c b/init/init_task.c
+index fd9e27185e23a..9b363c986f824 100644
+--- a/init/init_task.c
++++ b/init/init_task.c
+@@ -85,6 +85,7 @@ struct task_struct init_task
+       .nr_cpus_allowed= NR_CPUS,
+       .mm             = NULL,
+       .active_mm      = &init_mm,
++      .faults_disabled_mapping = NULL,
+       .restart_block  = {
+               .fn = do_no_restart_syscall,
+       },
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..c4de17f
--- /dev/null
@@ -0,0 +1,56 @@
+From 275739a23e132d0ba096e834af9db5372dcc83c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 916c076484608..213aea93120d9 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..aa7e211
--- /dev/null
@@ -0,0 +1,71 @@
+From a7572438acbf05c8a9a5cf7b861f90a3ce176ed5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index c182aa83f2c93..4d23205129432 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
new file mode 100644 (file)
index 0000000..180a18f
--- /dev/null
@@ -0,0 +1,165 @@
+From 339fa9d2fc339dabd04fb527dc560d34b5a6e43c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 16:11:45 +0800
+Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
+
+From: Xingui Yang <yangxingui@huawei.com>
+
+[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
+
+Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue
+for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
+the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue,
+calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI
+devices and waits for the host to become runtime-active. But the host
+cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the
+drain is blocked on that very handler.
+
+However skipping the drain reintroduces a race: hisi_sas returns from
+resume before all PHY UP work and libsas discovery work finish. The
+controller may then autosuspend while disks are still waking up. The
+disks issue IO to a suspended controller, the IO fails, and the disks
+get disabled.
+
+Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT
+notification to after sas_drain_work(). By then the host resume is about
+to complete, so device removal through device_link no longer blocks on
+the resume and the cycle is broken.
+
+With the deadlock gone, restore sas_resume_ha() (the draining variant)
+in hisi_sas and remove sas_resume_ha_no_sync().
+
+The reorder is safe for the other libsas consumers (isci, pm8001,
+aic94xx, mvsas). During suspend, sas_suspend_devices() calls
+sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to
+NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a
+timed-out phy's disk is immediately rejected by the LLDD before reaching
+hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET),
+and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both
+complete directly via scsi_done() without entering SCSI EH. This is
+identical in both the old and new ordering since lldd_dev_gone runs
+during suspend, before resume. The reorder only affects when the
+PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work()
+vs. asynchronous after resume returns), not whether I/O can reach the
+device. aic94xx and mvsas do not register any PM ops and never reach
+this code path.
+
+Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume")
+Signed-off-by: Xingui Yang <yangxingui@huawei.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------
+ drivers/scsi/libsas/sas_init.c         | 37 +++++++++++++-------------
+ include/scsi/libsas.h                  |  1 -
+ 3 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index 7075dde4584db..d386672d59e4a 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -5345,15 +5345,7 @@ static int _resume_v3_hw(struct device *device)
+               return rc;
+       }
+       phys_init_v3_hw(hisi_hba);
+-
+-      /*
+-       * If a directly-attached disk is removed during suspend, a deadlock
+-       * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+-       * hisi_hba->device to be active, which can only happen when resume
+-       * completes. So don't wait for the HA event workqueue to drain upon
+-       * resume.
+-       */
+-      sas_resume_ha_no_sync(sha);
++      sas_resume_ha(sha);
+       clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+       dev_warn(dev, "end of resuming controller\n");
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index 8586dc79f2a0b..9fcdad2b55a8f 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+       }
+ }
+-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
++void sas_resume_ha(struct sas_ha_struct *ha)
+ {
+       const unsigned long tmo = msecs_to_jiffies(25000);
+       int i;
+@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+                        i, i > 1 ? "s" : "");
+       wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
++
++      /*
++       * All phys are back up or timed out. Turn on I/O and drain
++       * pending work.
++       */
++      scsi_unblock_requests(ha->shost);
++      sas_drain_work(ha);
++
++      /*
++       * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
++       * calls sas_deform_port() -> sas_destruct_devices(), which removes
++       * SCSI devices and, for LLDDs using device_link() PM sync, waits
++       * for the host to be runtime-active. Sending it before the drain
++       * would deadlock: the drain waits for the handler, the handler
++       * waits for host resume, and host resume waits for the drain to
++       * finish.
++       */
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_phy *phy = ha->sas_phy[i];
+@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               }
+       }
+-      /* all phys are back up or timed out, turn on i/o so we can
+-       * flush out disks that did not return
+-       */
+-      scsi_unblock_requests(ha->shost);
+-      if (drain)
+-              sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+       sas_queue_deferred_work(ha);
+@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+        */
+       sas_resume_insert_broadcast_ha(ha);
+ }
+-
+-void sas_resume_ha(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, true);
+-}
+ EXPORT_SYMBOL(sas_resume_ha);
+-/* A no-sync variant, which does not call sas_drain_ha(). */
+-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, false);
+-}
+-EXPORT_SYMBOL(sas_resume_ha_no_sync);
+-
+ void sas_suspend_ha(struct sas_ha_struct *ha)
+ {
+       int i;
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index 8a43534eea5cb..367b06fe5fcb8 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -678,7 +678,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
+ extern int sas_unregister_ha(struct sas_ha_struct *);
+ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
+ extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
+-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
+ extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
+ int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates);
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
new file mode 100644 (file)
index 0000000..9cb1765
--- /dev/null
@@ -0,0 +1,80 @@
+From 722836f34972b805dab74c38cc89fc7daff93d95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:30:10 +0300
+Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
+
+When target_get_sess_cmd() fails during session shutdown because
+percpu_ref_tryget_live() returns false, the command keeps the
+se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier
+without owning a reference. Final release through
+target_release_cmd_kref() then issues an unmatched percpu_ref_put().
+
+Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during
+cmd setup") moved the cmd_cnt assignment ahead of the reference
+acquisition.  Clear se_cmd->cmd_cnt whenever the initial
+target_get_sess_cmd() fails in target_init_cmd() and
+target_submit_tmr(), so release performs exactly one matching put per
+acquired reference.
+
+Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup")
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_transport.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
+index b9a144a59dff3..f4e3ba173dbe3 100644
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -1670,6 +1670,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+                   u32 data_length, int task_attr, int data_dir, int flags)
+ {
+       struct se_portal_group *se_tpg;
++      int ret;
+       se_tpg = se_sess->se_tpg;
+       BUG_ON(!se_tpg);
+@@ -1699,7 +1700,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
+        * kref_put() to happen during fabric packet acknowledgement.
+        */
+-      return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      if (ret)
++              se_cmd->cmd_cnt = NULL;
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(target_init_cmd);
+@@ -1994,8 +1999,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * allocation failure.
+        */
+       ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+-      if (ret < 0)
++      if (ret < 0) {
++              se_cmd->cmd_cnt = NULL;
+               return -ENOMEM;
++      }
+       if (tm_type == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = tag;
+@@ -2003,6 +2010,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+       /* See target_submit_cmd for commentary */
+       ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+       if (ret) {
++              se_cmd->cmd_cnt = NULL;
+               core_tmr_release_req(se_cmd->se_tmr_req);
+               return ret;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
new file mode 100644 (file)
index 0000000..32d0996
--- /dev/null
@@ -0,0 +1,54 @@
+From 06804e3b28ece015323f30a57d38f45bc86e5a42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:58:50 +0800
+Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE
+
+From: TanZheng <tanzheng@kylinos.cn>
+
+[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ]
+
+In the iblock_execute_pr_out() function, PRO_PREEMPT,
+PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability
+checks through ops->pr_clear. The error check allows unimplemented hooks
+to pass through the gate, resulting dereferencing a NULL function
+pointer.
+
+Check whether the hooks that need to be called are supported.
+
+Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock")
+Signed-off-by: TanZheng <tanzheng@kylinos.cn>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_iblock.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
+index a6a06a5f74834..4d025163569ac 100644
+--- a/drivers/target/target_core_iblock.c
++++ b/drivers/target/target_core_iblock.c
+@@ -885,7 +885,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+               break;
+       case PRO_PREEMPT:
+       case PRO_PREEMPT_AND_ABORT:
+-              if (!ops->pr_clear) {
++              if (!ops->pr_preempt) {
+                       pr_err("block_device does not support pr_preempt.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+@@ -895,8 +895,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+                                     sa == PRO_PREEMPT_AND_ABORT);
+               break;
+       case PRO_RELEASE:
+-              if (!ops->pr_clear) {
+-                      pr_err("block_device does not support pr_pclear.\n");
++              if (!ops->pr_release) {
++                      pr_err("block_device does not support pr_release.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
new file mode 100644 (file)
index 0000000..cb3409a
--- /dev/null
@@ -0,0 +1,70 @@
+From 7c17ca95b3ce8a19de1970c7f0b7e7b08dd12763 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 01:27:26 +0800
+Subject: scsi: ufs: core: Cancel RTC work in active-active suspend
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ]
+
+UFS RTC support schedules ufs_rtc_update_work to periodically update the
+device RTC. The work can issue query commands and access the UFS host
+controller.
+
+A previous change moved the RTC work cancellation before the PRE_CHANGE
+vendor suspend callback to close a race in the common suspend path.
+However, the active-active path jumps directly to vops_suspend after
+flushing exception handling work and therefore bypasses the
+cancellation.
+
+If the RTC work runs while the vendor suspend callback is gating or
+otherwise changing hardware state, it can access the controller during
+suspend and trigger an SError.
+
+Cancel the RTC work before entering the vendor suspend callback in the
+active-active path. Since this path now cancels the work, move the RTC
+work scheduling outside the device and link state restoration block in
+the resume path. This restarts RTC updates after an active-active
+suspend and resume cycle.
+
+Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend")
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 142abff301220..3bbe1088b7e49 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -9830,6 +9830,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+                       req_link_state == UIC_LINK_ACTIVE_STATE) {
+               ufshcd_disable_auto_bkops(hba);
+               flush_work(&hba->eeh_work);
++              cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
+               goto vops_suspend;
+       }
+@@ -10039,10 +10040,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+               if (ret)
+                       goto set_old_link_state;
+               ufshcd_set_timestamp_attr(hba);
+-              schedule_delayed_work(&hba->ufs_rtc_update_work,
+-                                    msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+       }
++      schedule_delayed_work(&hba->ufs_rtc_update_work,
++                            msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
++
+       if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+               ufshcd_enable_auto_bkops(hba);
+       else
+-- 
+2.53.0
+
diff --git a/queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..9377ad0
--- /dev/null
@@ -0,0 +1,66 @@
+From 36a9acded91f5004274061670e1f885ba40880e6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index ab2f35bc294da..d3cc884ccd599 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
index 441fddf862ed4be55e107e92f9b10aaf404f2781..e9d5e7c094f1f5bba40b69b4aeab8c42d8ffcee5 100644 (file)
@@ -7,3 +7,79 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
 hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
 soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
 thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
+pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
+ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
+ata-libahci_platform-support-non-consecutive-port-nu.patch
+ahci-introduce-ahci_ignore_port-helper.patch
+ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
+phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+sched-add-task_struct-faults_disabled_mapping.patch
+ipvs-fix-the-checksum-validations.patch
+ipvs-fix-places-with-wrong-packet-offsets.patch
+ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+spi-spi-cadence-supports-transmission-with-bits_per_.patch
+spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
+hwmon-nct6775-core-fix-number-of-temperature-registe.patch
+hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
+hwmon-nzxt-smart2-dma-align-output-buffer.patch
+net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
+bluetooth-btintel-validate-length-before-parsing-dia.patch
+bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch
+bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
+bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
+bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
+scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
+net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
+ksmbd-return-success-for-deferred-final-close.patch
+ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
diff --git a/queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..71a0914
--- /dev/null
@@ -0,0 +1,85 @@
+From a778b6aacf61fa25feee9825af8db6e2bae7b87f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 91f4e50af1e94..877b4ad03fe9c 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -1416,8 +1416,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1529,8 +1531,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1780,8 +1784,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
new file mode 100644 (file)
index 0000000..ccd48c4
--- /dev/null
@@ -0,0 +1,111 @@
+From 307298393a127d324ea21cf7433e0fd2181f647a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 18:25:10 +0530
+Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Srikanth Boyapally <srikanth.boyapally@amd.com>
+
+[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ]
+
+SPI host transfers could intermittently stall with spi_transfer timeouts.
+The TXFULL condition was checked only once in cdns_transfer_one() before
+cdns_spi_process_fifo(), so if the FIFO became full again during refill,
+writes could be dropped and the transfer would never complete.
+
+Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so
+the 10µs back-off is applied per FIFO entry during filling, ensuring
+forward progress and eliminating spurious timeouts.
+
+Restrict the delay to host mode using spi_controller_is_target(), the
+controller is passed into cdns_spi_process_fifo() so the check is made at
+the point of use. In target mode this delay must not run as it causes the
+target to miss its transfer window and corrupt data.
+
+Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()")
+Signed-off-by: Srikanth Boyapally <srikanth.boyapally@amd.com>
+Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index 80ec9bbf84187..01b386878ee3a 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -385,11 +385,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi)
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
++ * @ctlr:     Pointer to the spi_controller structure
+  * @xspi:     Pointer to the cdns_spi structure
+  * @ntx:      Number of bytes to pack into the TX FIFO
+  * @nrx:      Number of bytes to drain from the RX FIFO
+  */
+-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
++static void cdns_spi_process_fifo(struct spi_controller *ctlr,
++                                struct cdns_spi *xspi, int ntx, int nrx)
+ {
+       ntx = clamp(ntx, 0, xspi->tx_bytes);
+       nrx = clamp(nrx, 0, xspi->rx_bytes);
+@@ -404,6 +406,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+               }
+               if (ntx) {
++                      /* When xspi in busy condition, bytes may send failed,
++                       * then spi control didn't work thoroughly, add one byte
++                       * delay. Only in host mode; in target mode this delay
++                       * causes data corruption as the target fails to prepare
++                       * data in time.
++                       */
++                      if (!spi_controller_is_target(ctlr) &&
++                          (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL))
++                              udelay(10);
++
+                       cdns_spi_writer(xspi);
+                       ntx--;
+               }
+@@ -457,14 +469,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, 1);
+               if (xspi->tx_bytes) {
+-                      cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt);
+               } else {
+                       /* Fixed delay due to controller limitation with
+                        * RX_NEMPTY incorrect status
+                        * Xilinx AR:65885 contains more details
+                        */
+                       udelay(10);
+-                      cdns_spi_process_fifo(xspi, 0, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt);
+                       cdns_spi_write(xspi, CDNS_SPI_IDR,
+                                      CDNS_SPI_IXR_DEFAULT);
+                       spi_finalize_current_transfer(ctlr);
+@@ -517,17 +529,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
+       }
+-      /* When xspi in busy condition, bytes may send failed,
+-       * then spi control didn't work thoroughly, add one byte delay
+-       */
+-      if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+-              udelay(10);
+-
+       xspi->n_bytes = cdns_spi_n_bytes(transfer);
+       xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+       xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+-      cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
++      cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+       return transfer->len;
+-- 
+2.53.0
+
diff --git a/queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch
new file mode 100644 (file)
index 0000000..0bd88b1
--- /dev/null
@@ -0,0 +1,199 @@
+From 6b49174a8e4a95adf14bc7ae721160fd333d2701 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Oct 2025 15:30:02 +0800
+Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and
+ 32
+
+From: Jun Guo <jun.guo@cixtech.com>
+
+[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ]
+
+The default FIFO data width of the Cadence SPI IP is 8 bits, but
+the hardware supports configurations of 16 bits and 32 bits.
+This patch enhances the driver to support communication with both
+16-bits and 32-bits FIFO data widths.
+
+Signed-off-by: Jun Guo <jun.guo@cixtech.com>
+Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 93 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index c6579db42eff3..80ec9bbf84187 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -108,6 +108,7 @@
+  * @rxbuf:            Pointer to the RX buffer
+  * @tx_bytes:         Number of bytes left to transfer
+  * @rx_bytes:         Number of bytes requested
++ * @n_bytes:          Number of bytes per word
+  * @dev_busy:         Device busy flag
+  * @is_decoded_cs:    Flag for decoder property set or not
+  * @tx_fifo_depth:    Depth of the TX FIFO
+@@ -118,15 +119,23 @@ struct cdns_spi {
+       struct clk *pclk;
+       unsigned int clk_rate;
+       u32 speed_hz;
+-      const u8 *txbuf;
+-      u8 *rxbuf;
++      const void *txbuf;
++      void *rxbuf;
+       int tx_bytes;
+       int rx_bytes;
++      u8 n_bytes;
+       u8 dev_busy;
+       u32 is_decoded_cs;
+       unsigned int tx_fifo_depth;
+ };
++enum cdns_spi_frame_n_bytes {
++      CDNS_SPI_N_BYTES_NULL = 0,
++      CDNS_SPI_N_BYTES_U8 = 1,
++      CDNS_SPI_N_BYTES_U16 = 2,
++      CDNS_SPI_N_BYTES_U32 = 4
++};
++
+ /* Macros for the SPI controller read/write */
+ static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
+ {
+@@ -302,6 +311,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
+       return 0;
+ }
++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
++{
++      if (transfer->bits_per_word <= 8)
++              return CDNS_SPI_N_BYTES_U8;
++      else if (transfer->bits_per_word <= 16)
++              return CDNS_SPI_N_BYTES_U16;
++      else
++              return CDNS_SPI_N_BYTES_U32;
++}
++
++static inline void cdns_spi_reader(struct cdns_spi *xspi)
++{
++      u32 rxw = 0;
++
++      if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
++              pr_err("%s: rxbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
++      if (xspi->rxbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      *(u8 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      *(u16 *)xspi->rxbuf = rxw;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      *(u32 *)xspi->rxbuf = rxw;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
++      }
++}
++
++static inline void cdns_spi_writer(struct cdns_spi *xspi)
++{
++      u32 txw = 0;
++
++      if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
++              pr_err("%s: txbuf address is not aligned for %d bytes\n",
++                     __func__, xspi->n_bytes);
++              return;
++      }
++
++      if (xspi->txbuf) {
++              switch (xspi->n_bytes) {
++              case CDNS_SPI_N_BYTES_U8:
++                      txw = *(u8 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U16:
++                      txw = *(u16 *)xspi->txbuf;
++                      break;
++              case CDNS_SPI_N_BYTES_U32:
++                      txw = *(u32 *)xspi->txbuf;
++                      break;
++              default:
++                      pr_err("%s invalid n_bytes %d\n", __func__,
++                             xspi->n_bytes);
++                      return;
++              }
++              cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
++              xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
++      }
++}
++
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
+  * @xspi:     Pointer to the cdns_spi structure
+@@ -318,23 +399,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+       while (ntx || nrx) {
+               if (nrx) {
+-                      u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
+-
+-                      if (xspi->rxbuf)
+-                              *xspi->rxbuf++ = data;
+-
++                      cdns_spi_reader(xspi);
+                       nrx--;
+               }
+               if (ntx) {
+-                      if (xspi->txbuf)
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
+-                      else
+-                              cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
+-
++                      cdns_spi_writer(xspi);
+                       ntx--;
+               }
+-
+       }
+ }
+@@ -451,6 +523,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+       if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+               udelay(10);
++      xspi->n_bytes = cdns_spi_n_bytes(transfer);
++      xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
++      xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
++
+       cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+@@ -641,6 +717,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
+       ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
+       ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
++      if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
++              ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
++
+       if (!spi_controller_is_target(ctlr)) {
+               ctlr->mode_bits |=  SPI_CS_HIGH;
+               ctlr->set_cs = cdns_spi_chipselect;
+@@ -796,6 +875,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
+ static const struct of_device_id cdns_spi_of_match[] = {
+       { .compatible = "xlnx,zynq-spi-r1p6" },
++      { .compatible = "cix,sky1-spi-r1p6" },
+       { .compatible = "cdns,spi-r1p6" },
+       { /* end of table */ }
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..67e3dd4
--- /dev/null
@@ -0,0 +1,43 @@
+From 74a639dada7fbdb3cf56c7f978717661ff34d667 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index 4d9e5c830dbe1..c523ce5aa4958 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..cbb4360
--- /dev/null
@@ -0,0 +1,52 @@
+From 835a8edbac2cce177c4e73f4f3b1c5a6ebc813cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index c1f964e9991cd..9914390ff31ff 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
new file mode 100644 (file)
index 0000000..4982666
--- /dev/null
@@ -0,0 +1,49 @@
+From ad4ff7d5d60cbf44cb845beeda20f54ffa421a58 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 02:25:20 +0500
+Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ]
+
+In encode_message() the per-transaction lower-bound check compares
+trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer,
+instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr.
+
+Every other length check in this file (encode_message() at the loop
+guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so
+this is an inconsistency. On 64-bit builds the pointer and the struct
+are both 8 bytes, so the check is correct by coincidence and there is
+no behavioural change. On 32-bit builds the pointer is 4 bytes, which
+weakens the minimum-length check below the 8-byte header size.
+
+Use sizeof(*trans_hdr) so the check validates against the actual
+transaction header size on all builds.
+
+Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()")
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accel/qaic/qaic_control.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
+index 43f84d4389602..4d4e789d5fcb8 100644
+--- a/drivers/accel/qaic/qaic_control.c
++++ b/drivers/accel/qaic/qaic_control.c
+@@ -786,7 +786,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+                       break;
+               }
+               trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
+-              if (trans_hdr->len < sizeof(trans_hdr) ||
++              if (trans_hdr->len < sizeof(*trans_hdr) ||
+                   size_add(user_len, trans_hdr->len) > user_msg->len) {
+                       ret = -EINVAL;
+                       break;
+-- 
+2.53.0
+
diff --git a/queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch b/queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch
new file mode 100644 (file)
index 0000000..a9a4835
--- /dev/null
@@ -0,0 +1,61 @@
+From a1c2472d5fda092f0f4d7a202f57a138995c459c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 11:40:42 +0100
+Subject: ACPI: CPPC: Skip writes to unsupported performance controls
+
+From: Christian Loehle <christian.loehle@arm.com>
+
+[ Upstream commit 47d4e945dff8139050473be4ab263a32e1da910c ]
+
+MIN_PERF and MAX_PERF are optional CPPC controls. DESIRED_PERF is also
+optional with CPPC2 when autonomous selection is supported.
+
+The cppc-cpufreq target callbacks populate both limits for every request
+without checking whether the controls are implemented. cppc_set_perf()
+consequently passes NULL register descriptors to cpc_write(). The writes
+fail width validation and their return values are ignored, so the failed
+access paths are repeated on every target request. An autonomous-only
+platform can take the same path for DESIRED_PERF.
+
+Check that each performance control is supported before calling
+cpc_write().
+
+Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks")
+Reviewed-by: Sumit Gupta <sumitg@nvidia.com>
+Signed-off-by: Christian Loehle <christian.loehle@arm.com>
+Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
+Link: https://patch.msgid.link/20260724104042.1481804-1-christian.loehle@arm.com
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/acpi/cppc_acpi.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
+index 34edec0f2bde2..aa4017be960a0 100644
+--- a/drivers/acpi/cppc_acpi.c
++++ b/drivers/acpi/cppc_acpi.c
+@@ -1940,16 +1940,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+               cpc_desc->write_cmd_status = 0;
+       }
+-      cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
++      if (CPC_SUPPORTED(desired_reg))
++              cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+       /*
+        * Only write if min_perf and max_perf not zero. Some drivers pass zero
+        * value to min and max perf, but they don't mean to set the zero value,
+        * they just don't want to write to those registers.
+        */
+-      if (perf_ctrls->min_perf)
++      if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg))
+               cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf);
+-      if (perf_ctrls->max_perf)
++      if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg))
+               cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
+       if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))
+-- 
+2.53.0
+
diff --git a/queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch b/queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch
new file mode 100644 (file)
index 0000000..e8ec81a
--- /dev/null
@@ -0,0 +1,41 @@
+From 841abc906f2843e538fa657f3a5617484a53f88a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 14:29:01 -0400
+Subject: af_unix: fix listen() succeeding on sockets in the wrong state
+
+From: John Ericson <mail@johnericson.me>
+
+[ Upstream commit f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 ]
+
+Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
+reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
+= -EINVAL and the socket-state check in unix_listen(). Since
+prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
+socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
+already connected) now silently returns success without doing anything,
+instead of failing with EINVAL as it did before.
+
+Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
+Signed-off-by: John Ericson <mail@johnericson.me>
+Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/unix/af_unix.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
+index 0d9cd977c7b78..bb01d5371dc85 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -823,6 +823,7 @@ static int unix_listen(struct socket *sock, int backlog)
+       if (err)
+               goto out;
+       unix_state_lock(sk);
++      err = -EINVAL;
+       if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
+               goto out_unlock;
+       if (backlog > sk->sk_max_ack_backlog)
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..2508e3b
--- /dev/null
@@ -0,0 +1,61 @@
+From 2183c56240c4328b35f328977144f7c460278018 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:12:54 +0530
+Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
+
+In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98090->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98090.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
+index 13a15459040fc..595efd84b62b3 100644
+--- a/sound/soc/codecs/max98090.c
++++ b/sound/soc/codecs/max98090.c
+@@ -2424,8 +2424,9 @@ static int max98090_probe(struct snd_soc_component *component)
+       dev_dbg(component->dev, "max98090_probe\n");
+       max98090->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98090->mclk))
++              if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       max98090->component = component;
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
new file mode 100644 (file)
index 0000000..057caa4
--- /dev/null
@@ -0,0 +1,61 @@
+From 373bb5657533fa799b4e9990109b626f541062ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:09:50 +0530
+Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
+
+From: Uday Khare <udaykhare77@gmail.com>
+
+[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
+
+In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is
+broken due to a missing IS_ERR() guard.
+
+The code intends to return -EPROBE_DEFER only when the clock lookup
+fails with that specific error.  However, without IS_ERR() the check:
+
+    if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+
+is called unconditionally, including when devm_clk_get() succeeds and
+returns a valid pointer.  Calling PTR_ERR() on a valid pointer
+reinterprets its address as a signed long; the result is arbitrary
+and is almost never equal to -EPROBE_DEFER, so the check silently
+does nothing in the success case.  When devm_clk_get() fails with
+any error other than -EPROBE_DEFER the check is also skipped, leaving
+max98095->mclk holding an error pointer with no indication to the caller.
+
+This means a deferred probe will never actually be triggered for this
+device, and any non-EPROBE_DEFER clock error is silently swallowed with
+the error pointer left in the mclk field.
+
+Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call,
+matching the pattern already used in the sibling max98088 and wm8960
+drivers.
+
+Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling")
+Signed-off-by: Uday Khare <udaykhare77@gmail.com>
+Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/max98095.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
+index aae6423156e16..207374e451810 100644
+--- a/sound/soc/codecs/max98095.c
++++ b/sound/soc/codecs/max98095.c
+@@ -1987,8 +1987,9 @@ static int max98095_probe(struct snd_soc_component *component)
+       int ret = 0;
+       max98095->mclk = devm_clk_get(component->dev, "mclk");
+-      if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+-              return -EPROBE_DEFER;
++      if (IS_ERR(max98095->mclk))
++              if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
+       /* reset the codec, the DSP core, and disable all interrupts */
+       max98095_reset(component);
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch b/queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch
new file mode 100644 (file)
index 0000000..5beb95f
--- /dev/null
@@ -0,0 +1,50 @@
+From a5a48fd2539aa85000227c8a2416946955a962f5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 11:34:58 +0100
+Subject: ASoC: SDCA: Always free firmware in FDL path
+
+From: Charles Keepax <ckeepax@opensource.cirrus.com>
+
+[ Upstream commit 7f64ccc374b2fe1f6a169182e95ab8e104cae406 ]
+
+In the case a disk firmware exists but is invalid and no SWFT firmware
+exists fdl_load_file() will return without calling release_firmware().
+Update the code to call this to ensure the firmware is released on the
+error path.
+
+Fixes: 71f7990a34cd ("ASoC: SDCA: Add FDL library for XU entities")
+Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
+Link: https://patch.msgid.link/20260722103500.872714-3-ckeepax@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sdca/sdca_fdl.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
+index 994821a6df617..60fdd406220d4 100644
+--- a/sound/soc/sdca/sdca_fdl.c
++++ b/sound/soc/sdca/sdca_fdl.c
+@@ -258,7 +258,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
+       if (!swf) {
+               dev_err(dev, "failed to locate SWF\n");
+-              return -ENOENT;
++              ret = -ENOENT;
++              goto error;
+       }
+       dev_info(dev, "loading SWF: %x-%x-%x\n",
+@@ -270,6 +271,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
+                                    SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset,
+                                    SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data,
+                                    swf->file_length - offsetof(struct acpi_sw_file, data));
++
++error:
+       release_firmware(firmware);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch b/queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch
new file mode 100644 (file)
index 0000000..c8da204
--- /dev/null
@@ -0,0 +1,41 @@
+From 09c00944ef8abd28ceb4c089d4cdb026e1b3ecb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 11:34:57 +0100
+Subject: ASoC: SDCA: Correct pointer passed to devm_acpi_table_put
+
+From: Charles Keepax <ckeepax@opensource.cirrus.com>
+
+[ Upstream commit 6a50332e194f58b562d396ab772c34e8c9890c0f ]
+
+devm_acpi_table_put() takes a struct acpi_table_header * but the value
+passed in is struct acpi_table_header ** so the value passed to
+acpi_put_table() is actually the pointer not the table itself.
+
+Remove the extra reference to correct the passed value.
+
+Fixes: c4d096c3ca42 ("ASoC: SDCA: Add SDCA FDL data parsing")
+Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
+Link: https://patch.msgid.link/20260722103500.872714-2-ckeepax@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sdca/sdca_device.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c
+index 405e80b979de8..4bcd8d1fdff82 100644
+--- a/sound/soc/sdca/sdca_device.c
++++ b/sound/soc/sdca/sdca_device.c
+@@ -43,7 +43,7 @@ void sdca_lookup_swft(struct sdw_slave *slave)
+               dev_info(&slave->dev, "SWFT not available\n");
+       else
+               devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
+-                                       &slave->sdca_data.swft);
++                                       slave->sdca_data.swft);
+ }
+ EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch b/queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch
new file mode 100644 (file)
index 0000000..104f110
--- /dev/null
@@ -0,0 +1,39 @@
+From a6590b364b86db91b2d5986984dcf9cbe5933851 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 11:35:00 +0100
+Subject: ASoC: SDCA: Ensure that Control Range is large enough for header
+
+From: Charles Keepax <ckeepax@opensource.cirrus.com>
+
+[ Upstream commit 951e921b039b793bef7050eaf5c5fb1a4a5341d1 ]
+
+When reading the Ranges structure from an SDCA Control, ensure that the
+read data is large enough to encompass the required header before
+accessing it.
+
+Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures")
+Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
+Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sdca/sdca_functions.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
+index 196bade11ab5d..ecc372a397573 100644
+--- a/sound/soc/sdca/sdca_functions.c
++++ b/sound/soc/sdca/sdca_functions.c
+@@ -847,6 +847,8 @@ static int find_sdca_control_range(struct device *dev,
+               return 0;
+       else if (num_range < 0)
+               return num_range;
++      else if (num_range < 2 * sizeof(*limits))
++              return -EINVAL;
+       range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL);
+       if (!range_list)
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch b/queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch
new file mode 100644 (file)
index 0000000..74ef5dc
--- /dev/null
@@ -0,0 +1,48 @@
+From 392aca6ce74c6e3de1e44b12d79b0c2eb0106f2b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 11:34:59 +0100
+Subject: ASoC: SDCA: Make UMP message size check more robust
+
+From: Charles Keepax <ckeepax@opensource.cirrus.com>
+
+[ Upstream commit 556d872e7c2a0b570c5b0974813847ef0d0cd637 ]
+
+If message offset was larger than the buffer length the size
+check will pass incorrectly. Refactor the check such that it is
+more robust to invalid sizes.
+
+Fixes: daab108504be ("ASoC: SDCA: Add UMP buffer helper functions")
+Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
+Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
+Link: https://patch.msgid.link/20260722103500.872714-4-ckeepax@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sdca/sdca_ump.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c
+index a86bb28c6d0ad..82a8bf75bbca5 100644
+--- a/sound/soc/sdca/sdca_ump.c
++++ b/sound/soc/sdca/sdca_ump.c
+@@ -141,7 +141,7 @@ int sdca_ump_read_message(struct device *dev,
+               return ret;
+       }
+-      if (msg_len > buf_len - msg_offset) {
++      if (msg_offset + msg_len > buf_len) {
+               dev_err(dev, "%s: message too big for UMP buffer: %d\n",
+                       entity->label, msg_len);
+               return -EINVAL;
+@@ -207,7 +207,7 @@ int sdca_ump_write_message(struct device *dev,
+       buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
+       ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0);
+-      if (msg_len > buf_len - msg_offset) {
++      if (msg_offset + msg_len > buf_len) {
+               dev_err(dev, "%s: message too big for UMP buffer: %d\n",
+                       entity->label, msg_len);
+               return -EINVAL;
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch b/queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch
new file mode 100644 (file)
index 0000000..582171f
--- /dev/null
@@ -0,0 +1,63 @@
+From 0646487e2f1c1806c5c6f907566ee563ac5025fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 11:08:04 +0530
+Subject: ASoC: sophgo: return 1 on volume change in cv1800b_adc_volume_set()
+
+From: Surendra Singh Chouhan <kr494167@gmail.com>
+
+[ Upstream commit f47064c970d3e920a27435454c02df310695f6e1 ]
+
+cv1800b_adc_volume_set() serves as the .put callback for the "Internal
+I2S Capture Volume" control.
+
+ALSA mixer control callbacks must return 1 when the register value is
+modified, 0 if unchanged, or a negative error code on failure. Returning
+0 unconditionally causes ALSA core to assume the value was unchanged,
+suppressing SNDRV_CTL_EVENT_MASK_VALUE change notifications to userspace
+sound servers (e.g. PipeWire/PulseAudio).
+
+Fix this by comparing the new register value with the existing register
+value. If unchanged, return 0; otherwise, write the updated value and
+return 1.
+
+Fixes: 4cf8752a03e6 ("ASoC: sophgo: add CV1800B internal ADC codec driver")
+Signed-off-by: Surendra Singh Chouhan <kr494167@gmail.com>
+Link: https://patch.msgid.link/20260727053804.25599-1-kr494167@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/sophgo/cv1800b-sound-adc.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c
+index b66761156b993..bd93e261bdd1d 100644
+--- a/sound/soc/sophgo/cv1800b-sound-adc.c
++++ b/sound/soc/sophgo/cv1800b-sound-adc.c
+@@ -251,16 +251,22 @@ static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol,
+       u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24);
+       u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24);
+-      u32 val;
++      u32 val, old_val;
+       val = readl(priv->regs + CV1800B_RXADC_ANA0);
++      old_val = val;
++
+       val = u32_replace_bits(val, cv1800b_gains[v_left],
+                              REG_COMB_LEFT_VOLUME);
+       val = u32_replace_bits(val, cv1800b_gains[v_right],
+                              REG_COMB_RIGHT_VOLUME);
++
++      if (val == old_val)
++              return 0;
++
+       writel(val, priv->regs + CV1800B_RXADC_ANA0);
+-      return 0;
++      return 1;
+ }
+ static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0);
+-- 
+2.53.0
+
diff --git a/queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch b/queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch
new file mode 100644 (file)
index 0000000..0ed468c
--- /dev/null
@@ -0,0 +1,44 @@
+From 8b830e9af599911cb9fb72639a594f06accbcbbd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:16:16 +0800
+Subject: ASoC: tas2781: Use correct calibration data for SINEGAIN2 register
+
+From: wangdicheng <wangdicheng@kylinos.cn>
+
+[ Upstream commit dd88cf6273de61f2f7206c2066af97798dbb38b0 ]
+
+The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[]
+rather than t->sin_gn2[], causing the second pilot tone gain
+calibration to be programmed with the wrong register address.
+
+These are distinct fields in struct fct_param_address and are
+populated from separate firmware parameters by the parser in
+tas2781-fmwlib.c.
+
+Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies")
+Signed-off-by: wangdicheng <wangdicheng@kylinos.cn>
+Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/tas2781-i2c.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
+index a78a8f9b98338..775c3767bf3a8 100644
+--- a/sound/soc/codecs/tas2781-i2c.c
++++ b/sound/soc/codecs/tas2781-i2c.c
+@@ -1308,8 +1308,8 @@ static void cali_reg_update(struct bulk_reg_val *p,
+                               t->sin_gn[2]);
+                       break;
+               case TAS2781_PRM_SINEGAIN2_REG:
+-                      reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+-                              t->sin_gn[2]);
++                      reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1],
++                              t->sin_gn2[2]);
+                       break;
+               default:
+                       reg = 0;
+-- 
+2.53.0
+
diff --git a/queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch
new file mode 100644 (file)
index 0000000..15bdd3f
--- /dev/null
@@ -0,0 +1,66 @@
+From 1037e819f922161518086b14489e5777cc8be47d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:05 -0400
+Subject: assoc_array: trim the final shortcut word using the current chunk end
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
+
+assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
+word that contains skip_to_level, gated on
+round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
+
+That guard is wrong in two opposite ways:
+
+ - When sc_level is word-aligned (every word after the first) round_up()
+   is a no-op, so the guard is sc_level > skip_to_level and never fires for
+   the word that holds skip_to_level.  A shortcut that spans more than one
+   word and ends in the middle of its last word leaves that word untrimmed,
+   and its stale high bits leak into the dissimilarity word and can steer
+   the walk down the wrong descendant.
+
+ - When sc_level is unaligned (the first word) and skip_to_level sits on
+   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
+   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
+   the whole dissimilarity word and makes a differing shortcut compare
+   equal.
+
+Use the end of the chunk that contains sc_level instead:
+
+       skip_to_level < round_down(sc_level, CHUNK) + CHUNK
+
+For an aligned sc_level whose word holds skip_to_level this now fires (the
+first bug); for an unaligned sc_level with skip_to_level on the following
+boundary it does not, so shift is never 0 when the branch runs and the trim
+never clears the whole word.
+
+Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/assoc_array.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/assoc_array.c b/lib/assoc_array.c
+index bcc6e0a013eb8..b6c9723e12ced 100644
+--- a/lib/assoc_array.c
++++ b/lib/assoc_array.c
+@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
+               sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
+               dissimilarity = segments ^ sc_segments;
+-              if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
++              if (shortcut->skip_to_level < round_down(sc_level,
++                              ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
+                       /* Trim segments that are beyond the shortcut */
+                       int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
+                       dissimilarity &= ~(ULONG_MAX << shift);
+-- 
+2.53.0
+
diff --git a/queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
new file mode 100644 (file)
index 0000000..f27d3c0
--- /dev/null
@@ -0,0 +1,76 @@
+From 1638affa848653f9fcac82d18a7a7e83ea24f923 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 23:55:26 +0530
+Subject: ata: ahci_ceva: fix error paths in
+ ceva_ahci_platform_enable_resources()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
+
+On phy_init() failure the error path fallsthrough to disable_rsts, which
+deasserts the controller reset and then enters disable_phys calling
+phy_power_off() on PHYs that were never powered on. That corrupts the PHY
+power_count and triggers an extra runtime PM put.
+
+Use a separate exit_phys path that unwinds with phy_exit() only and falls
+through to disable_clks while the controller remains in reset.  Reserve
+phy_power_off() for the phy_power_on() failure path only, and skip
+masked-out ports in both unwind loops.
+
+On phy_power_on() failure re-assert the controller reset before disabling
+clocks and regulators, matching the teardown order used by
+ahci_platform_enable_resources() and ahci_platform_disable_resources().
+
+Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/ahci_ceva.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
+index 2d6a08c23d6ad..2961e53288f42 100644
+--- a/drivers/ata/ahci_ceva.c
++++ b/drivers/ata/ahci_ceva.c
+@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+               rc = phy_init(hpriv->phys[i]);
+               if (rc)
+-                      goto disable_rsts;
++                      goto exit_phys;
+       }
+       /* De-assert the controller reset */
+@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+       return 0;
+-disable_rsts:
+-      ahci_platform_deassert_rsts(hpriv);
+-
+ disable_phys:
+       while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
+               phy_power_off(hpriv->phys[i]);
+               phy_exit(hpriv->phys[i]);
+       }
++      ahci_platform_assert_rsts(hpriv);
++      goto disable_clks;
++
++exit_phys:
++      while (--i >= 0) {
++              if (ahci_ignore_port(hpriv, i))
++                      continue;
++
++              phy_exit(hpriv->phys[i]);
++      }
+ disable_clks:
+       ahci_platform_disable_clks(hpriv);
+-- 
+2.53.0
+
diff --git a/queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
new file mode 100644 (file)
index 0000000..2bf04b3
--- /dev/null
@@ -0,0 +1,44 @@
+From a44c8a2214cdbdb81d48ed2ff7473077dbc96dd9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 15:31:37 -0700
+Subject: ata: sata_mv: accept 1 or 2 resources in platform probe
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
+
+Board files in arch/arm/plat-orion, arch/arm/mach-dove,
+arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the
+"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ).
+Those devices are rejected with -EINVAL, so SATA no longer probes on
+legacy Marvell Orion/Kirkwood-style boards.
+
+Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2
+resources (legacy, IRQ supplied as a second resource) so both probing
+paths work.
+
+Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone")
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/sata_mv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
+index ffb396f61731f..1d8ca95fb7328 100644
+--- a/drivers/ata/sata_mv.c
++++ b/drivers/ata/sata_mv.c
+@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)
+       /*
+        * Simple resource validation ..
+        */
+-      if (unlikely(pdev->num_resources != 1)) {
++      if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
+               dev_err(&pdev->dev, "invalid number of resources\n");
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch
new file mode 100644 (file)
index 0000000..67c97d7
--- /dev/null
@@ -0,0 +1,40 @@
+From cda0a3385cf2f3a0e7a3bd0d9304d9e3c8396b74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 01:54:40 -0700
+Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV
+
+From: Zijun Hu <zijun.hu@oss.qualcomm.com>
+
+[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ]
+
+btintel_diagnostics() accesses tlv->val[0] without first validating
+that the diagnostics VSE is long enough to contain that field, so
+may cause reading data beyond the received frame.
+
+Fix by validating the length before access.
+
+Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support")
+Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bluetooth/btintel.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
+index 5e9cac090bd8f..bf567b7c5f00b 100644
+--- a/drivers/bluetooth/btintel.c
++++ b/drivers/bluetooth/btintel.c
+@@ -3771,6 +3771,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+       struct intel_tlv *tlv = (void *)&skb->data[5];
++      if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
++              goto recv_frame;
++
+       /* The first event is always an event type TLV */
+       if (tlv->type != INTEL_TLV_TYPE_ID)
+               goto recv_frame;
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
new file mode 100644 (file)
index 0000000..36a810e
--- /dev/null
@@ -0,0 +1,55 @@
+From c373769b704e3b579c2247073d2521f9bd5d190c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:17 +0300
+Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that.
+
+Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_conn.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index 924c7795e368a..fb96d019cf856 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -3168,6 +3168,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
+       return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
+ }
++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
++{
++      struct hci_conn *conn = data;
++
++      hci_conn_put(conn);
++}
++
+ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+ {
+       struct hci_dev *hdev = conn->hdev;
+@@ -3193,7 +3200,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+-      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
++      err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
++                                  abort_conn_destroy);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
new file mode 100644 (file)
index 0000000..91e2612
--- /dev/null
@@ -0,0 +1,43 @@
+From 89a5fe25ec8b9da48236eef63dd03031d14dd758 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:22 +0300
+Subject: Bluetooth: hci_sync: fix hci_conn_del() use in
+ hci_le_create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ]
+
+hci_conn_del() caller must hold hdev->lock, check the conn was not
+concurrently deleted, and usually inform socket the conn is going to be
+deleted.
+
+Use hci_abort_conn_sync() instead of calling hci_conn_del() without
+locks etc.
+
+Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 70ac18fe74448..0bb015da6f88c 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6672,7 +6672,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+               if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE &&
+                   !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
+-                      hci_conn_del(conn);
++                      conn->state = BT_OPEN;
++                      hci_abort_conn_sync(hdev, conn,
++                                          HCI_ERROR_REJ_LIMITED_RESOURCES);
+                       hci_conn_put(conn);
+                       return -EBUSY;
+               }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch
new file mode 100644 (file)
index 0000000..796020b
--- /dev/null
@@ -0,0 +1,103 @@
+From ea5fa13433e9bd5edcfef8a682619b6892bccaf3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:18 +0300
+Subject: Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 2f5d635ad5906b0235bc0c870e8beba3116e1e98 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task
+is running.
+
+Hold refcount to avoid that.
+
+Fixes: 881559af5f5c ("Bluetooth: hci_sync: Attempt to dequeue connection attempt")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 32 ++++++++++++++++++++++++--------
+ 1 file changed, 24 insertions(+), 8 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 42bbffba2172e..2cac69922353a 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -7067,12 +7067,23 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+       return err;
+ }
++static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
++                                            int err)
++{
++      struct hci_conn *conn = data;
++
++      hci_conn_put(conn);
++}
++
+ int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
+ {
+       int err;
+-      err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
+-                                    NULL);
++      err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
++                                    hci_conn_get(conn),
++                                    hci_acl_create_conn_sync_complete);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+@@ -7083,36 +7094,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
+       bt_dev_dbg(hdev, "err %d", err);
+       if (err == -ECANCELED)
+-              return;
++              goto done;
+       hci_dev_lock(hdev);
+       if (!hci_conn_valid(hdev, conn))
+-              goto done;
++              goto unlock;
+       if (!err) {
+               hci_connect_le_scan_cleanup(conn, 0x00);
+-              goto done;
++              goto unlock;
+       }
+       /* Check if connection is still pending */
+       if (conn != hci_lookup_le_connect(hdev))
+-              goto done;
++              goto unlock;
+       /* Flush to make sure we send create conn cancel command if needed */
+       flush_delayed_work(&conn->le_conn_timeout);
+       hci_conn_failed(conn, bt_status(err));
+-done:
++unlock:
+       hci_dev_unlock(hdev);
++done:
++      hci_conn_put(conn);
+ }
+ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
+ {
+       int err;
+-      err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
++      err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
++                                    hci_conn_get(conn),
+                                     create_le_conn_complete);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch
new file mode 100644 (file)
index 0000000..626dc6d
--- /dev/null
@@ -0,0 +1,62 @@
+From 6e7a89d05dbeab9afe15db80ef89a4dd9377de14 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:19 +0300
+Subject: Bluetooth: hci_sync: hold conn in hci_connect_big_sync() callback
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 56e78b670356caab0b607e8aad4cf819a1909d07 ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that. Handle NULL hcon, return 0 + do nothing to
+match the previous behavior.
+
+Fixes: 024421cf3992 ("Bluetooth: hci_conn: Fix not setting timeout for BIG Create Sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 2cac69922353a..2860779e6a791 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -7437,10 +7437,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
+       bt_dev_dbg(hdev, "err %d", err);
+       if (err == -ECANCELED)
+-              return;
++              goto done;
+-      if (hci_conn_valid(hdev, conn))
+-              clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
++      clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
++
++done:
++      hci_conn_put(conn);
+ }
+ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
+@@ -7492,8 +7494,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
+ {
+       int err;
+-      err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
++      if (!conn)
++              return 0;
++
++      err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
++                                    hci_conn_get(conn),
+                                     create_big_complete);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch
new file mode 100644 (file)
index 0000000..ada431b
--- /dev/null
@@ -0,0 +1,60 @@
+From 5bd6218bb74a63f1a00a0852a279dc084d840b7a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:20 +0300
+Subject: Bluetooth: hci_sync: hold conn in hci_connect_pa_sync() callback
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 44fc74069d8988f2825246f9401218e29de2c0ab ]
+
+There is theoretical UAF if the conn is freed while the hci_sync task is
+running.
+
+Hold refcount to avoid that.
+
+Fixes: 6d0417e4e1cf ("Bluetooth: hci_conn: Fix not setting conn_timeout for Broadcast Receiver")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 2860779e6a791..6c3dc910fdeba 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -7251,7 +7251,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
+       bt_dev_dbg(hdev, "err %d", err);
+       if (err == -ECANCELED)
+-              return;
++              goto done;
+       hci_dev_lock(hdev);
+@@ -7275,6 +7275,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
+ unlock:
+       hci_dev_unlock(hdev);
++done:
++      hci_conn_put(conn);
+ }
+ static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
+@@ -7425,8 +7427,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
+ {
+       int err;
+-      err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
++      err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
++                                    hci_conn_get(conn),
+                                     create_pa_complete);
++      if (err)
++              hci_conn_put(conn);
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch
new file mode 100644 (file)
index 0000000..16e65ff
--- /dev/null
@@ -0,0 +1,62 @@
+From 44cf060da682e227d36059cd9384ec2ab138ab3f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:21 +0300
+Subject: Bluetooth: hci_sync: hold conn in hci_past_sync() callback
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit abf9753edf3f88282c44a605f3945d8d4f8dd86c ]
+
+Avoids giving freed pointers to hci_conn_valid(), which kmalloc may have
+reused.
+
+Hold refcount to avoid that.
+
+Fixes: d3413703d5f8 ("Bluetooth: ISO: Add support to bind to trigger PAST")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 6c3dc910fdeba..70ac18fe74448 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -7521,6 +7521,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err)
+       bt_dev_dbg(hdev, "err %d", err);
++      hci_conn_put(past->conn);
++      hci_conn_put(past->le);
+       kfree(past);
+ }
+@@ -7585,8 +7587,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
+       if (!data)
+               return -ENOMEM;
+-      data->conn = conn;
+-      data->le = le;
++      data->conn = hci_conn_get(conn);
++      data->le = hci_conn_get(le);
+       if (conn->role == HCI_ROLE_MASTER)
+               err = hci_cmd_sync_queue_once(conn->hdev,
+@@ -7596,8 +7598,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
+               err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
+                                             data, past_complete);
+-      if (err)
++      if (err) {
++              hci_conn_put(data->conn);
++              hci_conn_put(data->le);
+               kfree(data);
++      }
+       return (err == -EEXIST) ? 0 : err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
new file mode 100644 (file)
index 0000000..016162d
--- /dev/null
@@ -0,0 +1,80 @@
+From 10974998169f87f40f6b734c795d3c75ba4fb469 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 12:59:23 +0300
+Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in
+ create_conn_sync
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ]
+
+hci_conn_get() without already held reference is data race against
+concurrent deletion.
+
+In previous patches, the refcount has been changed to be taken before
+starting the hci_sync task, so remove these extra get() + put() as they
+are not needed.
+
+Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/hci_sync.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 0bb015da6f88c..540da127ecea6 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -6656,11 +6656,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       bt_dev_dbg(hdev, "conn %p", conn);
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() at done.
+-       */
+-      hci_conn_get(conn);
+-
+       clear_bit(HCI_CONN_SCANNING, &conn->flags);
+       conn->state = BT_CONNECT;
+@@ -6675,7 +6670,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+                       conn->state = BT_OPEN;
+                       hci_abort_conn_sync(hdev, conn,
+                                           HCI_ERROR_REJ_LIMITED_RESOURCES);
+-                      hci_conn_put(conn);
+                       return -EBUSY;
+               }
+@@ -6773,7 +6767,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
+       /* Re-enable advertising after the connection attempt is finished. */
+       hci_resume_advertising_sync(hdev);
+-      hci_conn_put(conn);
+       return err;
+ }
+@@ -7048,11 +7041,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+       else
+               cp.role_switch = 0x00;
+-      /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+-       * clear_bit() below.
+-       */
+-      hci_conn_get(conn);
+-
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+@@ -7064,7 +7052,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
+                                      conn->conn_timeout, NULL);
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+-      hci_conn_put(conn);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
new file mode 100644 (file)
index 0000000..c9335e0
--- /dev/null
@@ -0,0 +1,188 @@
+From 6b764e6ec0f66c5302a4f397c3f3a992b99af25d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:31 +0300
+Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ]
+
+iso_sock_timeout() takes lock_sock, so sync disabling the timer while
+holding that lock may deadlock.
+
+iso_sock_timeout() may also run concurrently with iso_conn_del(), which
+leads to UAF
+
+        [Task 1]                      [Task hdev->workqueue]
+        iso_sock_timeout              iso_conn_del
+          iso_conn_hold_unless_zero     iso_chan_del
+                           `------------> iso_conn_put
+                                      caller frees hcon
+          iso_conn_put
+            iso_conn_free
+              conn->hcon->iso_data = NULL; /* UAF */
+
+Fix the deadlock by removing the disable from the lock_sock sections.
+Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn
+which may need to be freed in lock_sock section. Convert some of the
+clear_timer to disable_timer.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 60 ++++++++++++++++++++++-----------------------
+ 1 file changed, 29 insertions(+), 31 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 1ca3576357006..7e1dcaa22a5b2 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -36,8 +36,6 @@ struct iso_conn {
+       spinlock_t      lock;
+       struct sock     *sk;
+-      struct delayed_work     timeout_work;
+-
+       struct sk_buff  *rx_skb;
+       __u32           rx_len;
+       __u16           tx_sn;
+@@ -80,6 +78,7 @@ struct iso_pinfo {
+       __u8                    base_len;
+       __u8                    base[BASE_MAX_LENGTH];
+       struct iso_conn         *conn;
++      struct delayed_work     timeout_work;
+ };
+ static struct bt_iso_qos default_qos;
+@@ -117,9 +116,6 @@ static void iso_conn_free(struct kref *ref)
+                       hci_conn_drop(conn->hcon);
+       }
+-      /* Ensure no more work items will run since hci_conn has been dropped */
+-      disable_delayed_work_sync(&conn->timeout_work);
+-
+       kfree_skb(conn->rx_skb);
+       kfree(conn);
+@@ -160,48 +156,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
+ static void iso_sock_timeout(struct work_struct *work)
+ {
+-      struct iso_conn *conn = container_of(work, struct iso_conn,
+-                                           timeout_work.work);
+-      struct sock *sk;
+-
+-      conn = iso_conn_hold_unless_zero(conn);
+-      if (!conn)
+-              return;
+-
+-      iso_conn_lock(conn);
+-      sk = iso_sock_hold(conn);
+-      iso_conn_unlock(conn);
+-      iso_conn_put(conn);
+-
+-      if (!sk)
+-              return;
++      struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
++                                          timeout_work.work);
++      struct sock *sk = &pi->bt.sk;
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+       lock_sock(sk);
+-      sk->sk_err = ETIMEDOUT;
+-      sk->sk_state_change(sk);
++      if (!sock_flag(sk, SOCK_ZAPPED)) {
++              sk->sk_err = ETIMEDOUT;
++              sk->sk_state_change(sk);
++      }
+       release_sock(sk);
+-      sock_put(sk);
+ }
+ static void iso_sock_set_timer(struct sock *sk, long timeout)
+ {
++      lockdep_assert(lockdep_sock_is_held(sk));
++
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++
+       if (!iso_pi(sk)->conn)
+               return;
+       BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
+-      schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
++      schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
+ }
+ static void iso_sock_clear_timer(struct sock *sk)
+ {
+-      if (!iso_pi(sk)->conn)
+-              return;
++      BT_DBG("sock %p state %d", sk, sk->sk_state);
++      cancel_delayed_work(&iso_pi(sk)->timeout_work);
++}
++
++static void iso_sock_disable_timer(struct sock *sk)
++{
++      lockdep_assert(!lockdep_sock_is_held(sk));
+       BT_DBG("sock %p state %d", sk, sk->sk_state);
+-      cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
++      disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
+ }
+ /* ---- ISO connections ---- */
+@@ -226,7 +219,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+       kref_init(&conn->ref);
+       spin_lock_init(&conn->lock);
+-      INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
+       hcon->iso_data = conn;
+       conn->hcon = hcon;
+@@ -291,8 +283,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+               return;
+       }
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
+       iso_sock_kill(sk);
+@@ -799,6 +792,8 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+@@ -894,8 +889,9 @@ static void __iso_sock_close(struct sock *sk)
+ /* Must be called on unlocked socket. */
+ static void iso_sock_close(struct sock *sk)
+ {
++      iso_sock_disable_timer(sk);
++
+       lock_sock(sk);
+-      iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+ }
+@@ -964,6 +960,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
+       iso_pi(sk)->qos = default_qos;
+       iso_pi(sk)->sync_handle = -1;
++      INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
++
+       bt_sock_link(&iso_sk_list, sk);
+       return sk;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
new file mode 100644 (file)
index 0000000..ad65d3a
--- /dev/null
@@ -0,0 +1,40 @@
+From 3d0a5282240734913f27319a3318db5bbcf77579 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 17:53:33 +0300
+Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ]
+
+When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is
+necessary, otherwise later iso_conn_free() will UAF.
+
+Fix clearing of iso_data in iso_sock_disconn()
+
+Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on
+iso_sock_release() followed by hci_abort_conn_sync().
+
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 1d5ce87e6496e..6bbbbce4c5f27 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -836,6 +836,7 @@ static void iso_sock_disconn(struct sock *sk)
+       sk->sk_state = BT_DISCONN;
+       iso_conn_lock(iso_pi(sk)->conn);
+       hci_conn_drop(iso_pi(sk)->conn->hcon);
++      iso_pi(sk)->conn->hcon->iso_data = NULL;
+       iso_pi(sk)->conn->hcon = NULL;
+       iso_conn_unlock(iso_pi(sk)->conn);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch b/queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch
new file mode 100644 (file)
index 0000000..96bcee2
--- /dev/null
@@ -0,0 +1,109 @@
+From 795a94bfe3665cf2b4e908e27546ca8515f8ea2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:32 +0300
+Subject: Bluetooth: ISO: ensure no dangling hcon references in iso_conn
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb ]
+
+After iso_conn_del(), ISO sockets should not dereference the hcon any
+more.  Currently, clearing iso_conn::hcon relies on iso_conn_del()
+releasing the last reference to the iso_conn.
+
+Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to
+avoid more complex reasoning on races about who holds the last
+reference.
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: fdfde532ab1c ("Bluetooth: ISO: fix refcounting of iso_conn")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 24 +++++++++++++++++++++---
+ 1 file changed, 21 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 7e1dcaa22a5b2..31ceb1338dc23 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -262,6 +262,7 @@ static void iso_chan_del(struct sock *sk, int err)
+ }
+ static void iso_conn_del(struct hci_conn *hcon, int err)
++      __must_hold(&hcon->hdev->lock)
+ {
+       struct iso_conn *conn = hcon->iso_data;
+       struct sock *sk;
+@@ -276,11 +277,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       iso_conn_lock(conn);
+       sk = iso_sock_hold(conn);
+       iso_conn_unlock(conn);
+-      iso_conn_put(conn);
+       if (!sk) {
+               iso_conn_put(conn);
+-              return;
++              goto done;
+       }
+       iso_sock_disable_timer(sk);
+@@ -290,6 +290,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       release_sock(sk);
+       iso_sock_kill(sk);
+       sock_put(sk);
++
++done:
++      /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
++      iso_conn_lock(conn);
++      conn->hcon = NULL;
++      hcon->iso_data = NULL;
++      iso_conn_unlock(conn);
++
++      iso_conn_put(conn);
+ }
+ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+@@ -305,6 +314,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+               return -EBUSY;
+       }
++      if (!conn->hcon) {
++              BT_ERR("conn->hcon missing");
++              return -EIO;
++      }
++
+       iso_pi(sk)->conn = conn;
+       conn->sk = sk;
+       clear_bit(ISO_CONN_DROPPED, conn->flags);
+@@ -2499,6 +2513,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+ }
+ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
++      __must_hold(&hcon->hdev->lock)
+ {
+       if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
+           hcon->type != PA_LINK) {
+@@ -2510,8 +2525,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+                       struct hci_link *link, *t;
+                       list_for_each_entry_safe(link, t, &hcon->link_list,
+-                                               list)
++                                               list) {
++                              lockdep_assert_held(&link->conn->hdev->lock);
+                               iso_conn_del(link->conn, bt_to_errno(status));
++                      }
+                       return;
+               }
+@@ -2541,6 +2558,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+ }
+ static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
++      __must_hold(&hcon->hdev->lock)
+ {
+       if (hcon->type != CIS_LINK && hcon->type !=  BIS_LINK &&
+           hcon->type != PA_LINK)
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch b/queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch
new file mode 100644 (file)
index 0000000..c75a2ec
--- /dev/null
@@ -0,0 +1,107 @@
+From 43d82ac82a722b68053915c4407fb60de16d73ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:24 +0300
+Subject: Bluetooth: ISO: fix CONNECTED -> CLOSED transition on
+ shutdown/release
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 0786469ee242952008628ed0e2d386098e2065ab ]
+
+Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
+merged a version of the UAF fix that breaks releasing connected
+ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't
+be called when the hci_conn disconnects, and the ISO socket does not emit
+POLLHUP correctly.
+
+Fix by retaining full hci_conn <-> iso_conn association while in
+BT_DISCONNECT state, so that local disconnect via shutdown() follows
+similar ISO socket code path as remote disconnect.  Use a separate flag
+to track whether hci_conn_drop() is needed, instead of setting
+iso_conn::hcon = NULL
+
+In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED,
+in case hcon connects while its drop is pending.
+
+Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
+Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 26 ++++++++++++++++++++------
+ 1 file changed, 20 insertions(+), 6 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 6bbbbce4c5f27..f1399ddd6ad05 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = {
+ };
+ /* ---- ISO connections ---- */
++enum {
++      ISO_CONN_DROPPED,
++      __ISO_CONN_NUM_FLAGS
++};
++
+ struct iso_conn {
+       struct hci_conn *hcon;
++      DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
+       /* @lock: spinlock protecting changes to iso_conn fields */
+       spinlock_t      lock;
+@@ -106,7 +112,8 @@ static void iso_conn_free(struct kref *ref)
+       if (conn->hcon) {
+               conn->hcon->iso_data = NULL;
+-              hci_conn_drop(conn->hcon);
++              if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
++                      hci_conn_drop(conn->hcon);
+       }
+       /* Ensure no more work items will run since hci_conn has been dropped */
+@@ -305,6 +312,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+       iso_pi(sk)->conn = conn;
+       conn->sk = sk;
++      clear_bit(ISO_CONN_DROPPED, conn->flags);
+       if (parent)
+               bt_accept_enqueue(parent, sk, true);
+@@ -834,11 +842,8 @@ static void iso_sock_disconn(struct sock *sk)
+       }
+       sk->sk_state = BT_DISCONN;
+-      iso_conn_lock(iso_pi(sk)->conn);
+-      hci_conn_drop(iso_pi(sk)->conn->hcon);
+-      iso_pi(sk)->conn->hcon->iso_data = NULL;
+-      iso_pi(sk)->conn->hcon = NULL;
+-      iso_conn_unlock(iso_pi(sk)->conn);
++      if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
++              hci_conn_drop(iso_pi(sk)->conn->hcon);
+ }
+ static void __iso_sock_close(struct sock *sk)
+@@ -2041,9 +2046,18 @@ static void iso_sock_ready(struct sock *sk)
+               return;
+       lock_sock(sk);
++
++      switch (sk->sk_state) {
++      case BT_DISCONN:
++      case BT_CLOSED:
++              release_sock(sk);
++              return;
++      }
++
+       iso_sock_clear_timer(sk);
+       sk->sk_state = BT_CONNECTED;
+       sk->sk_state_change(sk);
++
+       release_sock(sk);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch
new file mode 100644 (file)
index 0000000..11cd5f2
--- /dev/null
@@ -0,0 +1,116 @@
+From 605ffa56c247cdfe98e6e225ea4ede1281d9a7f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:30 +0300
+Subject: Bluetooth: ISO: fix leaking sk after socket release
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ]
+
+iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+sock_flag(sk, SOCK_DEAD) for early return, but this is always true since
+sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket
+always leaks, iso_sock_destruct is never called.
+
+The socket reference also leaks when __iso_sock_close() does not set
+SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after
+zapping.
+
+Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for
+something else, and lock_sock to ensure iso_sock_kill() puts sk only
+after socket release only once. Release and iso_conn_del may run
+concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up
+after zapping.
+
+Remove call to iso_sock_kill() from iso_sock_close(), as it's generally
+no-op there.
+
+Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 22 ++++++++++++++++++----
+ 1 file changed, 18 insertions(+), 4 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 9d1ac27202664..1ca3576357006 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -61,6 +61,7 @@ static void iso_sock_kill(struct sock *sk);
+ enum {
+       BT_SK_BIG_SYNC,
+       BT_SK_PA_SYNC,
++      BT_SK_KILLED,
+ };
+ struct iso_pinfo {
+@@ -294,6 +295,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       iso_sock_clear_timer(sk);
+       iso_chan_del(sk, err);
+       release_sock(sk);
++      iso_sock_kill(sk);
+       sock_put(sk);
+ }
+@@ -797,24 +799,29 @@ static void iso_sock_cleanup_listen(struct sock *parent)
+  */
+ static void iso_sock_kill(struct sock *sk)
+ {
++      lock_sock(sk);
++
+       if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
+-          sock_flag(sk, SOCK_DEAD))
++          test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
++              release_sock(sk);
+               return;
++      }
+       BT_DBG("sk %p state %d", sk, sk->sk_state);
+       /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
+-      lock_sock(sk);
+       if (iso_pi(sk)->conn) {
+               iso_conn_lock(iso_pi(sk)->conn);
+               iso_pi(sk)->conn->sk = NULL;
+               iso_conn_unlock(iso_pi(sk)->conn);
+       }
+-      release_sock(sk);
+       /* Kill poor orphan */
+       bt_sock_unlink(&iso_sk_list, sk);
+       sock_set_flag(sk, SOCK_DEAD);
++      set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
++
++      release_sock(sk);
+       sock_put(sk);
+ }
+@@ -891,7 +898,6 @@ static void iso_sock_close(struct sock *sk)
+       iso_sock_clear_timer(sk);
+       __iso_sock_close(sk);
+       release_sock(sk);
+-      iso_sock_kill(sk);
+ }
+ static void iso_sock_init(struct sock *sk, struct sock *parent)
+@@ -2039,8 +2045,16 @@ static int iso_sock_release(struct socket *sock)
+               release_sock(sk);
+       }
++      /* Make sure sk is valid even if iso_conn_del() is concurrent */
++      sock_hold(sk);
++
++      lock_sock(sk);
+       sock_orphan(sk);
++      release_sock(sk);
++
+       iso_sock_kill(sk);
++
++      sock_put(sk);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch b/queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch
new file mode 100644 (file)
index 0000000..f7863d4
--- /dev/null
@@ -0,0 +1,232 @@
+From 4b971e237928d41cdb2aa7e4c4906703f7da43f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:34 +0300
+Subject: Bluetooth: ISO: fix race of kfree vs kref_get_unless_zero
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit af24e338bf5dafb80f42baa9a0b9e9b57b1c5d9c ]
+
+hci_conn::iso_data is accessed and modified without lock or RCU.
+This leads to a race
+
+    [Task hdev->workqueue]                     [Task 2]
+    iso_recv                                    iso_conn_put(conn)
+      conn = LOAD hcon->iso_data                  iso_conn_free(conn)
+      iso_conn_hold_unless_zero(conn)               hcon->iso_data = NULL
+                                                    kfree(conn)
+        kref_get_unless_zero(&conn->ref) /* UAF */
+
+and also to races in iso_conn_add() vs. iso_conn_free().
+
+Fix by adding spinlock hci_conn::proto_lock and using it to guard
+hci_conn::iso_data.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/bluetooth/hci_core.h |  4 +-
+ net/bluetooth/hci_conn.c         |  2 +
+ net/bluetooth/iso.c              | 64 ++++++++++++++++++++++++++------
+ 3 files changed, 58 insertions(+), 12 deletions(-)
+
+diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
+index 02ba1cba6b236..2c15c9d8dbf74 100644
+--- a/include/net/bluetooth/hci_core.h
++++ b/include/net/bluetooth/hci_core.h
+@@ -770,9 +770,11 @@ struct hci_conn {
+       struct dentry   *debugfs;
+       struct hci_dev  *hdev;
++
++      spinlock_t      proto_lock; /* lock guarding protocol data */
+       void            *l2cap_data;
+       void            *sco_data;
+-      void            *iso_data;
++      void            *iso_data __guarded_by(&proto_lock);
+       struct list_head link_list;
+       struct hci_conn *parent;
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index eba4a548bef52..924c7795e368a 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -1126,6 +1126,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
+       INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
+       INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
++      spin_lock_init(&conn->proto_lock);
++
+       atomic_set(&conn->refcnt, 0);
+       hci_dev_hold(hdev);
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 3b22193a9ec32..f946ef5f37b37 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -108,9 +108,16 @@ static void iso_conn_free(struct kref *ref)
+       BT_DBG("conn %p", conn);
+       if (conn->hcon) {
+-              conn->hcon->iso_data = NULL;
+-              if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
+-                      hci_conn_drop(conn->hcon);
++              spin_lock(&conn->hcon->proto_lock);
++
++              /* Check we are not racing with iso_conn_add */
++              if (conn->hcon->iso_data == conn) {
++                      conn->hcon->iso_data = NULL;
++                      if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
++                              hci_conn_drop(conn->hcon);
++              }
++
++              spin_unlock(&conn->hcon->proto_lock);
+       }
+       kfree_skb(conn->rx_skb);
+@@ -125,7 +132,21 @@ static void iso_conn_put(struct iso_conn *conn)
+       BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref));
++      /* The following race vs. iso_conn_del() is possible:
++       *
++       * 1. conn->hcon != NULL here
++       * 2. kref_put puts the last reference
++       * 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL
++       *    and returns immediately, so conn->hcon is not cleared
++       * 4. iso_conn_free() dereferences conn->hcon
++       *
++       * To avoid UAF in step 4, take RCU before decrementing the refcount.
++       */
++      rcu_read_lock();
++
+       kref_put(&conn->ref, iso_conn_free);
++
++      rcu_read_unlock();
+ }
+ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
+@@ -204,22 +225,28 @@ static void iso_sock_disable_timer(struct sock *sk)
+ /* ---- ISO connections ---- */
+ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
++      __must_hold(&hcon->hdev->lock)
+ {
+-      struct iso_conn *conn = hcon->iso_data;
++      struct iso_conn *conn;
++
++      spin_lock(&hcon->proto_lock);
+-      conn = iso_conn_hold_unless_zero(conn);
++      conn = iso_conn_hold_unless_zero(hcon->iso_data);
+       if (conn) {
+               if (!conn->hcon) {
+                       iso_conn_lock(conn);
+                       conn->hcon = hcon;
+                       iso_conn_unlock(conn);
+               }
++              spin_unlock(&hcon->proto_lock);
+               return conn;
+       }
+-      conn = kzalloc_obj(*conn);
+-      if (!conn)
++      conn = kzalloc_obj(*conn, GFP_ATOMIC);
++      if (!conn) {
++              spin_unlock(&hcon->proto_lock);
+               return NULL;
++      }
+       kref_init(&conn->ref);
+       spin_lock_init(&conn->lock);
+@@ -228,6 +255,8 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+       conn->hcon = hcon;
+       conn->tx_sn = 0;
++      spin_unlock(&hcon->proto_lock);
++
+       BT_DBG("hcon %p conn %p", hcon, conn);
+       return conn;
+@@ -268,10 +297,12 @@ static void iso_chan_del(struct sock *sk, int err)
+ static void iso_conn_del(struct hci_conn *hcon, int err)
+       __must_hold(&hcon->hdev->lock)
+ {
+-      struct iso_conn *conn = hcon->iso_data;
++      struct iso_conn *conn;
+       struct sock *sk;
+-      conn = iso_conn_hold_unless_zero(conn);
++      spin_lock(&hcon->proto_lock);
++      conn = iso_conn_hold_unless_zero(hcon->iso_data);
++      spin_unlock(&hcon->proto_lock);
+       if (!conn)
+               return;
+@@ -295,10 +326,12 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+ done:
+       /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
++      spin_lock(&hcon->proto_lock);
+       iso_conn_lock(conn);
+       conn->hcon = NULL;
+       hcon->iso_data = NULL;
+       iso_conn_unlock(conn);
++      spin_unlock(&hcon->proto_lock);
+       iso_conn_put(conn);
+ }
+@@ -420,6 +453,8 @@ static int iso_connect_bis(struct sock *sk)
+                       iso_pi(sk)->bc_sid = hcon->sid;
+       }
++      lockdep_assert_held(&hcon->hdev->lock);
++
+       conn = iso_conn_add(hcon);
+       if (!conn) {
+               hci_conn_drop(hcon);
+@@ -523,6 +558,8 @@ static int iso_connect_cis(struct sock *sk)
+               }
+       }
++      lockdep_assert_held(&hcon->hdev->lock);
++
+       conn = iso_conn_add(hcon);
+       if (!conn) {
+               hci_conn_drop(hcon);
+@@ -854,8 +891,8 @@ static void iso_sock_disconn(struct sock *sk)
+                */
+               if (bis_sk) {
+                       hcon->state = BT_OPEN;
+-                      hcon->iso_data = NULL;
+-                      iso_pi(sk)->conn->hcon = NULL;
++                      set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags);
++
+                       iso_sock_clear_timer(sk);
+                       iso_chan_del(sk, bt_to_errno(hcon->abort_reason));
+                       sock_put(bis_sk);
+@@ -1305,6 +1342,8 @@ static int iso_listen_bis(struct sock *sk)
+               goto unlock;
+       }
++      lockdep_assert_held(&hcon->hdev->lock);
++
+       conn = iso_conn_add(hcon);
+       if (!conn) {
+               hci_conn_drop(hcon);
+@@ -2590,7 +2629,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
+               return -ENOENT;
+       }
++      spin_lock(&hcon->proto_lock);
+       conn = iso_conn_hold_unless_zero(hcon->iso_data);
++      spin_unlock(&hcon->proto_lock);
++
+       hcon = NULL;
+       hci_dev_unlock(hdev);
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch b/queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch
new file mode 100644 (file)
index 0000000..f16daf5
--- /dev/null
@@ -0,0 +1,142 @@
+From b8bb6172e3523e8c28da6c36ef420092e094c238 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:33 +0300
+Subject: Bluetooth: ISO: fix refcounting of iso_conn
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ]
+
+iso_conn_del() and iso_chan_del() have a race that results to double-put
+of iso_conn:
+
+    [Task hdev->workqueue]         [Task 2]
+    iso_conn_del                   iso_chan_del
+      iso_conn_hold_unless_zero      iso_conn_lock
+      iso_conn_lock                  conn->sk = NULL
+                                     iso_conn_unlock
+      sk = iso_sock_hold(conn)  <---------´
+      if (!sk) iso_conn_put          iso_conn_put
+      iso_conn_put /* UAF */
+
+The extra put for !sk in iso_conn_del() is currently required since
+failing iso_chan_add() may leave iso_conn not associated with any sk.
+
+Fix by having iso_pi(sk)->conn own refcount when non-NULL, so
+iso_conn_del does not need to put it.  Adjust the iso_conn_add()
+refcounting so that conn is put if it does not get associated with an
+sk.
+
+Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 29 +++++++++++++++++------------
+ 1 file changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 31ceb1338dc23..3b22193a9ec32 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -107,9 +107,6 @@ static void iso_conn_free(struct kref *ref)
+       BT_DBG("conn %p", conn);
+-      if (conn->sk)
+-              iso_pi(conn->sk)->conn = NULL;
+-
+       if (conn->hcon) {
+               conn->hcon->iso_data = NULL;
+               if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
+@@ -144,6 +141,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
+       return conn;
+ }
++static struct iso_conn *iso_conn_hold(struct iso_conn *conn)
++{
++      BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref));
++
++      kref_get(&conn->ref);
++      return conn;
++}
++
+ static struct sock *iso_sock_hold(struct iso_conn *conn)
+ {
+       if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk))
+@@ -209,7 +214,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+                       conn->hcon = hcon;
+                       iso_conn_unlock(conn);
+               }
+-              iso_conn_put(conn);
+               return conn;
+       }
+@@ -278,10 +282,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
+       sk = iso_sock_hold(conn);
+       iso_conn_unlock(conn);
+-      if (!sk) {
+-              iso_conn_put(conn);
++      if (!sk)
+               goto done;
+-      }
+       iso_sock_disable_timer(sk);
+@@ -319,7 +321,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
+               return -EIO;
+       }
+-      iso_pi(sk)->conn = conn;
++      iso_pi(sk)->conn = iso_conn_hold(conn);
+       conn->sk = sk;
+       clear_bit(ISO_CONN_DROPPED, conn->flags);
+@@ -426,6 +428,7 @@ static int iso_connect_bis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
++      iso_conn_put(conn);
+       if (err)
+               goto unlock;
+@@ -528,6 +531,7 @@ static int iso_connect_cis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
++      iso_conn_put(conn);
+       if (err)
+               goto unlock;
+@@ -1309,10 +1313,9 @@ static int iso_listen_bis(struct sock *sk)
+       }
+       err = iso_chan_add(conn, sk, NULL);
+-      if (err) {
+-              hci_conn_drop(hcon);
++      iso_conn_put(conn);
++      if (err)
+               goto unlock;
+-      }
+ unlock:
+       release_sock(sk);
+@@ -2550,8 +2553,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+               struct iso_conn *conn;
+               conn = iso_conn_add(hcon);
+-              if (conn)
++              if (conn) {
+                       iso_conn_ready(conn);
++                      iso_conn_put(conn);
++              }
+       } else {
+               iso_conn_del(hcon, bt_to_errno(status));
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
new file mode 100644 (file)
index 0000000..b9f7e59
--- /dev/null
@@ -0,0 +1,38 @@
+From 4e44b3f29c80b7d07c36501d14536e1e989e264d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:27 +0300
+Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ]
+
+In iso.c check_bcast_qos(), missing bcast.timeout is not set to its
+default value, and appears typoed as bcast.sync_timeout.
+
+Fix the typo.
+
+Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 6fadaeb1f7e96..7e8f8a51eebd2 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1795,7 +1795,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
+               return false;
+       if (!qos->bcast.timeout)
+-              qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
++              qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
+       if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch b/queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch
new file mode 100644 (file)
index 0000000..9f3bc0f
--- /dev/null
@@ -0,0 +1,133 @@
+From e4294eeffd2afea156c18cb817c6511e32a6b11a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:29 +0300
+Subject: Bluetooth: ISO: hold sk properly in iso_conn_ready
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 0d255e63fcf3f13a570d7ac11678fa1164ac015c ]
+
+sk deref in iso_conn_ready must be done either under conn->lock, or
+holding a refcount, to avoid concurrent close. conn->sk is currently
+accessed without either:
+
+    [Task 1]            [Task 2]
+                        iso_sock_release
+    iso_conn_ready
+      sk = conn->sk
+                          lock_sock(sk)
+                            conn->sk = NULL
+      lock_sock(sk)
+                          release_sock(sk)
+                          iso_sock_kill(sk)
+       UAF on sk deref
+
+Fix possible UAF by holding sk refcount in iso_conn_ready().  Also
+recheck after lock_sock that the socket is still valid.  Adjust locking
+so conn->sk is cleared only under lock_sock.
+
+Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 06b6fec364485..9d1ac27202664 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -804,11 +804,13 @@ static void iso_sock_kill(struct sock *sk)
+       BT_DBG("sk %p state %d", sk, sk->sk_state);
+       /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
++      lock_sock(sk);
+       if (iso_pi(sk)->conn) {
+               iso_conn_lock(iso_pi(sk)->conn);
+               iso_pi(sk)->conn->sk = NULL;
+               iso_conn_unlock(iso_pi(sk)->conn);
+       }
++      release_sock(sk);
+       /* Kill poor orphan */
+       bt_sock_unlink(&iso_sk_list, sk);
+@@ -2046,23 +2048,17 @@ static void iso_sock_ready(struct sock *sk)
+ {
+       BT_DBG("sk %p", sk);
+-      if (!sk)
+-              return;
+-
+-      lock_sock(sk);
++      lockdep_assert(lockdep_sock_is_held(sk));
+       switch (sk->sk_state) {
+       case BT_DISCONN:
+       case BT_CLOSED:
+-              release_sock(sk);
+               return;
+       }
+       iso_sock_clear_timer(sk);
+       sk->sk_state = BT_CONNECTED;
+       sk->sk_state_change(sk);
+-
+-      release_sock(sk);
+ }
+ static bool iso_match_big(struct sock *sk, void *data)
+@@ -2092,7 +2088,7 @@ static bool iso_match_dst(struct sock *sk, void *data)
+ static void iso_conn_ready(struct iso_conn *conn)
+ {
+       struct sock *parent = NULL;
+-      struct sock *sk = conn->sk;
++      struct sock *sk;
+       struct hci_ev_le_big_sync_established *ev = NULL;
+       struct hci_ev_le_pa_sync_established *ev2 = NULL;
+       struct hci_ev_le_per_adv_report *ev3 = NULL;
+@@ -2101,7 +2097,22 @@ static void iso_conn_ready(struct iso_conn *conn)
+       BT_DBG("conn %p", conn);
++      iso_conn_lock(conn);
++      sk = iso_sock_hold(conn);
++      iso_conn_unlock(conn);
++
+       if (sk) {
++              lock_sock(sk);
++
++              /* conn->sk may have become NULL if racing with sk close, but
++               * due to held hdev->lock, it can't become different sk.
++               */
++              if (!conn->sk) {
++                      release_sock(sk);
++                      sock_put(sk);
++                      return;
++              }
++
+               /* Attempt to update source address in case of BIS Sender if
+                * the advertisement is using a random address.
+                */
+@@ -2114,14 +2125,15 @@ static void iso_conn_ready(struct iso_conn *conn)
+                       adv = hci_find_adv_instance(bis->hdev,
+                                                   bis->iso_qos.bcast.bis);
+                       if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
+-                              lock_sock(sk);
+                               iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
+                               bacpy(&iso_pi(sk)->src, &adv->random_addr);
+-                              release_sock(sk);
+                       }
+               }
+-              iso_sock_ready(conn->sk);
++              iso_sock_ready(sk);
++
++              release_sock(sk);
++              sock_put(sk);
+       } else {
+               hcon = conn->hcon;
+               if (!hcon)
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch b/queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch
new file mode 100644 (file)
index 0000000..32c5ae6
--- /dev/null
@@ -0,0 +1,92 @@
+From 39a81e7836829a3f79218884ff9461a7aa6158d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:26 +0300
+Subject: Bluetooth: ISO: lock sk in iso_connect_ind
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 4311fd6f429065a8ba208660360a895627a00cf3 ]
+
+Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the
+"ev3" part of iso_connect_ind.  It may also be NULL if socket has
+transitioned away from the LISTEN/CONNECT states before locking.
+
+Fix by adding lock/release. Recheck hcon is valid after lock acquire
+where needed.
+
+Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 476c679791234..6fadaeb1f7e96 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -2368,7 +2368,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                       lock_sock(sk);
+-                      hcon = iso_pi(sk)->conn->hcon;
++                      hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
+                       iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
+                       if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
+@@ -2408,9 +2408,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+               if (!sk)
+                       goto done;
+-              hcon = iso_pi(sk)->conn->hcon;
++              lock_sock(sk);
++
++              hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
+               if (!hcon)
+-                      goto done;
++                      goto release3;
+               if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
+                       /* The controller was unable to retrieve PA data. */
+@@ -2418,12 +2420,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                              HCI_MAX_PER_AD_TOT_LEN);
+                       hcon->le_per_adv_data_len = 0;
+                       hcon->le_per_adv_data_offset = 0;
+-                      goto done;
++                      goto release3;
+               }
+               if (hcon->le_per_adv_data_offset + ev3->length >
+                   HCI_MAX_PER_AD_TOT_LEN)
+-                      goto done;
++                      goto release3;
+               memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
+                      ev3->data, ev3->length);
+@@ -2442,18 +2444,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
+                                                   &base_len);
+                       if (!base || base_len > BASE_MAX_LENGTH)
+-                              goto done;
++                              goto release3;
+-                      lock_sock(sk);
+                       memcpy(iso_pi(sk)->base, base, base_len);
+                       iso_pi(sk)->base_len = base_len;
+-                      release_sock(sk);
+               } else {
+                       /* This is a PA data fragment. Keep pa_data_len set to 0
+                        * until all data has been reassembled.
+                        */
+                       hcon->le_per_adv_data_len = 0;
+               }
++
++release3:
++              release_sock(sk);
+       } else {
+               sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
+                                 BT_LISTEN, iso_match_dst, BDADDR_ANY);
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch b/queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch
new file mode 100644 (file)
index 0000000..9b0a3e3
--- /dev/null
@@ -0,0 +1,46 @@
+From c8e998290bdf0f8f2baa32c4daa67d90a6d0710f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:25 +0300
+Subject: Bluetooth: ISO: lock sk in iso_sock_getname
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 ]
+
+Accessing iso_pi(sk)->conn requires lock_sock, which is not held here.
+
+Fix by adding the lock/release.
+
+Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index f1399ddd6ad05..476c679791234 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1471,6 +1471,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
+       BT_DBG("sock %p, sk %p", sock, sk);
++      lock_sock(sk);
++
+       addr->sa_family = AF_BLUETOOTH;
+       if (peer) {
+@@ -1492,6 +1494,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
+               sa->iso_bdaddr_type = iso_pi(sk)->src_type;
+       }
++      release_sock(sk);
++
+       return len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
new file mode 100644 (file)
index 0000000..c4c8ecb
--- /dev/null
@@ -0,0 +1,49 @@
+From 5f8c62de1c694e3969e15fec3e02462ad770bc4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:20:28 +0300
+Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis()
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ]
+
+iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before
+validating the BIS values, so it's possible to end up with bc_num_bis
+inconsistent.
+
+Assign to iso_pi(sk)->bc_num_bis only after validation.
+
+Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket")
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/iso.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
+index 7e8f8a51eebd2..06b6fec364485 100644
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1038,15 +1038,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
+               goto done;
+       }
+-      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
+-
+-      for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
++      for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
+               if (sa->iso_bc->bc_bis[i] < 0x01 ||
+                   sa->iso_bc->bc_bis[i] > 0x1f) {
+                       err = -EINVAL;
+                       goto done;
+               }
++      iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
++
+       memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
+              iso_pi(sk)->bc_num_bis);
+-- 
+2.53.0
+
diff --git a/queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
new file mode 100644 (file)
index 0000000..0d4960b
--- /dev/null
@@ -0,0 +1,60 @@
+From 78f19c8e1d362e9e1d59241fc88312272412db5f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 14:48:45 +0800
+Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
+
+From: Jiale Yao <yaojiale02@163.com>
+
+[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
+
+l2cap_le_connect_rsp() obtains a channel via
+__l2cap_get_chan_by_ident() but neither holds a reference nor uses
+l2cap_chan_hold_unless_zero() before locking and operating on it.
+A concurrent l2cap_chan_del() triggered by a remote disconnect can
+free the channel between the lookup and l2cap_chan_lock(), causing
+a use-after-free.
+
+The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler
+l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero()
+to safely hold a reference, but l2cap_le_connect_rsp() was left
+unprotected.
+
+Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup
+and l2cap_chan_put() on the exit path, consistent with other L2CAP
+response handlers.
+
+Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request")
+Assisted-by: Claude:deepseek-v4-pro
+Signed-off-by: Jiale Yao <yaojiale02@163.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/l2cap_core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index 189085000c73b..6585f08fe4a14 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -4823,6 +4823,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       if (!chan)
+               return -EBADSLT;
++      chan = l2cap_chan_hold_unless_zero(chan);
++      if (!chan)
++              return -EBADSLT;
++
+       err = 0;
+       l2cap_chan_lock(chan);
+@@ -4868,6 +4872,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+       }
+       l2cap_chan_unlock(chan);
++      l2cap_chan_put(chan);
+       return err;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
new file mode 100644 (file)
index 0000000..602f237
--- /dev/null
@@ -0,0 +1,79 @@
+From ad790c7713dc3bb4767b9da9a3146690a78347c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 4 Jul 2026 17:58:56 +0930
+Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ]
+
+[BUG]
+The following script can lead to unexpected qgroup rescan failure:
+
+  # mkfs.btrfs -f -O quota $dev
+  # mount $dev $mnt
+  # mount -o remount,rescue=ibadroots $mnt
+    ^^^^^ This above command is expected to fail
+
+  # btrfs quota rescan -w $mnt
+    ^^^^^ The above qgroup rescan is not expected to fail
+
+  # btrfs qgroup show $mnt
+  WARNING: qgroup data inconsistent, rescan recommended
+  Qgroupid    Referenced    Exclusive   Path
+  --------    ----------    ---------   ----
+  0/5           16.00KiB     16.00KiB   <toplevel>
+
+The above short script will be converted to a proper fstests case.
+
+[CAUSE]
+Inside btrfs_reconfigure(), if either btrfs_check_options() or
+btrfs_check_features() failed, we will always have
+BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful
+remount.
+
+That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations,
+including:
+
+- Qgroup rescan
+- Auto defrag
+- Space reclaim
+
+[FIX]
+Change the error handling of btrfs_check_options() and
+btrfs_check_features() to goto restore label.
+
+Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context")
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.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>
+---
+ fs/btrfs/super.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index ba70d727622e8..ba0b31e5816b6 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -1518,12 +1518,14 @@ static int btrfs_reconfigure(struct fs_context *fc)
+       sync_filesystem(sb);
+       set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
+-      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags))
+-              return -EINVAL;
++      if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) {
++              ret = -EINVAL;
++              goto restore;
++      }
+       ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY));
+       if (ret < 0)
+-              return ret;
++              goto restore;
+       btrfs_ctx_to_info(fs_info, ctx);
+       btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags);
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch b/queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch
new file mode 100644 (file)
index 0000000..e6e09bc
--- /dev/null
@@ -0,0 +1,58 @@
+From 95c2f099645aa86ffcf3df794dc209a38496a39e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 13:12:51 +0930
+Subject: btrfs: raid56: fix an incorrect csum skip during scrub
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 330dcc553f282e8dc0b88c9495b4c296465364e1 ]
+
+Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all()
+helper") uses the new helper to replace the nested loop inside
+verify_bio_data_sectors(), which simplifies the code.
+
+However that also changed the behavior of "continue" when a block has no
+data checksum.
+
+Previously the "continue" would skip the old for() loop, which would also
+increase @total_sector_nr.
+
+Now the "continue" will skip the new btrfs_bio_for_each_block_all()
+loop, which doesn't update @total_sector_nr.
+
+This means if we hit a block that has no data checksum, we will skip all
+the remaining blocks no matter if they have data checksum.
+As @total_sector_nr will never be updated, and that test_bit() will
+always return false.
+
+Fix it by increasing @total_sector_nr before calling "continue".
+
+Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper")
+Reviewed-by: Daniel Vacek <neelx@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/raid56.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
+index 08ee8f316d96d..515ffa4b02114 100644
+--- a/fs/btrfs/raid56.c
++++ b/fs/btrfs/raid56.c
+@@ -1679,8 +1679,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
+                       continue;
+               /* No csum for this sector, skip to the next sector. */
+-              if (!test_bit(total_sector_nr, rbio->csum_bitmap))
++              if (!test_bit(total_sector_nr, rbio->csum_bitmap)) {
++                      total_sector_nr++;
+                       continue;
++              }
+               expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
+               btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch b/queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch
new file mode 100644 (file)
index 0000000..e460487
--- /dev/null
@@ -0,0 +1,137 @@
+From cf46fbbbf8d6b556d621d4e55a188ddce22677bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 16:50:08 +0800
+Subject: btrfs: skip global block reserve accounting for rescue mounts
+
+From: Dongjiang Zhu <zhudongjiang@fnnas.com>
+
+[ Upstream commit 51a0e8399858621442807a26057bcd1cd3ced046 ]
+
+[BUG]
+Mounting with rescue=ibadroots after corrupting the block group tree
+root triggers a NULL pointer dereference:
+
+  BUG: kernel NULL pointer dereference, address: 0000000000000100
+  RIP: 0010:btrfs_update_global_block_rsv+0x9d/0x1c0 [btrfs]
+  Call Trace:
+   fill_dummy_bgs+0xd4/0x120 [btrfs]
+   open_ctree+0xc6e/0x1ca0 [btrfs]
+   btrfs_get_tree+0x50d/0xa40 [btrfs]
+
+The same crash occurs with a corrupted raid stripe tree root, via
+btrfs_read_block_groups() instead of fill_dummy_bgs().
+
+[CAUSE]
+With rescue=ibadroots, btrfs_read_roots() allows the mount to continue
+when either root cannot be read, leaving the corresponding root pointer
+NULL while its on-disk feature bit remains set.
+
+btrfs_update_global_block_rsv() then dereferences the missing root based
+on the feature bit alone.
+
+[FIX]
+Rescue mounts are fully read-only and cannot start transactions, so the
+global reserve is never consumed. Under btrfs_is_full_ro(), mark the
+reserve as full and return before performing the accounting.
+
+And since we need to check if the fs is mount fully RO, export
+fs_is_full_ro() as btrfs_is_full_ro(), and move it to fs.h.
+
+Fixes: 8dbfc14fc736 ("btrfs: account block group tree when calculating global reserve size")
+Fixes: 515020900d44 ("btrfs: read raid stripe tree from disk")
+Suggested-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Dongjiang Zhu <zhudongjiang@fnnas.com>
+[ Squash the fs_is_full_ro() export commit into this one. ]
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/block-rsv.c | 19 +++++++++++++++++--
+ fs/btrfs/disk-io.c   | 11 +----------
+ fs/btrfs/fs.h        |  9 +++++++++
+ 3 files changed, 27 insertions(+), 12 deletions(-)
+
+diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c
+index 9efb3016ef116..c68a8f4b7d19c 100644
+--- a/fs/btrfs/block-rsv.c
++++ b/fs/btrfs/block-rsv.c
+@@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
+ void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info)
+ {
+       struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
+-      struct btrfs_space_info *sinfo = block_rsv->space_info;
++      struct btrfs_space_info *sinfo;
+       struct btrfs_root *root, *tmp;
+-      u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
+       unsigned int min_items = 1;
++      u64 num_bytes;
++
++      /*
++       * A full read-only mount (rescue options) cannot start transactions,
++       * so the global reserve is never consumed. Mark it as full and skip
++       * the accounting.
++       */
++      if (btrfs_is_full_ro(fs_info)) {
++              spin_lock(&block_rsv->lock);
++              block_rsv->full = true;
++              spin_unlock(&block_rsv->lock);
++              return;
++      }
++
++      sinfo = block_rsv->space_info;
++      num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
+       /*
+        * The global block rsv is based on the size of the extent tree, the
+diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
+index 833965b06f90e..7ca64f79451cb 100644
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -3267,15 +3267,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
+       return 0;
+ }
+-static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
+-{
+-      if (!sb_rdonly(fs_info->sb))
+-              return false;
+-      if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
+-              return true;
+-      return false;
+-}
+-
+ /*
+  * Try to wait for any metadata readahead, and invalidate all btree folios.
+  *
+@@ -3432,7 +3423,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
+               WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
+       /* If the fs has any rescue options, no transaction is allowed. */
+-      if (fs_is_full_ro(fs_info))
++      if (btrfs_is_full_ro(fs_info))
+               WRITE_ONCE(fs_info->fs_error, -EROFS);
+       /* Set up fs_info before parsing mount options */
+diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
+index a8aa086a4df86..0292246db614d 100644
+--- a/fs/btrfs/fs.h
++++ b/fs/btrfs/fs.h
+@@ -1146,6 +1146,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
+ #define btrfs_test_opt(fs_info, opt)  ((fs_info)->mount_opt & \
+                                        BTRFS_MOUNT_##opt)
++static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info)
++{
++      if (!sb_rdonly(fs_info->sb))
++              return false;
++      if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
++              return true;
++      return false;
++}
++
+ static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info)
+ {
+       return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags));
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch b/queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch
new file mode 100644 (file)
index 0000000..5a6e894
--- /dev/null
@@ -0,0 +1,183 @@
+From 8d847284b77c6a7befc6ee27925fcb1fff7a2062 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 10:37:23 +0930
+Subject: btrfs: warn about extent buffer that can not be released
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 83f7e52b7ed1c3e03b79123e20b6f6adf8d886bb ]
+
+When we unmount the fs or during mount failures, btrfs will call
+invalidate_inode_pages() to release all btree inode folios.
+
+However that function can return -EBUSY if any folios can not be
+invalidated.
+This can be caused by:
+
+- Some extent buffers are still held by btrfs
+  This is a logic error, as we should release all tree root nodes
+  during unmount and mount failure handling.
+
+- Some extent buffers are under readahead and haven't yet finished
+  These are much rarer but valid cases.
+  In that case we should wait for those extent buffers.
+
+Introduce a new helper invalidate_and_check_btree_folios() which will:
+
+- Call invalidate_inode_pages2() and catch its return value
+  If it returned 0 as expected, that's great and we can call it a day.
+
+- Otherwise go through each extent buffer in buffer_tree
+  Increase the ref by one first for the eb we're checking.
+  This is to ensure the eb won't be freed after the readahead is
+  finished.
+
+  For ebs that still have EXTENT_BUFFER_READING flag, wait for them to
+  finish first.
+
+  After waiting for the readahead, check the refs of the eb and if it's
+  still dirty.
+
+  If the eb ref count is greater than 2 (one for the buffer tree, one
+  held by us), it means we are still holding the extent buffer somewhere
+  else, which is a code bug.
+
+  If the eb is still dirty, it means a bug in transaction handling, e.g.
+  the bug fixed by patch "btrfs: only release the dirty pages io tree
+  after successful writes".
+
+  For either case, show a warning message about the eb, including its
+  bytenr, owner, refs and flags.
+  And if it's a debug build, also trigger WARN_ON_ONCE() so that fstests
+  can properly catch such situation.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=221270
+Reported-by: AHN SEOK-YOUNG <iamsyahn@gmail.com>
+CC: Teng Liu <27rabbitlt@gmail.com>
+Tested-by: Teng Liu <27rabbitlt@gmail.com>
+Reviewed-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 51a0e8399858 ("btrfs: skip global block reserve accounting for rescue mounts")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/disk-io.c   | 53 ++++++++++++++++++++++++++++++++++++++++++--
+ fs/btrfs/extent_io.c |  6 -----
+ fs/btrfs/extent_io.h |  6 +++++
+ 3 files changed, 57 insertions(+), 8 deletions(-)
+
+diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
+index ab92b35fa3cc1..833965b06f90e 100644
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -3276,6 +3276,55 @@ static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
+       return false;
+ }
++/*
++ * Try to wait for any metadata readahead, and invalidate all btree folios.
++ *
++ * If the invalidation failed, report any dirty/held extent buffers.
++ */
++static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info)
++{
++      unsigned long index = 0;
++      struct extent_buffer *eb;
++      int ret;
++
++      ret = invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
++      if (likely(ret == 0))
++              return;
++
++      /*
++       * Some btree pages can not be invalidated, this happens when some tree
++       * blocks are still held (either by readahead or some task is holding a ref).
++       */
++      rcu_read_lock();
++      xa_for_each(&fs_info->buffer_tree, index, eb) {
++              /* Increase the ref so that the eb won't disappear. */
++              if (!refcount_inc_not_zero(&eb->refs))
++                      continue;
++              rcu_read_unlock();
++
++              /* Wait for any readahead first. */
++              if (test_bit(EXTENT_BUFFER_READING, &eb->bflags))
++                      wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING,
++                                     TASK_UNINTERRUPTIBLE);
++              /*
++               * The refs threshold is 2, one held by us at the beginning
++               * of the loop, one for the ownership in the buffer tree.
++               */
++              if (unlikely(refcount_read(&eb->refs) > 2 || extent_buffer_under_io(eb))) {
++                      WARN_ON_ONCE(IS_ENABLED(CONFIG_BTRFS_DEBUG));
++                      btrfs_warn(fs_info,
++                      "unable to release extent buffer %llu owner %llu gen %llu refs %u flags 0x%lx",
++                                 eb->start, btrfs_header_owner(eb),
++                                 btrfs_header_generation(eb),
++                                 refcount_read(&eb->refs), eb->bflags);
++              }
++              free_extent_buffer(eb);
++              rcu_read_lock();
++      }
++      rcu_read_unlock();
++      invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
++}
++
+ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices)
+ {
+       u32 sectorsize;
+@@ -3706,7 +3755,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
+       if (fs_info->data_reloc_root)
+               btrfs_drop_and_free_fs_root(fs_info, fs_info->data_reloc_root);
+       free_root_pointers(fs_info, true);
+-      invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
++      invalidate_and_check_btree_folios(fs_info);
+ fail_sb_buffer:
+       btrfs_stop_all_workers(fs_info);
+@@ -4445,7 +4494,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
+        * We must make sure there is not any read request to
+        * submit after we stop all workers.
+        */
+-      invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
++      invalidate_and_check_btree_folios(fs_info);
+       btrfs_stop_all_workers(fs_info);
+       /*
+diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
+index f0bfa8a6218a7..741fd1d4ab2ea 100644
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -2877,12 +2877,6 @@ bool try_release_extent_mapping(struct folio *folio, gfp_t mask)
+       return try_release_extent_state(io_tree, folio);
+ }
+-static int extent_buffer_under_io(const struct extent_buffer *eb)
+-{
+-      return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
+-              test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
+-}
+-
+ static bool folio_range_has_eb(struct folio *folio)
+ {
+       struct btrfs_folio_state *bfs;
+diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
+index b310a5145cf69..7b4152387d886 100644
+--- a/fs/btrfs/extent_io.h
++++ b/fs/btrfs/extent_io.h
+@@ -327,6 +327,12 @@ static inline bool extent_buffer_uptodate(const struct extent_buffer *eb)
+       return test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
+ }
++static inline bool extent_buffer_under_io(const struct extent_buffer *eb)
++{
++      return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
++              test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
++}
++
+ int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
+                        unsigned long start, unsigned long len);
+ void read_extent_buffer(const struct extent_buffer *eb, void *dst,
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
new file mode 100644 (file)
index 0000000..483abde
--- /dev/null
@@ -0,0 +1,77 @@
+From b8aa38554846e42a97d81e559ff05990ba129129 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:40 +0200
+Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction
+ commit
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ]
+
+When writing out metadata extent buffers in a zoned filesystem,
+btree_writepages() holds fs_info->zoned_meta_io_lock across the whole
+writeback loop, including the call to btrfs_check_meta_write_pointer() ->
+check_bg_is_active().
+
+For the tree-log block group, check_bg_is_active() may fail to activate
+the zone and fall back to btrfs_zone_finish_one_bg() to free an active
+zone. That path waits for the running transaction to commit while still
+holding zoned_meta_io_lock, but the committer needs that same lock to
+write out the tree extents, so the two tasks deadlock:
+
+  Task A (kworker, metadata writeback)      Task B (fsstress, transaction commit)
+  ------------------------------------      -------------------------------------
+  wb_workfn()                               btrfs_commit_transaction(T)
+   btree_writepages()                        btrfs_write_and_wait_transaction()
+    btrfs_zoned_meta_io_lock()                btrfs_write_marked_extents()
+    btrfs_check_meta_write_pointer()           btree_writepages()
+     check_bg_is_active() [treelog_bg]          btrfs_zoned_meta_io_lock()
+      btrfs_zone_finish_one_bg()               <blocks on zoned_meta_io_lock,
+       btrfs_zone_finish()                      held by Task A>
+        do_zone_finish()
+         btrfs_inc_block_group_ro()
+          btrfs_wait_for_commit()
+           <blocks waiting for commit
+            of transaction T, done by
+            Task B>
+
+The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock
+around do_zone_finish() for this exact reason. Do the same in the tree-log
+branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire
+it afterwards. The lock only protects fs_info->active_{meta,system}_bg,
+which this branch does not touch, and ctx->zoned_bg keeps a reference to
+the block group across the unlock, so nothing is lost while the lock
+is dropped.
+
+This hang occasionally reproduces with fstests generic/475 on a zoned
+btrfs filesystem.
+
+Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index 0d590e81f3259..a85fc150c97b5 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -2188,7 +2188,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx,
+       if (fs_info->treelog_bg == block_group->start) {
+               if (!btrfs_zone_activate(block_group)) {
+-                      int ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      int ret_fin;
++
++                      btrfs_zoned_meta_io_unlock(fs_info);
++                      ret_fin = btrfs_zone_finish_one_bg(fs_info);
++                      btrfs_zoned_meta_io_lock(fs_info);
+                       if (ret_fin != 1 || !btrfs_zone_activate(block_group))
+                               return false;
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch b/queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch
new file mode 100644 (file)
index 0000000..98f88b8
--- /dev/null
@@ -0,0 +1,56 @@
+From c95574819aef191e8285da4bd2a8b1f28dd5d974 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 07:54:45 +0200
+Subject: btrfs: zoned: reset meta_write_pointer on zone reset
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit 5fabb1cf25d723274009d7b759545fd59f230c9d ]
+
+btrfs_reset_unused_block_groups() resets a block group's zone and sets
+alloc_offset back to 0 so the space can be reused, but it leaves
+meta_write_pointer pointing at the previous end of the zone.
+
+Once the block group is reactivated and reused for metadata, newly
+allocated tree blocks live before that stale write pointer.
+btrfs_check_meta_write_pointer() then sees them behind the write pointer,
+so they can never be written out in sequential order: the dirty extent
+buffers are stranded and pin their btree_inode folios until unmount.
+
+Reset meta_write_pointer back to the start of the block group for
+metadata and system block groups.
+
+Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting")
+Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index a85fc150c97b5..6963e9068a565 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -3196,6 +3196,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num
+               reclaimed = bg->alloc_offset;
+               bg->zone_unusable = bg->length - bg->zone_capacity;
+               bg->alloc_offset = 0;
++              /*
++               * The zone was just reset to empty, so alloc_offset went back to
++               * the start of the zone. For metadata/system block groups the
++               * write pointer must follow it back to the start of the zone;
++               * otherwise it stays stale at the previous (finished) zone end,
++               * and metadata written into the reused zone would sit behind the
++               * write pointer, could never be written out in sequential order,
++               * and would be stranded (pinning its folio) until unmount.
++               */
++              if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM))
++                      bg->meta_write_pointer = bg->start;
+               /*
+                * This holds because we currently reset fully used then freed
+                * block group.
+-- 
+2.53.0
+
diff --git a/queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch b/queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch
new file mode 100644 (file)
index 0000000..3a6675b
--- /dev/null
@@ -0,0 +1,56 @@
+From ac03bea00a34bb7e3c31f5a2558ebe32c331538c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 10:23:12 +0200
+Subject: btrfs: zoned: skip fully truncated ordered extents at zone finish
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ Upstream commit ab602da96a915d42dcb1b0b322e8daea0f71b51f ]
+
+A fully truncated ordered extent (truncated_len == 0) wrote no data, so its
+->csum_list is empty and btrfs_finish_ordered_zoned() trips:
+
+  assertion failed: !list_empty(&ordered->csum_list), in fs/btrfs/zoned.c:2141
+
+Since commit 66ff4d366e7e a short or cancelled direct IO write finishes the
+unsubmitted ordered extent as truncated with uptodate = true instead of
+setting BTRFS_ORDERED_IOERR, so it now reaches btrfs_finish_ordered_zoned()
+rather than being skipped by the IOERR check in btrfs_finish_ordered_io().
+generic/208 hits this on a zoned filesystem.
+
+Return early for these, like the BTRFS_ORDERED_PREALLOC case; there is no
+zone append result to record and btrfs_finish_one_ordered() skips them too.
+
+Fixes: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write")
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/zoned.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
+index 6963e9068a565..e9d72401d328f 100644
+--- a/fs/btrfs/zoned.c
++++ b/fs/btrfs/zoned.c
+@@ -2136,6 +2136,16 @@ void btrfs_finish_ordered_zoned(struct btrfs_ordered_extent *ordered)
+       if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags))
+               return;
++      /*
++       * A fully truncated ordered extent wrote no data and so has
++       * no zone append result to record.
++       */
++      if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags) &&
++          ordered->truncated_len == 0) {
++              ASSERT(list_empty(&ordered->csum_list));
++              return;
++      }
++
+       ASSERT(!list_empty(&ordered->csum_list));
+       sum = list_first_entry(&ordered->csum_list, struct btrfs_ordered_sum, list);
+       logical = sum->logical;
+-- 
+2.53.0
+
diff --git a/queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch
new file mode 100644 (file)
index 0000000..caf9708
--- /dev/null
@@ -0,0 +1,57 @@
+From f76297ebfeddac4fa6236e472524cc0ba3d673c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 16:56:56 +0800
+Subject: can: isotp: check register_netdevice_notifier() error in module init
+
+From: Minhong He <heminhong@kylinos.cn>
+
+[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
+
+Register the netdevice notifier before can_proto_register() and check the
+return value. If protocol registration fails, unregister the notifier
+before returning the error.
+
+Align isotp_module_init() with the reordering already done for raw.c
+(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and
+bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization
+in bcm_module_init()")).
+
+Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier")
+Signed-off-by: Minhong He <heminhong@kylinos.cn>
+Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/can/isotp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/net/can/isotp.c b/net/can/isotp.c
+index 54becaf6898f1..ae6260e98a7a6 100644
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -1907,13 +1907,18 @@ static __init int isotp_module_init(void)
+       pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
++      err = register_netdevice_notifier(&canisotp_notifier);
++      if (err)
++              return err;
++
+       err = can_proto_register(&isotp_can_proto);
+-      if (err < 0)
++      if (err < 0) {
+               pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
+-      else
+-              register_netdevice_notifier(&canisotp_notifier);
++              unregister_netdevice_notifier(&canisotp_notifier);
++              return err;
++      }
+-      return err;
++      return 0;
+ }
+ static __exit void isotp_module_exit(void)
+-- 
+2.53.0
+
diff --git a/queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch b/queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch
new file mode 100644 (file)
index 0000000..22b16cb
--- /dev/null
@@ -0,0 +1,178 @@
+From c7cc6621c2c76b74ac60980ad27a81a938df674f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Apr 2026 16:54:52 -0400
+Subject: dmaengine: idxd: fix double free of wq, engine, and group structs
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit ec2d428b2e32dd157de8f86a86dd85c5b2c8f45c ]
+
+The release callbacks for wq, engine, and group devices
+(idxd_conf_wq_release, idxd_conf_engine_release,
+idxd_conf_group_release) each call kfree() on the enclosing struct.
+The setup error paths and cleanup functions also call kfree()
+explicitly after put_device(), producing a double free whenever
+put_device() drops the reference count to zero and fires the release.
+
+In the setup functions, device_initialize() is called before
+device_add(), so the reference count is exactly 1 at the error sites.
+put_device() unconditionally fires the release, which frees the struct;
+the subsequent explicit kfree() then operates on freed memory.
+
+For idxd_setup_wqs(), the wq release callback also owns opcap_bmap
+and wqcfg. The error unwind additionally freed those fields explicitly
+before calling put_device(), causing further double frees on both.
+
+Remove the redundant explicit kfree() calls from all setup error paths
+and cleanup functions for wq, engine, and group structs, delegating
+sole ownership of those allocations to the release callbacks.
+
+Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime")
+Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime")
+Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260415205452.67155-1-dbgh9129@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/idxd/init.c | 36 +++++-------------------------------
+ 1 file changed, 5 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
+index f1cfc7790d950..4b827a3297564 100644
+--- a/drivers/dma/idxd/init.c
++++ b/drivers/dma/idxd/init.c
+@@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd)
+ static void idxd_clean_wqs(struct idxd_device *idxd)
+ {
+-      struct idxd_wq *wq;
+       struct device *conf_dev;
+       int i;
+       for (i = 0; i < idxd->max_wqs; i++) {
+-              wq = idxd->wqs[i];
+-              if (idxd->hw.wq_cap.op_config)
+-                      bitmap_free(wq->opcap_bmap);
+-              kfree(wq->wqcfg);
+-              conf_dev = wq_confdev(wq);
++              conf_dev = wq_confdev(idxd->wqs[i]);
+               put_device(conf_dev);
+-              kfree(wq);
+       }
+       bitmap_free(idxd->wq_enable_map);
+       kfree(idxd->wqs);
+@@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
+               rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
+               if (rc < 0) {
+                       put_device(conf_dev);
+-                      kfree(wq);
+                       goto err_unwind;
+               }
+@@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
+               wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
+               if (!wq->wqcfg) {
+                       put_device(conf_dev);
+-                      kfree(wq);
+                       rc = -ENOMEM;
+                       goto err_unwind;
+               }
+@@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
+               if (idxd->hw.wq_cap.op_config) {
+                       wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
+                       if (!wq->opcap_bmap) {
+-                              kfree(wq->wqcfg);
+                               put_device(conf_dev);
+-                              kfree(wq);
+                               rc = -ENOMEM;
+                               goto err_unwind;
+                       }
+@@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
+ err_unwind:
+       while (--i >= 0) {
+-              wq = idxd->wqs[i];
+-              if (idxd->hw.wq_cap.op_config)
+-                      bitmap_free(wq->opcap_bmap);
+-              kfree(wq->wqcfg);
+-              conf_dev = wq_confdev(wq);
++              conf_dev = wq_confdev(idxd->wqs[i]);
+               put_device(conf_dev);
+-              kfree(wq);
+       }
+       bitmap_free(idxd->wq_enable_map);
+@@ -270,15 +255,12 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
+ static void idxd_clean_engines(struct idxd_device *idxd)
+ {
+-      struct idxd_engine *engine;
+       struct device *conf_dev;
+       int i;
+       for (i = 0; i < idxd->max_engines; i++) {
+-              engine = idxd->engines[i];
+-              conf_dev = engine_confdev(engine);
++              conf_dev = engine_confdev(idxd->engines[i]);
+               put_device(conf_dev);
+-              kfree(engine);
+       }
+       kfree(idxd->engines);
+ }
+@@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd)
+               rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id);
+               if (rc < 0) {
+                       put_device(conf_dev);
+-                      kfree(engine);
+                       goto err;
+               }
+@@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd)
+  err:
+       while (--i >= 0) {
+-              engine = idxd->engines[i];
+-              conf_dev = engine_confdev(engine);
++              conf_dev = engine_confdev(idxd->engines[i]);
+               put_device(conf_dev);
+-              kfree(engine);
+       }
+       kfree(idxd->engines);
+@@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd)
+ static void idxd_clean_groups(struct idxd_device *idxd)
+ {
+-      struct idxd_group *group;
+       int i;
+       for (i = 0; i < idxd->max_groups; i++) {
+-              group = idxd->groups[i];
+-              put_device(group_confdev(group));
+-              kfree(group);
++              put_device(group_confdev(idxd->groups[i]));
+       }
+       kfree(idxd->groups);
+ }
+@@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
+               rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id);
+               if (rc < 0) {
+                       put_device(conf_dev);
+-                      kfree(group);
+                       goto err;
+               }
+@@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
+       while (--i >= 0) {
+               group = idxd->groups[i];
+               put_device(group_confdev(group));
+-              kfree(group);
+       }
+       kfree(idxd->groups);
+-- 
+2.53.0
+
diff --git a/queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
new file mode 100644 (file)
index 0000000..d7c6e51
--- /dev/null
@@ -0,0 +1,65 @@
+From 07e4b21dfc4e87f46404ff66c17c49267710f5ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 May 2026 10:15:50 -0400
+Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open()
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ]
+
+The failed_dev_add and failed_dev_name paths drop the file-device
+reference while wq->wq_lock is still held. If put_device(fdev) drops the
+last reference, idxd_file_dev_release() runs synchronously and tries to
+take wq->wq_lock again, deadlocking.
+
+Those paths also fall through into the later ctx cleanup labels even
+though idxd_file_dev_release() owns that cleanup and frees ctx. This can
+make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context.
+
+Move idxd_wq_get() before file-device setup can fail, since the release
+callback always calls idxd_wq_put(). Then unlock wq->wq_lock before
+put_device(fdev) and return directly from the file-device setup failure
+path, leaving ctx cleanup to the release callback.
+
+Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/idxd/cdev.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
+index 0366c7cf35020..82b07cf942ef8 100644
+--- a/drivers/dma/idxd/cdev.c
++++ b/drivers/dma/idxd/cdev.c
+@@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+       fdev->parent = cdev_dev(idxd_cdev);
+       fdev->bus = &dsa_bus_type;
+       fdev->type = &idxd_cdev_file_type;
++      idxd_wq_get(wq);
+       rc = dev_set_name(fdev, "file%d", ctx->id);
+       if (rc < 0) {
+@@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
+               goto failed_dev_add;
+       }
+-      idxd_wq_get(wq);
+       mutex_unlock(&wq->wq_lock);
+       return 0;
+ failed_dev_add:
+ failed_dev_name:
++      mutex_unlock(&wq->wq_lock);
+       put_device(fdev);
++      return rc;
+ failed_ida:
+ failed_set_pasid:
+       if (device_user_pasid_enabled(idxd))
+-- 
+2.53.0
+
diff --git a/queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
new file mode 100644 (file)
index 0000000..0658c07
--- /dev/null
@@ -0,0 +1,60 @@
+From cc3a8ba039b951dbd8adb0b1282bb5f7a69034b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2026 12:57:33 +0800
+Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
+
+When terminating DMA transfers, active descriptors are not properly
+reclaimed. Only cyclic descriptors were handled, leaving non-cyclic
+descriptors and their LLI chains to be permanently leaked.
+
+Fix by using vchan_terminate_vdesc() which handles both cyclic and
+non-cyclic descriptors by adding them to desc_terminated queue for
+proper cleanup.
+
+Add pchan->desc != pchan->done check to prevent double-adding completed
+descriptors, which would corrupt the list.
+
+Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
+Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
+Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
+Suggested-by: Frank Li <Frank.li@oss.nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/sun6i-dma.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index a9a254dbf8cb9..f47a326dd7ffa 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -945,16 +945,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+       spin_lock_irqsave(&vchan->vc.lock, flags);
+-      if (vchan->cyclic) {
+-              vchan->cyclic = false;
+-              if (pchan && pchan->desc) {
+-                      struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      struct virt_dma_chan *vc = &vchan->vc;
++      if (pchan && pchan->desc && pchan->desc != pchan->done) {
++              struct virt_dma_desc *vd = &pchan->desc->vd;
+-                      list_add_tail(&vd->node, &vc->desc_completed);
+-              }
++              vchan_terminate_vdesc(vd);
+       }
++      vchan->cyclic = false;
+       vchan_get_all_descriptors(&vchan->vc, &head);
+       if (pchan) {
+-- 
+2.53.0
+
diff --git a/queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch b/queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch
new file mode 100644 (file)
index 0000000..ecfbc79
--- /dev/null
@@ -0,0 +1,47 @@
+From 5f5576dc7be79bef833c320479a285634d0c9e4d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 17 Mar 2026 08:32:52 +0000
+Subject: dmaengine: switchtec-dma: fix FIELD_GET misuse when programming SE
+ threshold
+
+From: David Carlier <devnexen@gmail.com>
+
+[ Upstream commit 9d12eb98582fec2578d17e025b13740dcfb57d8e ]
+
+FIELD_GET(SE_THRESH_MASK, thresh) extracts bits [31:23] from thresh and
+right-shifts them, which is the inverse of the intended operation. Since
+thresh is derived from se_buf_len / 2 (at most 255), bits [31:23] are
+always zero, so the SE threshold is never actually programmed into the
+register.
+
+Use FIELD_PREP() instead to correctly left-shift thresh into bits [31:23]
+of the valid_en_se register, consistent with the FIELD_PREP usage for
+the perf tuner config just above.
+
+Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup")
+Signed-off-by: David Carlier <devnexen@gmail.com>
+Review-by: Logan Gunthorpe <logang@deltatee.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260317083252.13224-1-devnexen@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/switchtec_dma.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
+index 3ef9286406159..71d9868ce613e 100644
+--- a/drivers/dma/switchtec_dma.c
++++ b/drivers/dma/switchtec_dma.c
+@@ -1099,7 +1099,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev,
+       dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len);
+       thresh = se_buf_len / 2;
+-      valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh);
++      valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh);
+       writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se);
+       /* request irqs */
+-- 
+2.53.0
+
diff --git a/queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
new file mode 100644 (file)
index 0000000..4207095
--- /dev/null
@@ -0,0 +1,55 @@
+From 07287edbbb8a5a79309122484769132dd2bd2a78 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 17:32:15 +0200
+Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep
+ annotation
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ]
+
+lockdep_hardirq_threaded() is supposed to be used within IRQ core code
+and not within drivers. It is not obvious from within the driver, that
+this is the only interrupt service routing and that it is not shared
+handler.
+
+Replace lockdep_hardirq_threaded() with a lockdep annotation limiting
+threaded context on PREEMPT_RT to __vmbus_isr().
+
+Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Michael Kelley <mhklinux@outlook.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/vmbus_drv.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
+index 23206640c6139..44877664d9d08 100644
+--- a/drivers/hv/vmbus_drv.c
++++ b/drivers/hv/vmbus_drv.c
+@@ -1384,8 +1384,19 @@ void vmbus_isr(void)
+       if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
+               vmbus_irqd_wake();
+       } else {
+-              lockdep_hardirq_threaded();
++              static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG);
++
++              /*
++               * vmbus_isr is never force-threaded and always invoked at hard
++               * IRQ level. __vmbus_isr() below can acquire a spinlock_t
++               * which becomes a sleeping lock and must not be acquired in
++               * this context. Therefore on PREEMPT_RT this will be threaded
++               * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets
++               * lockdep know that acquiring a spinlock_t is not an issue.
++               */
++              lock_map_acquire_try(&vmbus_map);
+               __vmbus_isr();
++              lock_map_release(&vmbus_map);
+       }
+ }
+ EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl");
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch b/queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch
new file mode 100644 (file)
index 0000000..0c2cbda
--- /dev/null
@@ -0,0 +1,80 @@
+From df1013221306f65150f4d2fc35e4a95523586b64 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 12:52:07 +0200
+Subject: drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC
+ encoder
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Alexander Kaplan <alexander.kaplan@sms-medipool.de>
+
+[ Upstream commit 8891e39e89042e285fd82fdde325d1311ec750a1 ]
+
+intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its
+DSC max FRL rate whenever the sink supports DSC 1.2.
+However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies
+to compressed video transport, which requires a DSC 1.2 encoder in
+the PCON (configured via intel_dp_pcon_dsc_configure()).
+Without such an encoder the HDMI link always carries uncompressed
+video, for which the regular Max_FRL_Rate is the correct limit.
+
+Applying the DSC limit unconditionally trains the FRL link at a lower
+rate than both the PCON and the sink support.
+E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps)
+behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC
+encoder):
+
+  Sink max rate from EDID = 24 Gbps
+  FRL trained with : 24 Gbps
+
+while Windows/macOS train the same hardware at 40/48 Gbps.
+The too low FRL rate needlessly constrains the formats available to
+the sink.
+
+Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2
+encoder, matching the gate in intel_dp_pcon_dsc_configure().
+PCONs with a DSC encoder keep the current conservative behavior,
+since the link is trained once and compressed transport may be used
+for any subsequent mode.
+With this the setup above trains at 48 Gbps.
+
+Tested on PTL (xe) with the above PCON/sink combo.
+
+Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding")
+Cc: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Reviewed-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Signed-off-by: Alexander Kaplan <alexander.kaplan@sms-medipool.de>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de
+(cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
+index f34da1a055dd5..c12a8ee846d74 100644
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -4109,7 +4109,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
+       rate_per_lane = info->hdmi.max_frl_rate_per_lane;
+       max_frl_rate = max_lanes * rate_per_lane;
+-      if (info->hdmi.dsc_cap.v_1p2) {
++      /*
++       * The sink's DSC max FRL rate only applies to compressed video
++       * transport, which requires a DSC 1.2 encoder in the PCON. Without
++       * one the HDMI link always carries uncompressed video, for which
++       * the regular max FRL rate is the limit.
++       */
++      if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) &&
++          info->hdmi.dsc_cap.v_1p2) {
+               max_dsc_lanes = info->hdmi.dsc_cap.max_lanes;
+               dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane;
+               if (max_dsc_lanes && dsc_rate_per_lane)
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch b/queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch
new file mode 100644 (file)
index 0000000..2000e8f
--- /dev/null
@@ -0,0 +1,117 @@
+From 5ba54986036fb36a0b4f2acea6d95fcb85bac771 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 07:55:44 +0530
+Subject: drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status
+
+From: Jerome Tollet <jerome.tollet@gmail.com>
+
+[ Upstream commit 1afb8eaeec44fd011f2b93ccd9fd426d753d963b ]
+
+HDMI 2.0 section 6.1.3.1 specifies that after enabling
+Scrambling_Enable and starting scrambled video transmission, the source
+should poll Scrambling_Status until it reads 1 or until a timeout of
+200 ms expires.
+
+Add a polling step after enabling the HDMI port to check the scrambling
+status when HDMI scrambling is enabled.
+
+On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz
+(594 MHz) failing to come up correctly because the sink has not yet
+finished its scrambling setup. In practice, waiting for the scrambling
+status here fixes such sinks.
+
+While this synchronous polling is not itself explicitly required for
+correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it
+as the way for the source to verify that the TMDS link is functioning
+correctly with scrambling enabled.
+
+v3:
+ - Add explicit HDMI 2.0 section reference in code comment
+ - Clarify commit message around the observed sink fix
+
+v2:
+ - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed
+   delay
+
+Reported-by: Jerome Tollet <jtollet@cisco.com>
+Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868
+Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/
+Signed-off-by: Jerome Tollet <jerome.tollet@gmail.com>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com
+(cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774)
+Fixes: 15953637886d ("drm/i915: enable scrambling")
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_ddi.c  |  2 ++
+ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++
+ drivers/gpu/drm/i915/display/intel_hdmi.h |  2 ++
+ 3 files changed, 30 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
+index 0987eee38bd13..a3bfb1be51de8 100644
+--- a/drivers/gpu/drm/i915/display/intel_ddi.c
++++ b/drivers/gpu/drm/i915/display/intel_ddi.c
+@@ -3502,6 +3502,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state,
+       }
+       intel_ddi_buf_enable(encoder, buf_ctl);
++
++      intel_hdmi_poll_for_scrambling_enable(crtc_state, connector);
+ }
+ static void intel_ddi_enable(struct intel_atomic_state *state,
+diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
+index 05e898d10a2be..234866310c14d 100644
+--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
++++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
+@@ -2693,6 +2693,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_
+               drm_connector_attach_max_bpc_property(&connector->base, 8, 12);
+ }
++/*
++ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after
++ * enabling Scrambling_Enable and starting scrambled video
++ * transmission, poll Scrambling_Status for up to 200 ms.
++ */
++void
++intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
++                                    struct drm_connector *_connector)
++{
++      struct intel_connector *connector = to_intel_connector(_connector);
++      struct intel_display *display = to_intel_display(crtc_state);
++      bool scrambling_enabled = false;
++      int ret;
++
++      if (!crtc_state->hdmi_scrambling)
++              return;
++
++      /* Poll for a max of 200 msec as per HDMI spec */
++      ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base),
++                            scrambling_enabled, 1000, 200 * 1000, false);
++      if (ret)
++              drm_dbg_kms(display->drm,
++                          "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n",
++                          connector->base.base.id, connector->base.name);
++}
++
+ /*
+  * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup
+  * @encoder: intel_encoder
+diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
+index be2fad57e4ad0..0fa3661568e86 100644
+--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
++++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
+@@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder,
+                       const struct intel_crtc_state *crtc_state,
+                       unsigned int type,
+                       void *frame, ssize_t len);
++void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
++                                         struct drm_connector *_connector);
+ #endif /* __INTEL_HDMI_H__ */
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch
new file mode 100644 (file)
index 0000000..e0b609f
--- /dev/null
@@ -0,0 +1,51 @@
+From df7d12ade4820ae52e31524de02d22e495310c3f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Jul 2026 23:05:28 +0800
+Subject: drm/mediatek: Check CRTC state before freeing
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
+
+mtk_crtc_reset() destroys the current CRTC state only when crtc->state
+is non-NULL, but it always converts crtc->state to struct mtk_crtc_state
+and passes the result to kfree().
+
+When reset is called without an existing state, container_of(NULL, ...)
+does not produce NULL. Keep the mtk state free in the same crtc->state
+guard as the helper state destruction.
+
+This issue was found by a static analysis checker and confirmed by
+manual source review.
+
+Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset")
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/
+Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
+index fcb16f3f7b23b..b01162a7df7fa 100644
+--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
+@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc)
+ {
+       struct mtk_crtc_state *state;
+-      if (crtc->state)
++      if (crtc->state) {
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+-
+-      kfree(to_mtk_crtc_state(crtc->state));
++              kfree(to_mtk_crtc_state(crtc->state));
++      }
+       crtc->state = NULL;
+       state = kzalloc_obj(*state);
+-- 
+2.53.0
+
diff --git a/queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch b/queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch
new file mode 100644 (file)
index 0000000..870a855
--- /dev/null
@@ -0,0 +1,78 @@
+From b84082dd4337ff1210d6c7ad9742175c9b6d39ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 11:29:17 +0530
+Subject: drm/xe/pt: check no-DMA huge-pte cases before DMA segment test
+
+From: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
+
+[ Upstream commit d94f82d57e7a86def6946f22b34eb53f96628f8a ]
+
+On a non-range clear, curs.size is never set, so the segment test
+(next - va_curs_start > curs->size) returns false for every level > 0
+before the clear_pt short-circuit is reached. The clear then descends to
+level 0 instead of forming a huge zero-leaf, wasting page tables and
+risking -ENOMEM on unbind.
+
+Move the null-VMA, purged-BO and clear_pt short-circuits above the
+curs->size test. The bind path always sets curs.size, so it is unaffected.
+
+v2
+- Also set curs.size on the clear path so the cursor stays meaningful
+during the walk. clear_pt is only reached with range == NULL, so assert
+that invariant. (Matthew Brost)
+
+Cc: Matthew Brost <matthew.brost@intel.com>
+Fixes: 5b658b7e89c3 ("drm/xe: Clear scratch page on vm_bind")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Reviewed-by: Matthew Brost <matthew.brost@intel.com>
+Link: https://patch.msgid.link/20260728055916.593707-2-himal.prasad.ghimiray@intel.com
+Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
+(cherry picked from commit 04eeeb45cb61b8a3e9d785003457e550c920ba49)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/xe/xe_pt.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
+index cf371c0d7b2bb..a4d0d9bf0c8eb 100644
+--- a/drivers/gpu/drm/xe/xe_pt.c
++++ b/drivers/gpu/drm/xe/xe_pt.c
+@@ -442,10 +442,6 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
+       if (!xe_pt_covers(addr, next, level, &xe_walk->base))
+               return false;
+-      /* Does the DMA segment cover the whole pte? */
+-      if (next - xe_walk->va_curs_start > xe_walk->curs->size)
+-              return false;
+-
+       /* null VMA's and purged BO's do not have dma addresses */
+       if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo)))
+               return true;
+@@ -454,6 +450,10 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
+       if (xe_walk->clear_pt)
+               return true;
++      /* Does the DMA segment cover the whole pte? */
++      if (next - xe_walk->va_curs_start > xe_walk->curs->size)
++              return false;
++
+       /* Is the DMA address huge PTE size aligned? */
+       size = next - addr;
+       dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs);
+@@ -774,8 +774,11 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
+       }
+       xe_walk.needs_64K = (vm->flags & XE_VM_FLAG_64K);
+-      if (clear_pt)
++      if (clear_pt) {
++              xe_assert(xe, !range);
++              curs.size = xe_vma_size(vma);
+               goto walk_pt;
++      }
+       if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) {
+               xe_walk.default_vram_pte = xe_atomic_for_vram(vm, vma) ? XE_USM_PPGTT_PTE_AE : 0;
+-- 
+2.53.0
+
diff --git a/queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch b/queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch
new file mode 100644 (file)
index 0000000..a905e2b
--- /dev/null
@@ -0,0 +1,116 @@
+From 815917a26cc807296207f876250c390fd6317ff4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Jun 2026 01:21:32 +0800
+Subject: erofs: clean up erofs_ishare_fill_inode()
+
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+
+[ Upstream commit 1ccc75909ca7c3b52163b408a3e1eb5453db013f ]
+
+ - Use the shorthand `si` to replace the overly long `sharedinode`;
+
+ - Introduce erofs_warn() and get rid of barely-used _erofs_printk();
+
+ - Get rid of the variable `hash`;
+
+ - Simplify error paths.
+
+Reviewed-by: Hongbo Li <lihongbo22@huawei.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Stable-dep-of: 96b2dbbe58a1 ("erofs: ensure valid f_path for page cache sharing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/erofs/internal.h |  2 ++
+ fs/erofs/ishare.c   | 45 +++++++++++++++++++--------------------------
+ 2 files changed, 21 insertions(+), 26 deletions(-)
+
+diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
+index 4792490161ec9..9e2ae7b619773 100644
+--- a/fs/erofs/internal.h
++++ b/fs/erofs/internal.h
+@@ -23,6 +23,8 @@
+ __printf(2, 3) void _erofs_printk(struct super_block *sb, const char *fmt, ...);
+ #define erofs_err(sb, fmt, ...)       \
+       _erofs_printk(sb, KERN_ERR fmt "\n", ##__VA_ARGS__)
++#define erofs_warn(sb, fmt, ...) \
++      _erofs_printk(sb, KERN_WARNING fmt "\n", ##__VA_ARGS__)
+ #define erofs_info(sb, fmt, ...) \
+       _erofs_printk(sb, KERN_INFO fmt "\n", ##__VA_ARGS__)
+diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
+index 6ed66b17359ba..35cbd0bc04d7d 100644
+--- a/fs/erofs/ishare.c
++++ b/fs/erofs/ishare.c
+@@ -40,49 +40,42 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data)
+ bool erofs_ishare_fill_inode(struct inode *inode)
+ {
+       struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
+-      struct erofs_inode *vi = EROFS_I(inode);
+       const struct address_space_operations *aops;
++      struct erofs_inode *vi = EROFS_I(inode);
+       struct erofs_inode_fingerprint fp;
+-      struct inode *sharedinode;
+-      unsigned long hash;
++      struct inode *si;
+       aops = erofs_get_aops(inode, true);
+       if (IS_ERR(aops))
+               return false;
+       if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
+               return false;
+-      hash = xxh32(fp.opaque, fp.size, 0);
+-      sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash,
+-                                 erofs_ishare_iget5_eq, erofs_ishare_iget5_set,
+-                                 &fp);
+-      if (!sharedinode) {
+-              kfree(fp.opaque);
+-              return false;
+-      }
+-      if (inode_state_read_once(sharedinode) & I_NEW) {
+-              sharedinode->i_mapping->a_ops = aops;
+-              sharedinode->i_size = vi->vfs_inode.i_size;
+-              unlock_new_inode(sharedinode);
++      si = iget5_locked(erofs_ishare_mnt->mnt_sb,
++                        xxh32(fp.opaque, fp.size, 0),
++                        erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
++      if (si && (inode_state_read_once(si) & I_NEW)) {
++              si->i_mapping->a_ops = aops;
++              si->i_size = inode->i_size;
++              unlock_new_inode(si);
+       } else {
+               kfree(fp.opaque);
+-              if (aops != sharedinode->i_mapping->a_ops) {
+-                      iput(sharedinode);
++              if (!si || aops != si->i_mapping->a_ops) {
++                      iput(si);
+                       return false;
+               }
+-              if (sharedinode->i_size != vi->vfs_inode.i_size) {
+-                      _erofs_printk(inode->i_sb, KERN_WARNING
+-                              "size(%lld:%lld) not matches for the same fingerprint\n",
+-                              vi->vfs_inode.i_size, sharedinode->i_size);
+-                      iput(sharedinode);
++              if (si->i_size != inode->i_size) {
++                      erofs_warn(inode->i_sb, "i_size mismatch (%lld != %lld) for the same fingerprint",
++                                 inode->i_size, si->i_size);
++                      iput(si);
+                       return false;
+               }
+       }
+-      vi->sharedinode = sharedinode;
++      vi->sharedinode = si;
+       INIT_LIST_HEAD(&vi->ishare_list);
+-      spin_lock(&EROFS_I(sharedinode)->ishare_lock);
+-      list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list);
+-      spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
++      spin_lock(&EROFS_I(si)->ishare_lock);
++      list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
++      spin_unlock(&EROFS_I(si)->ishare_lock);
+       return true;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch b/queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch
new file mode 100644 (file)
index 0000000..b05bfad
--- /dev/null
@@ -0,0 +1,192 @@
+From c73a6cf0ae38ea3c1cda98c6aa86017c6e1e8129 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 12:27:39 +0800
+Subject: erofs: ensure valid f_path for page cache sharing
+
+From: Gao Xiang <xiang@kernel.org>
+
+[ Upstream commit 96b2dbbe58a1ea5df8d29c2fe24b5f04715f4443 ]
+
+Previously, backing files for page cache sharing were set up with
+f_path left as NULL (only f_inode was valid).  It worked, but a recent
+mincore fix relies on f_path.mnt and crashes (found by "erofs/028" on
+7.2-rc4):
+
+ BUG: kernel NULL pointer dereference, address: 0000000000000018
+ #PF: supervisor read access in kernel mode
+ #PF: error_code(0x0000) - not-present page
+ PGD 0 P4D 0
+ Oops: Oops: 0000 [#1] SMP PTI
+ CPU: 3 UID: 0 PID: 675528 Comm: fincore Not tainted 7.2.0-rc4-00002-g[]-dirty #1 PREEMPT(lazy)
+ Hardware name: Red Hat KVM, BIOS 1.16.0-4.al8 04/01/2014
+ RIP: 0010:__do_sys_mincore+0xc0/0x2c0
+ ...
+
+Specify valid paths using valid disconnected dentries together with
+erofs_ishare_mnt instead of leaving f_path empty, so they are more
+like real backing files in a pseudo filesystem and standard
+backing_file_open() can be used directly.
+
+Fixes: e187bc02f8fa ("mm: do file ownership checks with the proper mount idmap")
+Acked-by: Hongbo Li <hongbohbli@tencent.com>
+Signed-off-by: Gao Xiang <xiang@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/erofs/Kconfig    |  1 +
+ fs/erofs/internal.h |  4 ++--
+ fs/erofs/ishare.c   | 58 ++++++++++++++++++++++-----------------------
+ 3 files changed, 31 insertions(+), 32 deletions(-)
+
+diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
+index 4789b1077d8ce..1feb28cfe557d 100644
+--- a/fs/erofs/Kconfig
++++ b/fs/erofs/Kconfig
+@@ -189,6 +189,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI
+ config EROFS_FS_PAGE_CACHE_SHARE
+       bool "EROFS page cache share support (experimental)"
+       depends on EROFS_FS && EROFS_FS_XATTR
++      select FS_STACK
+       help
+         This enables page cache sharing among inodes with identical
+         content fingerprints on the same machine.
+diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
+index 580f8d9f14e7f..57bd21859c65d 100644
+--- a/fs/erofs/internal.h
++++ b/fs/erofs/internal.h
+@@ -288,8 +288,8 @@ struct erofs_inode {
+                       struct erofs_inode_fingerprint fingerprint;
+                       spinlock_t ishare_lock;
+               };
+-              /* for each real inode */
+-              struct inode *sharedinode;
++              /* for each real filesystem inode */
++              struct dentry *sharedentry;
+       };
+ #endif
+       /* the corresponding vfs inode */
+diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
+index 0868c12fc15b4..25558df98a389 100644
+--- a/fs/erofs/ishare.c
++++ b/fs/erofs/ishare.c
+@@ -2,14 +2,13 @@
+ /*
+  * Copyright (C) 2024, Alibaba Cloud
+  */
++#include <linux/backing-file.h>
+ #include <linux/xxhash.h>
+ #include <linux/mount.h>
+ #include <linux/security.h>
+ #include "internal.h"
+ #include "xattr.h"
+-#include "../internal.h"
+-
+ static struct vfsmount *erofs_ishare_mnt;
+ static inline bool erofs_is_ishare_inode(struct inode *inode)
+@@ -39,10 +38,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data)
+ bool erofs_ishare_fill_inode(struct inode *inode)
+ {
++      static const struct file_operations empty_fops = {};
+       struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
+       const struct address_space_operations *aops;
+       struct erofs_inode *vi = EROFS_I(inode);
+       struct erofs_inode_fingerprint fp;
++      struct dentry *sd;
+       struct inode *si;
+       aops = erofs_get_aops(inode);
+@@ -55,7 +56,9 @@ bool erofs_ishare_fill_inode(struct inode *inode)
+                         xxh32(fp.opaque, fp.size, 0),
+                         erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
+       if (si && (inode_state_read_once(si) & I_NEW)) {
++              si->i_fop = &empty_fops;
+               si->i_mapping->a_ops = aops;
++              si->i_mode = 0444 | S_IFREG;
+               si->i_size = inode->i_size;
+               unlock_new_inode(si);
+       } else {
+@@ -71,7 +74,10 @@ bool erofs_ishare_fill_inode(struct inode *inode)
+                       return false;
+               }
+       }
+-      vi->sharedinode = si;
++      sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */
++      if (IS_ERR(sd))
++              return false;
++      vi->sharedentry = sd;
+       INIT_LIST_HEAD(&vi->ishare_list);
+       spin_lock(&EROFS_I(si)->ishare_lock);
+       list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
+@@ -81,48 +87,40 @@ bool erofs_ishare_fill_inode(struct inode *inode)
+ void erofs_ishare_free_inode(struct inode *inode)
+ {
+-      struct erofs_inode *vi = EROFS_I(inode);
+-      struct inode *sharedinode = vi->sharedinode;
++      struct erofs_inode *vi = EROFS_I(inode), *svi;
+-      if (!sharedinode)
++      if (!vi->sharedentry)
+               return;
+-      spin_lock(&EROFS_I(sharedinode)->ishare_lock);
++      svi = EROFS_I(d_inode(vi->sharedentry));
++      spin_lock(&svi->ishare_lock);
+       list_del(&vi->ishare_list);
+-      spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
+-      iput(sharedinode);
+-      vi->sharedinode = NULL;
++      spin_unlock(&svi->ishare_lock);
++      dput(vi->sharedentry);
++      vi->sharedentry = NULL;
+ }
+ static int erofs_ishare_file_open(struct inode *inode, struct file *file)
+ {
+-      struct inode *sharedinode = EROFS_I(inode)->sharedinode;
+-      struct file *realfile;
++      struct path sharedpath = {
++              .mnt = erofs_ishare_mnt,
++              .dentry = EROFS_I(inode)->sharedentry,
++      };
++      struct file *rf;
+       if (file->f_flags & O_DIRECT)
+               return -EINVAL;
+-      realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(),
+-                                          file);
+-      if (IS_ERR(realfile))
+-              return PTR_ERR(realfile);
+-      ihold(sharedinode);
+-      realfile->f_op = &erofs_file_fops;
+-      realfile->f_inode = sharedinode;
+-      realfile->f_mapping = sharedinode->i_mapping;
+-      path_get(&file->f_path);
+-      backing_file_set_user_path(realfile, &file->f_path);
+-
+-      file_ra_state_init(&realfile->f_ra, file->f_mapping);
+-      realfile->private_data = EROFS_I(inode);
+-      file->private_data = realfile;
++
++      rf = backing_file_open(file, file->f_flags | O_NOATIME,
++                             &sharedpath, current_cred());
++      if (IS_ERR(rf))
++              return PTR_ERR(rf);
++      file->private_data = rf;
+       return 0;
+ }
+ static int erofs_ishare_file_release(struct inode *inode, struct file *file)
+ {
+-      struct file *realfile = file->private_data;
+-
+-      iput(realfile->f_inode);
+-      fput(realfile);
++      fput(file->private_data);
+       file->private_data = NULL;
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/erofs-remove-fscache-backend-entirely.patch b/queue-7.1/erofs-remove-fscache-backend-entirely.patch
new file mode 100644 (file)
index 0000000..197f1e5
--- /dev/null
@@ -0,0 +1,1275 @@
+From e63a500e59ec32aa2329f001f08f60a7e760daf2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 22 Jun 2026 09:36:22 +0800
+Subject: erofs: remove fscache backend entirely
+
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+
+[ Upstream commit c37460cd9b2fcb61ec66b7eb4fde737e65ec2a56 ]
+
+EROFS over fscache was introduced to provide image lazy pulling
+functionality. After the feature landed, the fscache subsystem made
+netfs a new hard dependency, which is unexpected for a local filesystem
+and has an kernel-defined caching hierarchy which could be inflexible
+compared to the fanotify pre-content hooks. Therefore, this feature has
+been deprecated for almost two years.
+
+As EROFS file-backed mounts and fanotify pre-content hooks both upstream
+for a while and already providing equivalent functionality (erofs-utils
+has supported fanotify pre-content hooks), let's remove the fscache
+backend now.
+
+The main application of this feature is Nydus [1], and they plan to move
+to use fanotify pre-content hooks in the near future too.
+
+I hope this patch can be merged into Linux 7.2, which is also motivated
+by newly found implementation issues [2][3] that are not worth
+investigating given the deprecation and limited development resources.
+The associated fscache/cachefiles cleanup patch will follow separately
+through the vfs tree (netfs) later: it seems fine since the codebase is
+isolated by CONFIG_CACHEFILES_ONDEMAND.
+
+[1] https://github.com/dragonflyoss/nydus/blob/v2.1.0/docs/nydus-fscache.md
+[2] https://github.com/dragonflyoss/nydus/pull/1824
+[3] https://lore.kernel.org/r/20260619135800.1594811-1-michael.bommarito@gmail.com
+
+Acked-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Stable-dep-of: 96b2dbbe58a1 ("erofs: ensure valid f_path for page cache sharing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/filesystems/erofs.rst |   9 +-
+ fs/erofs/Kconfig                    |  21 +-
+ fs/erofs/Makefile                   |   1 -
+ fs/erofs/data.c                     |   4 +-
+ fs/erofs/fscache.c                  | 664 ----------------------------
+ fs/erofs/inode.c                    |   2 +-
+ fs/erofs/internal.h                 |  70 +--
+ fs/erofs/ishare.c                   |   2 +-
+ fs/erofs/super.c                    |  94 +---
+ fs/erofs/zdata.c                    |   6 -
+ 10 files changed, 26 insertions(+), 847 deletions(-)
+ delete mode 100644 fs/erofs/fscache.c
+
+diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst
+index fe06308e546c1..4a82e1e31e5fa 100644
+--- a/Documentation/filesystems/erofs.rst
++++ b/Documentation/filesystems/erofs.rst
+@@ -127,12 +127,9 @@ dax                    A legacy option which is an alias for ``dax=always``.
+ device=%s              Specify a path to an extra device to be used together.
+ directio               (For file-backed mounts) Use direct I/O to access backing
+                        files, and asynchronous I/O will be enabled if supported.
+-fsid=%s                Specify a filesystem image ID for Fscache back-end.
+-domain_id=%s           Specify a trusted domain ID for fscache mode so that
+-                       different images with the same blobs, identified by blob IDs,
+-                       can share storage within the same trusted domain.
+-                       Also used for different filesystems with inode page sharing
+-                       enabled to share page cache within the trusted domain.
++domain_id=%s           Specify a trusted domain ID. Filesystems sharing the same
++                       domain ID can share page cache across mounts when inode
++                       page sharing is enabled. (not shown in mountinfo output)
+ fsoffset=%llu          Specify block-aligned filesystem offset for the primary device.
+ inode_share            Enable inode page sharing for this filesystem.  Inodes with
+                        identical content within the same domain ID can share the
+diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
+index 97c48ebe84584..4789b1077d8ce 100644
+--- a/fs/erofs/Kconfig
++++ b/fs/erofs/Kconfig
+@@ -3,13 +3,11 @@
+ config EROFS_FS
+       tristate "EROFS filesystem support"
+       depends on BLOCK
+-      select CACHEFILES if EROFS_FS_ONDEMAND
+       select CRC32
+       select CRYPTO if EROFS_FS_ZIP_ACCEL
+       select CRYPTO_DEFLATE if EROFS_FS_ZIP_ACCEL
+       select FS_IOMAP
+       select LZ4_DECOMPRESS if EROFS_FS_ZIP
+-      select NETFS_SUPPORT if EROFS_FS_ONDEMAND
+       select XXHASH if EROFS_FS_XATTR
+       select XZ_DEC if EROFS_FS_ZIP_LZMA
+       select XZ_DEC_MICROLZMA if EROFS_FS_ZIP_LZMA
+@@ -109,9 +107,6 @@ config EROFS_FS_BACKED_BY_FILE
+         be used to simplify error-prone lifetime management of unnecessary
+         virtual block devices.
+-        Note that this feature, along with ongoing fanotify pre-content
+-        hooks, will eventually replace "EROFS over fscache."
+-
+         If you don't want to enable this feature, say N.
+ config EROFS_FS_ZIP
+@@ -172,20 +167,6 @@ config EROFS_FS_ZIP_ACCEL
+         If unsure, say N.
+-config EROFS_FS_ONDEMAND
+-      bool "EROFS fscache-based on-demand read support (deprecated)"
+-      depends on EROFS_FS
+-      select FSCACHE
+-      select CACHEFILES_ONDEMAND
+-      help
+-        This permits EROFS to use fscache-backed data blobs with on-demand
+-        read support.
+-
+-        It is now deprecated and scheduled to be removed from the kernel
+-        after fanotify pre-content hooks are landed.
+-
+-        If unsure, say N.
+-
+ config EROFS_FS_PCPU_KTHREAD
+       bool "EROFS per-cpu decompression kthread workers"
+       depends on EROFS_FS_ZIP
+@@ -207,7 +188,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI
+ config EROFS_FS_PAGE_CACHE_SHARE
+       bool "EROFS page cache share support (experimental)"
+-      depends on EROFS_FS && EROFS_FS_XATTR && !EROFS_FS_ONDEMAND
++      depends on EROFS_FS && EROFS_FS_XATTR
+       help
+         This enables page cache sharing among inodes with identical
+         content fingerprints on the same machine.
+diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
+index a80e1762b6079..30423496786fe 100644
+--- a/fs/erofs/Makefile
++++ b/fs/erofs/Makefile
+@@ -9,5 +9,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
+ erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
+ erofs-$(CONFIG_EROFS_FS_ZIP_ACCEL) += decompressor_crypto.o
+ erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
+-erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
+ erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o
+diff --git a/fs/erofs/data.c b/fs/erofs/data.c
+index 44da21c9d7776..af846bab1368c 100644
+--- a/fs/erofs/data.c
++++ b/fs/erofs/data.c
+@@ -80,9 +80,7 @@ int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb,
+       if (erofs_is_fileio_mode(sbi)) {
+               buf->file = sbi->dif0.file;     /* some fs like FUSE needs it */
+               buf->mapping = buf->file->f_mapping;
+-      } else if (erofs_is_fscache_mode(sb))
+-              buf->mapping = sbi->dif0.fscache->inode->i_mapping;
+-      else
++      } else
+               buf->mapping = sb->s_bdev->bd_mapping;
+       return 0;
+ }
+diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c
+deleted file mode 100644
+index 685c68774379b..0000000000000
+--- a/fs/erofs/fscache.c
++++ /dev/null
+@@ -1,664 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2022, Alibaba Cloud
+- * Copyright (C) 2022, Bytedance Inc. All rights reserved.
+- */
+-#include <linux/fscache.h>
+-#include "internal.h"
+-
+-static DEFINE_MUTEX(erofs_domain_list_lock);
+-static DEFINE_MUTEX(erofs_domain_cookies_lock);
+-static LIST_HEAD(erofs_domain_list);
+-static LIST_HEAD(erofs_domain_cookies_list);
+-static struct vfsmount *erofs_pseudo_mnt;
+-
+-struct erofs_fscache_io {
+-      struct netfs_cache_resources cres;
+-      struct iov_iter         iter;
+-      netfs_io_terminated_t   end_io;
+-      void                    *private;
+-      refcount_t              ref;
+-};
+-
+-struct erofs_fscache_rq {
+-      struct address_space    *mapping;       /* The mapping being accessed */
+-      loff_t                  start;          /* Start position */
+-      size_t                  len;            /* Length of the request */
+-      size_t                  submitted;      /* Length of submitted */
+-      short                   error;          /* 0 or error that occurred */
+-      refcount_t              ref;
+-};
+-
+-static bool erofs_fscache_io_put(struct erofs_fscache_io *io)
+-{
+-      if (!refcount_dec_and_test(&io->ref))
+-              return false;
+-      if (io->cres.ops)
+-              io->cres.ops->end_operation(&io->cres);
+-      kfree(io);
+-      return true;
+-}
+-
+-static void erofs_fscache_req_complete(struct erofs_fscache_rq *req)
+-{
+-      struct folio *folio;
+-      bool failed = req->error;
+-      pgoff_t start_page = req->start / PAGE_SIZE;
+-      pgoff_t last_page = ((req->start + req->len) / PAGE_SIZE) - 1;
+-
+-      XA_STATE(xas, &req->mapping->i_pages, start_page);
+-
+-      rcu_read_lock();
+-      xas_for_each(&xas, folio, last_page) {
+-              if (xas_retry(&xas, folio))
+-                      continue;
+-              if (!failed)
+-                      folio_mark_uptodate(folio);
+-              folio_unlock(folio);
+-      }
+-      rcu_read_unlock();
+-}
+-
+-static void erofs_fscache_req_put(struct erofs_fscache_rq *req)
+-{
+-      if (!refcount_dec_and_test(&req->ref))
+-              return;
+-      erofs_fscache_req_complete(req);
+-      kfree(req);
+-}
+-
+-static struct erofs_fscache_rq *erofs_fscache_req_alloc(struct address_space *mapping,
+-                                              loff_t start, size_t len)
+-{
+-      struct erofs_fscache_rq *req = kzalloc_obj(*req);
+-
+-      if (!req)
+-              return NULL;
+-      req->mapping = mapping;
+-      req->start = start;
+-      req->len = len;
+-      refcount_set(&req->ref, 1);
+-      return req;
+-}
+-
+-static void erofs_fscache_req_io_put(struct erofs_fscache_io *io)
+-{
+-      struct erofs_fscache_rq *req = io->private;
+-
+-      if (erofs_fscache_io_put(io))
+-              erofs_fscache_req_put(req);
+-}
+-
+-static void erofs_fscache_req_end_io(void *priv, ssize_t transferred_or_error)
+-{
+-      struct erofs_fscache_io *io = priv;
+-      struct erofs_fscache_rq *req = io->private;
+-
+-      if (IS_ERR_VALUE(transferred_or_error))
+-              req->error = transferred_or_error;
+-      erofs_fscache_req_io_put(io);
+-}
+-
+-static struct erofs_fscache_io *erofs_fscache_req_io_alloc(struct erofs_fscache_rq *req)
+-{
+-      struct erofs_fscache_io *io = kzalloc_obj(*io);
+-
+-      if (!io)
+-              return NULL;
+-      io->end_io = erofs_fscache_req_end_io;
+-      io->private = req;
+-      refcount_inc(&req->ref);
+-      refcount_set(&io->ref, 1);
+-      return io;
+-}
+-
+-/*
+- * Read data from fscache described by cookie at pstart physical address
+- * offset, and fill the read data into buffer described by io->iter.
+- */
+-static int erofs_fscache_read_io_async(struct fscache_cookie *cookie,
+-              loff_t pstart, struct erofs_fscache_io *io)
+-{
+-      enum netfs_io_source source;
+-      struct netfs_cache_resources *cres = &io->cres;
+-      struct iov_iter *iter = &io->iter;
+-      int ret;
+-
+-      ret = fscache_begin_read_operation(cres, cookie);
+-      if (ret)
+-              return ret;
+-
+-      while (iov_iter_count(iter)) {
+-              size_t orig_count = iov_iter_count(iter), len = orig_count;
+-              unsigned long flags = 1 << NETFS_SREQ_ONDEMAND;
+-
+-              source = cres->ops->prepare_ondemand_read(cres,
+-                              pstart, &len, LLONG_MAX, &flags, 0);
+-              if (WARN_ON(len == 0))
+-                      source = NETFS_INVALID_READ;
+-              if (source != NETFS_READ_FROM_CACHE) {
+-                      erofs_err(NULL, "prepare_ondemand_read failed (source %d)", source);
+-                      return -EIO;
+-              }
+-
+-              iov_iter_truncate(iter, len);
+-              refcount_inc(&io->ref);
+-              ret = fscache_read(cres, pstart, iter, NETFS_READ_HOLE_FAIL,
+-                                 io->end_io, io);
+-              if (ret == -EIOCBQUEUED)
+-                      ret = 0;
+-              if (ret) {
+-                      erofs_err(NULL, "fscache_read failed (ret %d)", ret);
+-                      return ret;
+-              }
+-              if (WARN_ON(iov_iter_count(iter)))
+-                      return -EIO;
+-
+-              iov_iter_reexpand(iter, orig_count - len);
+-              pstart += len;
+-      }
+-      return 0;
+-}
+-
+-struct erofs_fscache_bio {
+-      struct erofs_fscache_io io;
+-      struct bio bio;         /* w/o bdev to share bio_add_page/endio() */
+-      struct bio_vec bvecs[BIO_MAX_VECS];
+-};
+-
+-static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error)
+-{
+-      struct erofs_fscache_bio *io = priv;
+-
+-      if (IS_ERR_VALUE(transferred_or_error))
+-              io->bio.bi_status = errno_to_blk_status(transferred_or_error);
+-      bio_endio(&io->bio);
+-      BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0);
+-      erofs_fscache_io_put(&io->io);
+-}
+-
+-struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev)
+-{
+-      struct erofs_fscache_bio *io;
+-
+-      io = kmalloc_obj(*io, GFP_KERNEL | __GFP_NOFAIL);
+-      bio_init(&io->bio, NULL, io->bvecs, BIO_MAX_VECS, REQ_OP_READ);
+-      io->io.private = mdev->m_dif->fscache->cookie;
+-      io->io.end_io = erofs_fscache_bio_endio;
+-      refcount_set(&io->io.ref, 1);
+-      return &io->bio;
+-}
+-
+-void erofs_fscache_submit_bio(struct bio *bio)
+-{
+-      struct erofs_fscache_bio *io = container_of(bio,
+-                      struct erofs_fscache_bio, bio);
+-      int ret;
+-
+-      iov_iter_bvec(&io->io.iter, ITER_DEST, io->bvecs, bio->bi_vcnt,
+-                    bio->bi_iter.bi_size);
+-      ret = erofs_fscache_read_io_async(io->io.private,
+-                              bio->bi_iter.bi_sector << 9, &io->io);
+-      erofs_fscache_io_put(&io->io);
+-      if (!ret)
+-              return;
+-      bio->bi_status = errno_to_blk_status(ret);
+-      bio_endio(bio);
+-}
+-
+-static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
+-{
+-      struct erofs_fscache *ctx = folio->mapping->host->i_private;
+-      int ret = -ENOMEM;
+-      struct erofs_fscache_rq *req;
+-      struct erofs_fscache_io *io;
+-
+-      req = erofs_fscache_req_alloc(folio->mapping,
+-                              folio_pos(folio), folio_size(folio));
+-      if (!req) {
+-              folio_unlock(folio);
+-              return ret;
+-      }
+-
+-      io = erofs_fscache_req_io_alloc(req);
+-      if (!io) {
+-              req->error = ret;
+-              goto out;
+-      }
+-      iov_iter_xarray(&io->iter, ITER_DEST, &folio->mapping->i_pages,
+-                      folio_pos(folio), folio_size(folio));
+-
+-      ret = erofs_fscache_read_io_async(ctx->cookie, folio_pos(folio), io);
+-      if (ret)
+-              req->error = ret;
+-
+-      erofs_fscache_req_io_put(io);
+-out:
+-      erofs_fscache_req_put(req);
+-      return ret;
+-}
+-
+-static int erofs_fscache_data_read_slice(struct erofs_fscache_rq *req)
+-{
+-      struct address_space *mapping = req->mapping;
+-      struct inode *inode = mapping->host;
+-      struct super_block *sb = inode->i_sb;
+-      struct erofs_fscache_io *io;
+-      struct erofs_map_blocks map;
+-      struct erofs_map_dev mdev;
+-      loff_t pos = req->start + req->submitted;
+-      size_t count;
+-      int ret;
+-
+-      map.m_la = pos;
+-      ret = erofs_map_blocks(inode, &map);
+-      if (ret)
+-              return ret;
+-
+-      if (map.m_flags & EROFS_MAP_META) {
+-              struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
+-              struct iov_iter iter;
+-              size_t size = map.m_llen;
+-              void *src;
+-
+-              src = erofs_read_metabuf(&buf, sb, map.m_pa,
+-                                       erofs_inode_in_metabox(inode));
+-              if (IS_ERR(src))
+-                      return PTR_ERR(src);
+-
+-              iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE);
+-              if (copy_to_iter(src, size, &iter) != size) {
+-                      erofs_put_metabuf(&buf);
+-                      return -EFAULT;
+-              }
+-              iov_iter_zero(PAGE_SIZE - size, &iter);
+-              erofs_put_metabuf(&buf);
+-              req->submitted += PAGE_SIZE;
+-              return 0;
+-      }
+-
+-      count = req->len - req->submitted;
+-      if (!(map.m_flags & EROFS_MAP_MAPPED)) {
+-              struct iov_iter iter;
+-
+-              iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count);
+-              iov_iter_zero(count, &iter);
+-              req->submitted += count;
+-              return 0;
+-      }
+-
+-      count = min_t(size_t, map.m_llen - (pos - map.m_la), count);
+-      DBG_BUGON(!count || count % PAGE_SIZE);
+-
+-      mdev = (struct erofs_map_dev) {
+-              .m_deviceid = map.m_deviceid,
+-              .m_pa = map.m_pa,
+-      };
+-      ret = erofs_map_dev(sb, &mdev);
+-      if (ret)
+-              return ret;
+-
+-      io = erofs_fscache_req_io_alloc(req);
+-      if (!io)
+-              return -ENOMEM;
+-      iov_iter_xarray(&io->iter, ITER_DEST, &mapping->i_pages, pos, count);
+-      ret = erofs_fscache_read_io_async(mdev.m_dif->fscache->cookie,
+-                      mdev.m_pa + (pos - map.m_la), io);
+-      erofs_fscache_req_io_put(io);
+-
+-      req->submitted += count;
+-      return ret;
+-}
+-
+-static int erofs_fscache_data_read(struct erofs_fscache_rq *req)
+-{
+-      int ret;
+-
+-      do {
+-              ret = erofs_fscache_data_read_slice(req);
+-              if (ret)
+-                      req->error = ret;
+-      } while (!ret && req->submitted < req->len);
+-      return ret;
+-}
+-
+-static int erofs_fscache_read_folio(struct file *file, struct folio *folio)
+-{
+-      struct erofs_fscache_rq *req;
+-      int ret;
+-
+-      req = erofs_fscache_req_alloc(folio->mapping,
+-                      folio_pos(folio), folio_size(folio));
+-      if (!req) {
+-              folio_unlock(folio);
+-              return -ENOMEM;
+-      }
+-
+-      ret = erofs_fscache_data_read(req);
+-      erofs_fscache_req_put(req);
+-      return ret;
+-}
+-
+-static void erofs_fscache_readahead(struct readahead_control *rac)
+-{
+-      struct erofs_fscache_rq *req;
+-
+-      if (!readahead_count(rac))
+-              return;
+-
+-      req = erofs_fscache_req_alloc(rac->mapping,
+-                      readahead_pos(rac), readahead_length(rac));
+-      if (!req)
+-              return;
+-
+-      /* The request completion will drop refs on the folios. */
+-      while (readahead_folio(rac))
+-              ;
+-
+-      erofs_fscache_data_read(req);
+-      erofs_fscache_req_put(req);
+-}
+-
+-static const struct address_space_operations erofs_fscache_meta_aops = {
+-      .read_folio = erofs_fscache_meta_read_folio,
+-};
+-
+-const struct address_space_operations erofs_fscache_access_aops = {
+-      .read_folio = erofs_fscache_read_folio,
+-      .readahead = erofs_fscache_readahead,
+-};
+-
+-static void erofs_fscache_domain_put(struct erofs_domain *domain)
+-{
+-      mutex_lock(&erofs_domain_list_lock);
+-      if (refcount_dec_and_test(&domain->ref)) {
+-              list_del(&domain->list);
+-              if (list_empty(&erofs_domain_list)) {
+-                      kern_unmount(erofs_pseudo_mnt);
+-                      erofs_pseudo_mnt = NULL;
+-              }
+-              fscache_relinquish_volume(domain->volume, NULL, false);
+-              mutex_unlock(&erofs_domain_list_lock);
+-              kfree_sensitive(domain->domain_id);
+-              kfree(domain);
+-              return;
+-      }
+-      mutex_unlock(&erofs_domain_list_lock);
+-}
+-
+-static int erofs_fscache_register_volume(struct super_block *sb)
+-{
+-      struct erofs_sb_info *sbi = EROFS_SB(sb);
+-      char *domain_id = sbi->domain_id;
+-      struct fscache_volume *volume;
+-      char *name;
+-      int ret = 0;
+-
+-      name = kasprintf(GFP_KERNEL, "erofs,%s",
+-                       domain_id ? domain_id : sbi->fsid);
+-      if (!name)
+-              return -ENOMEM;
+-
+-      volume = fscache_acquire_volume(name, NULL, NULL, 0);
+-      if (IS_ERR_OR_NULL(volume)) {
+-              erofs_err(sb, "failed to register volume for %s", name);
+-              ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP;
+-              volume = NULL;
+-      }
+-
+-      sbi->volume = volume;
+-      kfree(name);
+-      return ret;
+-}
+-
+-static int erofs_fscache_init_domain(struct super_block *sb)
+-{
+-      int err;
+-      struct erofs_domain *domain;
+-      struct erofs_sb_info *sbi = EROFS_SB(sb);
+-
+-      domain = kzalloc_obj(struct erofs_domain);
+-      if (!domain)
+-              return -ENOMEM;
+-
+-      domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL);
+-      if (!domain->domain_id) {
+-              kfree(domain);
+-              return -ENOMEM;
+-      }
+-
+-      err = erofs_fscache_register_volume(sb);
+-      if (err)
+-              goto out;
+-
+-      if (!erofs_pseudo_mnt) {
+-              struct vfsmount *mnt = kern_mount(&erofs_anon_fs_type);
+-              if (IS_ERR(mnt)) {
+-                      err = PTR_ERR(mnt);
+-                      goto out;
+-              }
+-              erofs_pseudo_mnt = mnt;
+-      }
+-
+-      domain->volume = sbi->volume;
+-      refcount_set(&domain->ref, 1);
+-      list_add(&domain->list, &erofs_domain_list);
+-      sbi->domain = domain;
+-      return 0;
+-out:
+-      kfree_sensitive(domain->domain_id);
+-      kfree(domain);
+-      return err;
+-}
+-
+-static int erofs_fscache_register_domain(struct super_block *sb)
+-{
+-      int err;
+-      struct erofs_domain *domain;
+-      struct erofs_sb_info *sbi = EROFS_SB(sb);
+-
+-      mutex_lock(&erofs_domain_list_lock);
+-      list_for_each_entry(domain, &erofs_domain_list, list) {
+-              if (!strcmp(domain->domain_id, sbi->domain_id)) {
+-                      sbi->domain = domain;
+-                      sbi->volume = domain->volume;
+-                      refcount_inc(&domain->ref);
+-                      mutex_unlock(&erofs_domain_list_lock);
+-                      return 0;
+-              }
+-      }
+-      err = erofs_fscache_init_domain(sb);
+-      mutex_unlock(&erofs_domain_list_lock);
+-      return err;
+-}
+-
+-static struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb,
+-                                              char *name, unsigned int flags)
+-{
+-      struct fscache_volume *volume = EROFS_SB(sb)->volume;
+-      struct erofs_fscache *ctx;
+-      struct fscache_cookie *cookie;
+-      struct super_block *isb;
+-      struct inode *inode;
+-      int ret;
+-
+-      ctx = kzalloc_obj(*ctx);
+-      if (!ctx)
+-              return ERR_PTR(-ENOMEM);
+-      INIT_LIST_HEAD(&ctx->node);
+-      refcount_set(&ctx->ref, 1);
+-
+-      cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE,
+-                                      name, strlen(name), NULL, 0, 0);
+-      if (!cookie) {
+-              erofs_err(sb, "failed to get cookie for %s", name);
+-              ret = -EINVAL;
+-              goto err;
+-      }
+-      fscache_use_cookie(cookie, false);
+-
+-      /*
+-       * Allocate anonymous inode in global pseudo mount for shareable blobs,
+-       * so that they are accessible among erofs fs instances.
+-       */
+-      isb = flags & EROFS_REG_COOKIE_SHARE ? erofs_pseudo_mnt->mnt_sb : sb;
+-      inode = new_inode(isb);
+-      if (!inode) {
+-              erofs_err(sb, "failed to get anon inode for %s", name);
+-              ret = -ENOMEM;
+-              goto err_cookie;
+-      }
+-
+-      inode->i_size = OFFSET_MAX;
+-      inode->i_mapping->a_ops = &erofs_fscache_meta_aops;
+-      mapping_set_gfp_mask(inode->i_mapping, GFP_KERNEL);
+-      inode->i_blkbits = EROFS_SB(sb)->blkszbits;
+-      inode->i_private = ctx;
+-
+-      ctx->cookie = cookie;
+-      ctx->inode = inode;
+-      return ctx;
+-
+-err_cookie:
+-      fscache_unuse_cookie(cookie, NULL, NULL);
+-      fscache_relinquish_cookie(cookie, false);
+-err:
+-      kfree(ctx);
+-      return ERR_PTR(ret);
+-}
+-
+-static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx)
+-{
+-      fscache_unuse_cookie(ctx->cookie, NULL, NULL);
+-      fscache_relinquish_cookie(ctx->cookie, false);
+-      iput(ctx->inode);
+-      kfree(ctx->name);
+-      kfree(ctx);
+-}
+-
+-static struct erofs_fscache *erofs_domain_init_cookie(struct super_block *sb,
+-                                              char *name, unsigned int flags)
+-{
+-      struct erofs_fscache *ctx;
+-      struct erofs_domain *domain = EROFS_SB(sb)->domain;
+-
+-      ctx = erofs_fscache_acquire_cookie(sb, name, flags);
+-      if (IS_ERR(ctx))
+-              return ctx;
+-
+-      ctx->name = kstrdup(name, GFP_KERNEL);
+-      if (!ctx->name) {
+-              erofs_fscache_relinquish_cookie(ctx);
+-              return ERR_PTR(-ENOMEM);
+-      }
+-
+-      refcount_inc(&domain->ref);
+-      ctx->domain = domain;
+-      list_add(&ctx->node, &erofs_domain_cookies_list);
+-      return ctx;
+-}
+-
+-static struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb,
+-                                              char *name, unsigned int flags)
+-{
+-      struct erofs_fscache *ctx;
+-      struct erofs_domain *domain = EROFS_SB(sb)->domain;
+-
+-      flags |= EROFS_REG_COOKIE_SHARE;
+-      mutex_lock(&erofs_domain_cookies_lock);
+-      list_for_each_entry(ctx, &erofs_domain_cookies_list, node) {
+-              if (ctx->domain != domain || strcmp(ctx->name, name))
+-                      continue;
+-              if (!(flags & EROFS_REG_COOKIE_NEED_NOEXIST)) {
+-                      refcount_inc(&ctx->ref);
+-              } else {
+-                      erofs_err(sb, "%s already exists in domain %s", name,
+-                                domain->domain_id);
+-                      ctx = ERR_PTR(-EEXIST);
+-              }
+-              mutex_unlock(&erofs_domain_cookies_lock);
+-              return ctx;
+-      }
+-      ctx = erofs_domain_init_cookie(sb, name, flags);
+-      mutex_unlock(&erofs_domain_cookies_lock);
+-      return ctx;
+-}
+-
+-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb,
+-                                                  char *name,
+-                                                  unsigned int flags)
+-{
+-      if (EROFS_SB(sb)->domain_id)
+-              return erofs_domain_register_cookie(sb, name, flags);
+-      return erofs_fscache_acquire_cookie(sb, name, flags);
+-}
+-
+-void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx)
+-{
+-      struct erofs_domain *domain = NULL;
+-
+-      if (!ctx)
+-              return;
+-      if (!ctx->domain)
+-              return erofs_fscache_relinquish_cookie(ctx);
+-
+-      mutex_lock(&erofs_domain_cookies_lock);
+-      if (refcount_dec_and_test(&ctx->ref)) {
+-              domain = ctx->domain;
+-              list_del(&ctx->node);
+-              erofs_fscache_relinquish_cookie(ctx);
+-      }
+-      mutex_unlock(&erofs_domain_cookies_lock);
+-      if (domain)
+-              erofs_fscache_domain_put(domain);
+-}
+-
+-int erofs_fscache_register_fs(struct super_block *sb)
+-{
+-      int ret;
+-      struct erofs_sb_info *sbi = EROFS_SB(sb);
+-      struct erofs_fscache *fscache;
+-      unsigned int flags = 0;
+-
+-      if (sbi->domain_id)
+-              ret = erofs_fscache_register_domain(sb);
+-      else
+-              ret = erofs_fscache_register_volume(sb);
+-      if (ret)
+-              return ret;
+-
+-      /*
+-       * When shared domain is enabled, using NEED_NOEXIST to guarantee
+-       * the primary data blob (aka fsid) is unique in the shared domain.
+-       *
+-       * For non-shared-domain case, fscache_acquire_volume() invoked by
+-       * erofs_fscache_register_volume() has already guaranteed
+-       * the uniqueness of primary data blob.
+-       *
+-       * Acquired domain/volume will be relinquished in kill_sb() on error.
+-       */
+-      if (sbi->domain_id)
+-              flags |= EROFS_REG_COOKIE_NEED_NOEXIST;
+-      fscache = erofs_fscache_register_cookie(sb, sbi->fsid, flags);
+-      if (IS_ERR(fscache))
+-              return PTR_ERR(fscache);
+-
+-      sbi->dif0.fscache = fscache;
+-      return 0;
+-}
+-
+-void erofs_fscache_unregister_fs(struct super_block *sb)
+-{
+-      struct erofs_sb_info *sbi = EROFS_SB(sb);
+-
+-      erofs_fscache_unregister_cookie(sbi->dif0.fscache);
+-
+-      if (sbi->domain)
+-              erofs_fscache_domain_put(sbi->domain);
+-      else
+-              fscache_relinquish_volume(sbi->volume, NULL, false);
+-
+-      sbi->dif0.fscache = NULL;
+-      sbi->volume = NULL;
+-      sbi->domain = NULL;
+-}
+diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
+index e0c47da4f09e0..45afe5c50de83 100644
+--- a/fs/erofs/inode.c
++++ b/fs/erofs/inode.c
+@@ -256,7 +256,7 @@ static int erofs_fill_inode(struct inode *inode)
+       }
+       mapping_set_large_folios(inode->i_mapping);
+-      aops = erofs_get_aops(inode, false);
++      aops = erofs_get_aops(inode);
+       if (IS_ERR(aops))
+               return PTR_ERR(aops);
+       inode->i_mapping->a_ops = aops;
+diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
+index 9e2ae7b619773..580f8d9f14e7f 100644
+--- a/fs/erofs/internal.h
++++ b/fs/erofs/internal.h
+@@ -43,7 +43,6 @@ typedef u64 erofs_blk_t;
+ struct erofs_device_info {
+       char *path;
+-      struct erofs_fscache *fscache;
+       struct file *file;
+       struct dax_device *dax_dev;
+       u64 fsoff, dax_part_off;
+@@ -80,24 +79,6 @@ struct erofs_sb_lz4_info {
+       u16 max_pclusterblks;
+ };
+-struct erofs_domain {
+-      refcount_t ref;
+-      struct list_head list;
+-      struct fscache_volume *volume;
+-      char *domain_id;
+-};
+-
+-struct erofs_fscache {
+-      struct fscache_cookie *cookie;
+-      struct inode *inode;    /* anonymous inode for the blob */
+-
+-      /* used for share domain mode */
+-      struct erofs_domain *domain;
+-      struct list_head node;
+-      refcount_t ref;
+-      char *name;
+-};
+-
+ struct erofs_xattr_prefix_item {
+       struct erofs_xattr_long_prefix *prefix;
+       u8 infix_len;
+@@ -162,10 +143,6 @@ struct erofs_sb_info {
+       struct completion s_kobj_unregister;
+       erofs_off_t dir_ra_bytes;
+-      /* fscache support */
+-      struct fscache_volume *volume;
+-      struct erofs_domain *domain;
+-      char *fsid;
+       char *domain_id;
+ };
+@@ -191,12 +168,6 @@ static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi)
+ extern struct file_system_type erofs_anon_fs_type;
+-static inline bool erofs_is_fscache_mode(struct super_block *sb)
+-{
+-      return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) &&
+-                      !erofs_is_fileio_mode(EROFS_SB(sb)) && !sb->s_bdev;
+-}
+-
+ enum {
+       EROFS_ZIP_CACHE_DISABLED,
+       EROFS_ZIP_CACHE_READAHEAD,
+@@ -413,11 +384,9 @@ struct erofs_map_dev {
+ };
+ extern const struct super_operations erofs_sops;
+-
+ extern const struct address_space_operations erofs_aops;
+ extern const struct address_space_operations erofs_fileio_aops;
+ extern const struct address_space_operations z_erofs_aops;
+-extern const struct address_space_operations erofs_fscache_access_aops;
+ extern const struct inode_operations erofs_generic_iops;
+ extern const struct inode_operations erofs_symlink_iops;
+@@ -430,10 +399,6 @@ extern const struct file_operations erofs_ishare_fops;
+ extern const struct iomap_ops z_erofs_iomap_report_ops;
+-/* flags for erofs_fscache_register_cookie() */
+-#define EROFS_REG_COOKIE_SHARE                0x0001
+-#define EROFS_REG_COOKIE_NEED_NOEXIST 0x0002
+-
+ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf,
+                         erofs_off_t *offset, int *lengthp);
+ void erofs_unmap_metabuf(struct erofs_buf *buf);
+@@ -473,7 +438,7 @@ static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count)
+ }
+ static inline const struct address_space_operations *
+-erofs_get_aops(struct inode *realinode, bool no_fscache)
++erofs_get_aops(struct inode *realinode)
+ {
+       if (erofs_inode_is_data_compressed(EROFS_I(realinode)->datalayout)) {
+               if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP))
+@@ -483,9 +448,6 @@ erofs_get_aops(struct inode *realinode, bool no_fscache)
+                         "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
+               return &z_erofs_aops;
+       }
+-      if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !no_fscache &&
+-          erofs_is_fscache_mode(realinode->i_sb))
+-              return &erofs_fscache_access_aops;
+       if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) &&
+           erofs_is_fileio_mode(EROFS_SB(realinode->i_sb)))
+               return &erofs_fileio_aops;
+@@ -548,36 +510,6 @@ static inline struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev) { r
+ static inline void erofs_fileio_submit_bio(struct bio *bio) {}
+ #endif
+-#ifdef CONFIG_EROFS_FS_ONDEMAND
+-int erofs_fscache_register_fs(struct super_block *sb);
+-void erofs_fscache_unregister_fs(struct super_block *sb);
+-
+-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb,
+-                                      char *name, unsigned int flags);
+-void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache);
+-struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev);
+-void erofs_fscache_submit_bio(struct bio *bio);
+-#else
+-static inline int erofs_fscache_register_fs(struct super_block *sb)
+-{
+-      return -EOPNOTSUPP;
+-}
+-static inline void erofs_fscache_unregister_fs(struct super_block *sb) {}
+-
+-static inline
+-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb,
+-                                      char *name, unsigned int flags)
+-{
+-      return ERR_PTR(-EOPNOTSUPP);
+-}
+-
+-static inline void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache)
+-{
+-}
+-static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) { return NULL; }
+-static inline void erofs_fscache_submit_bio(struct bio *bio) {}
+-#endif
+-
+ #ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE
+ int __init erofs_init_ishare(void);
+ void erofs_exit_ishare(void);
+diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
+index 35cbd0bc04d7d..0868c12fc15b4 100644
+--- a/fs/erofs/ishare.c
++++ b/fs/erofs/ishare.c
+@@ -45,7 +45,7 @@ bool erofs_ishare_fill_inode(struct inode *inode)
+       struct erofs_inode_fingerprint fp;
+       struct inode *si;
+-      aops = erofs_get_aops(inode, true);
++      aops = erofs_get_aops(inode);
+       if (IS_ERR(aops))
+               return false;
+       if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
+diff --git a/fs/erofs/super.c b/fs/erofs/super.c
+index 579443e6acfeb..86fa5c6a0c708 100644
+--- a/fs/erofs/super.c
++++ b/fs/erofs/super.c
+@@ -126,7 +126,6 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
+                            struct erofs_device_info *dif, erofs_off_t *pos)
+ {
+       struct erofs_sb_info *sbi = EROFS_SB(sb);
+-      struct erofs_fscache *fscache;
+       struct erofs_deviceslot *dis;
+       struct file *file;
+       bool _48bit;
+@@ -145,12 +144,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
+                       return -ENOMEM;
+       }
+-      if (erofs_is_fscache_mode(sb)) {
+-              fscache = erofs_fscache_register_cookie(sb, dif->path, 0);
+-              if (IS_ERR(fscache))
+-                      return PTR_ERR(fscache);
+-              dif->fscache = fscache;
+-      } else if (!sbi->devs->flatdev) {
++      if (!sbi->devs->flatdev) {
+               file = erofs_is_fileio_mode(sbi) ?
+                               filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) :
+                               bdev_file_open_by_path(dif->path,
+@@ -216,7 +210,7 @@ static int erofs_scan_devices(struct super_block *sb,
+       if (!ondisk_extradevs)
+               return 0;
+-      if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb))
++      if (!sbi->devs->extra_devices)
+               sbi->devs->flatdev = true;
+       sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
+@@ -372,8 +366,6 @@ static int erofs_read_superblock(struct super_block *sb)
+               erofs_info(sb, "EXPERIMENTAL 48-bit layout support in use. Use at your own risk!");
+       if (erofs_sb_has_metabox(sbi))
+               erofs_info(sb, "EXPERIMENTAL metadata compression support in use. Use at your own risk!");
+-      if (erofs_is_fscache_mode(sb))
+-              erofs_info(sb, "[deprecated] fscache-based on-demand read feature in use. Use at your own risk!");
+ out:
+       erofs_put_metabuf(&buf);
+       return ret;
+@@ -393,8 +385,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi)
+ enum {
+       Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,
+-      Opt_device, Opt_fsid, Opt_domain_id, Opt_directio, Opt_fsoffset,
+-      Opt_inode_share,
++      Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,
+ };
+ static const struct constant_table erofs_param_cache_strategy[] = {
+@@ -418,7 +409,6 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = {
+       fsparam_flag("dax",             Opt_dax),
+       fsparam_enum("dax",             Opt_dax_enum, erofs_dax_param_enums),
+       fsparam_string("device",        Opt_device),
+-      fsparam_string("fsid",          Opt_fsid),
+       fsparam_string("domain_id",     Opt_domain_id),
+       fsparam_flag_no("directio",     Opt_directio),
+       fsparam_u64("fsoffset",         Opt_fsoffset),
+@@ -509,25 +499,14 @@ static int erofs_fc_parse_param(struct fs_context *fc,
+               }
+               ++sbi->devs->extra_devices;
+               break;
+-#ifdef CONFIG_EROFS_FS_ONDEMAND
+-      case Opt_fsid:
+-              kfree(sbi->fsid);
+-              sbi->fsid = kstrdup(param->string, GFP_KERNEL);
+-              if (!sbi->fsid)
+-                      return -ENOMEM;
+-              break;
+-#endif
+-#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE)
+       case Opt_domain_id:
+-              kfree_sensitive(sbi->domain_id);
+-              sbi->domain_id = no_free_ptr(param->string);
+-              break;
+-#else
+-      case Opt_fsid:
+-      case Opt_domain_id:
+-              errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
++              if (!IS_ENABLED(CONFIG_EROFS_FS_PAGE_CACHE_SHARE)) {
++                      errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
++              } else {
++                      kfree_sensitive(sbi->domain_id);
++                      sbi->domain_id = no_free_ptr(param->string);
++              }
+               break;
+-#endif
+       case Opt_directio:
+               if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE))
+                       errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
+@@ -620,12 +599,7 @@ static void erofs_set_sysfs_name(struct super_block *sb)
+ {
+       struct erofs_sb_info *sbi = EROFS_SB(sb);
+-      if (sbi->domain_id && sbi->fsid)
+-              super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id,
+-                                           sbi->fsid);
+-      else if (sbi->fsid)
+-              super_set_sysfs_name_generic(sb, "%s", sbi->fsid);
+-      else if (erofs_is_fileio_mode(sbi))
++      if (erofs_is_fileio_mode(sbi))
+               super_set_sysfs_name_generic(sb, "%s",
+                                            bdi_dev_name(sb->s_bdi));
+       else
+@@ -680,11 +654,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
+               sb->s_blocksize = PAGE_SIZE;
+               sb->s_blocksize_bits = PAGE_SHIFT;
+-              if (erofs_is_fscache_mode(sb)) {
+-                      err = erofs_fscache_register_fs(sb);
+-                      if (err)
+-                              return err;
+-              }
+               err = super_setup_bdi(sb);
+               if (err)
+                       return err;
+@@ -703,11 +672,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
+               return err;
+       if (sb->s_blocksize_bits != sbi->blkszbits) {
+-              if (erofs_is_fscache_mode(sb)) {
+-                      errorfc(fc, "unsupported blksize for fscache mode");
+-                      return -EINVAL;
+-              }
+-
+               if (erofs_is_fileio_mode(sbi)) {
+                       sb->s_blocksize = 1 << sbi->blkszbits;
+                       sb->s_blocksize_bits = sbi->blkszbits;
+@@ -716,14 +680,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
+                       return -EINVAL;
+               }
+       }
+-
+-      if (sbi->dif0.fsoff) {
+-              if (sbi->dif0.fsoff & (sb->s_blocksize - 1))
+-                      return invalfc(fc, "fsoffset %llu is not aligned to block size %lu",
+-                                     sbi->dif0.fsoff, sb->s_blocksize);
+-              if (erofs_is_fscache_mode(sb))
+-                      return invalfc(fc, "cannot use fsoffset in fscache mode");
+-      }
++      if (sbi->dif0.fsoff & (sb->s_blocksize - 1))
++              return invalfc(fc, "fsoffset %llu is not aligned to block size %lu",
++                             sbi->dif0.fsoff, sb->s_blocksize);
+       if (test_opt(&sbi->opt, DAX_ALWAYS) && sbi->blkszbits != PAGE_SHIFT) {
+               erofs_info(sb, "unsupported blocksize for DAX");
+@@ -793,16 +752,13 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
+ static int erofs_fc_get_tree(struct fs_context *fc)
+ {
+-      struct erofs_sb_info *sbi = fc->s_fs_info;
+       int ret;
+-      if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid)
+-              return get_tree_nodev(fc, erofs_fc_fill_super);
+-
+       ret = get_tree_bdev_flags(fc, erofs_fc_fill_super,
+               IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ?
+                       GET_TREE_BDEV_QUIET_LOOKUP : 0);
+       if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && ret == -ENOTBLK) {
++              struct erofs_sb_info *sbi = fc->s_fs_info;
+               struct file *file;
+               if (!fc->source)
+@@ -827,8 +783,8 @@ static int erofs_fc_reconfigure(struct fs_context *fc)
+       DBG_BUGON(!sb_rdonly(sb));
+-      if (new_sbi->fsid || new_sbi->domain_id)
+-              erofs_info(sb, "ignoring reconfiguration for fsid|domain_id.");
++      if (new_sbi->domain_id)
++              erofs_info(sb, "ignoring reconfiguration for domain_id.");
+       if (test_opt(&new_sbi->opt, POSIX_ACL))
+               fc->sb_flags |= SB_POSIXACL;
+@@ -848,8 +804,6 @@ static int erofs_release_device_info(int id, void *ptr, void *data)
+       fs_put_dax(dif->dax_dev, NULL);
+       if (dif->file)
+               fput(dif->file);
+-      erofs_fscache_unregister_cookie(dif->fscache);
+-      dif->fscache = NULL;
+       kfree(dif->path);
+       kfree(dif);
+       return 0;
+@@ -867,7 +821,6 @@ static void erofs_free_dev_context(struct erofs_dev_context *devs)
+ static void erofs_sb_free(struct erofs_sb_info *sbi)
+ {
+       erofs_free_dev_context(sbi->devs);
+-      kfree(sbi->fsid);
+       kfree_sensitive(sbi->domain_id);
+       if (sbi->dif0.file)
+               fput(sbi->dif0.file);
+@@ -928,14 +881,12 @@ static void erofs_kill_sb(struct super_block *sb)
+ {
+       struct erofs_sb_info *sbi = EROFS_SB(sb);
+-      if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) ||
+-          sbi->dif0.file)
++      if (sbi->dif0.file)
+               kill_anon_super(sb);
+       else
+               kill_block_super(sb);
+       erofs_drop_internal_inodes(sbi);
+       fs_put_dax(sbi->dif0.dax_dev, NULL);
+-      erofs_fscache_unregister_fs(sb);
+       erofs_sb_free(sbi);
+       sb->s_fs_info = NULL;
+ }
+@@ -950,7 +901,6 @@ static void erofs_put_super(struct super_block *sb)
+       erofs_drop_internal_inodes(sbi);
+       erofs_free_dev_context(sbi->devs);
+       sbi->devs = NULL;
+-      erofs_fscache_unregister_fs(sb);
+ }
+ static struct file_system_type erofs_fs_type = {
+@@ -962,14 +912,12 @@ static struct file_system_type erofs_fs_type = {
+ };
+ MODULE_ALIAS_FS("erofs");
+-#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE)
++#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE
+ static void erofs_free_anon_inode(struct inode *inode)
+ {
+       struct erofs_inode *vi = EROFS_I(inode);
+-#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE
+       kfree(vi->fingerprint.opaque);
+-#endif
+       kmem_cache_free(erofs_inode_cachep, vi);
+ }
+@@ -1099,12 +1047,6 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root)
+               seq_puts(seq, ",dax=never");
+       if (erofs_is_fileio_mode(sbi) && test_opt(opt, DIRECT_IO))
+               seq_puts(seq, ",directio");
+-      if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND)) {
+-              if (sbi->fsid)
+-                      seq_printf(seq, ",fsid=%s", sbi->fsid);
+-              if (sbi->domain_id)
+-                      seq_printf(seq, ",domain_id=%s", sbi->domain_id);
+-      }
+       if (sbi->dif0.fsoff)
+               seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff);
+       if (test_opt(opt, INODE_SHARE))
+diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
+index c6240dccbb0f0..5fa6651147eaa 100644
+--- a/fs/erofs/zdata.c
++++ b/fs/erofs/zdata.c
+@@ -1714,8 +1714,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f,
+ drain_io:
+                               if (erofs_is_fileio_mode(EROFS_SB(sb)))
+                                       erofs_fileio_submit_bio(bio);
+-                              else if (erofs_is_fscache_mode(sb))
+-                                      erofs_fscache_submit_bio(bio);
+                               else
+                                       submit_bio(bio);
+@@ -1744,8 +1742,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f,
+                       if (!bio) {
+                               if (erofs_is_fileio_mode(EROFS_SB(sb)))
+                                       bio = erofs_fileio_bio_alloc(&mdev);
+-                              else if (erofs_is_fscache_mode(sb))
+-                                      bio = erofs_fscache_bio_alloc(&mdev);
+                               else
+                                       bio = bio_alloc(mdev.m_bdev, BIO_MAX_VECS,
+                                                       REQ_OP_READ, GFP_NOIO);
+@@ -1774,8 +1770,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f,
+       if (bio) {
+               if (erofs_is_fileio_mode(EROFS_SB(sb)))
+                       erofs_fileio_submit_bio(bio);
+-              else if (erofs_is_fscache_mode(sb))
+-                      erofs_fscache_submit_bio(bio);
+               else
+                       submit_bio(bio);
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch b/queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch
new file mode 100644 (file)
index 0000000..89cddaf
--- /dev/null
@@ -0,0 +1,147 @@
+From 47723c3eef971d69af7904cd0e1b3d3282f4f1c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 21:13:42 -0700
+Subject: ethtool: Embed FEC hist ranges as buffer in struct
+
+From: Eric Joyner <eric.joyner@amd.com>
+
+[ Upstream commit 97ac08560d236ca17f6606d9e671118e5eae5721 ]
+
+When a driver's .get_fec_stats() handler is called and the driver
+supports FEC histogram stats, the driver supplies the histogram bin
+ranges via a pointer.  This pointer is assigned while under the netdev
+ops lock in fec_prepare_data(), but the actual data is only read after
+the lock is released; so this allows the driver to change the ranges
+(e.g. from another .get_fec_stats() call) while the current call chain
+is reading them in fec_fill_reply().
+
+Fix this by adding an ethtool core-owned buffer, ranges_buf, to struct
+ethtool_fec_hist. Drivers whose ranges are built dynamically (currently
+just mlx5) fill ranges_buf and then point the existing ranges pointer at
+it, giving ethtool a consistent copy that stays valid after the netdev
+ops lock is dropped and later in fec_fill_reply(). Drivers whose ranges
+are compile-time constants (bnxt, netdevsim) are unaffected by the
+potential race and keep setting the existing ranges pointer to their
+constant array, without making copies.
+
+Fixes: cc2f08129925 ("ethtool: add FEC bins histogram report")
+Signed-off-by: Eric Joyner <eric.joyner@amd.com>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Link: https://patch.msgid.link/20260723041342.39238-1-eric.joyner@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mellanox/mlx5/core/en.h  |  1 -
+ .../net/ethernet/mellanox/mlx5/core/en_main.c |  7 -------
+ .../ethernet/mellanox/mlx5/core/en_stats.c    | 19 +++++++++----------
+ include/linux/ethtool.h                       |  1 +
+ 4 files changed, 10 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
+index d507289096c20..6867a5aed42c0 100644
+--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
++++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
+@@ -984,7 +984,6 @@ struct mlx5e_priv {
+       struct mlx5e_mqprio_rl    *mqprio_rl;
+       struct dentry             *dfs_root;
+       struct mlx5_devcom_comp_dev *devcom;
+-      struct ethtool_fec_hist_range *fec_ranges;
+ };
+ static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
+diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+index fd442109aea8c..7d1063c7bf649 100644
+--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+@@ -6374,14 +6374,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv,
+       if (!priv->channel_stats)
+               goto err_free_tx_rates;
+-      priv->fec_ranges = kzalloc_objs(*priv->fec_ranges, ETHTOOL_FEC_HIST_MAX);
+-      if (!priv->fec_ranges)
+-              goto err_free_channel_stats;
+-
+       return 0;
+-err_free_channel_stats:
+-      kfree(priv->channel_stats);
+ err_free_tx_rates:
+       kfree(priv->tx_rates);
+ err_free_txq2sq_stats:
+@@ -6406,7 +6400,6 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv)
+       if (!priv->mdev)
+               return;
+-      kfree(priv->fec_ranges);
+       for (i = 0; i < priv->stats_nch; i++)
+               kvfree(priv->channel_stats[i]);
+       kfree(priv->channel_stats);
+diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+index 8632b73179cbc..bba51e198d731 100644
+--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+@@ -1551,7 +1551,7 @@ static bool fec_rs_validate_hist_type(int mode, int hist_type)
+ static u8
+ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
+-                           const struct ethtool_fec_hist_range **ranges)
++                           struct ethtool_fec_hist_range *ranges)
+ {
+       struct mlx5_core_dev *mdev = priv->mdev;
+       u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0};
+@@ -1559,8 +1559,6 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
+       int sz = MLX5_ST_SZ_BYTES(pphcr_reg);
+       u8 hist_type, num_of_bins;
+-      memset(priv->fec_ranges, 0,
+-             ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges));
+       MLX5_SET(pphcr_reg, in, local_port, 1);
+       if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0))
+               return 0;
+@@ -1576,12 +1574,11 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
+       for (int i = 0; i < num_of_bins; i++) {
+               void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]);
+-              priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range,
+-                                                  high_val);
+-              priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
+-                                                 low_val);
++              ranges[i].high = MLX5_GET(bin_range_layout, bin_range,
++                                        high_val);
++              ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
++                                       low_val);
+       }
+-      *ranges = priv->fec_ranges;
+       return num_of_bins;
+ }
+@@ -1623,10 +1620,12 @@ static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode,
+       case MLX5E_FEC_LLRS_272_257_1:
+       case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD:
+               num_of_bins =
+-                      fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges);
+-              if (num_of_bins)
++                      fec_rs_histogram_fill_ranges(priv, mode, hist->ranges_buf);
++              if (num_of_bins) {
++                      hist->ranges = hist->ranges_buf;
+                       return fec_rs_histogram_fill_stats(priv, num_of_bins,
+                                                          hist);
++              }
+               break;
+       default:
+               return;
+diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
+index 1cb0740ba3310..091d382f0a2d3 100644
+--- a/include/linux/ethtool.h
++++ b/include/linux/ethtool.h
+@@ -560,6 +560,7 @@ struct ethtool_fec_hist {
+               u64 per_lane[ETHTOOL_MAX_LANES];
+       } values[ETHTOOL_FEC_HIST_MAX];
+       const struct ethtool_fec_hist_range *ranges;
++      struct ethtool_fec_hist_range ranges_buf[ETHTOOL_FEC_HIST_MAX];
+ };
+ /**
+  * struct ethtool_fec_stats - statistics for IEEE 802.3 FEC
+-- 
+2.53.0
+
diff --git a/queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
new file mode 100644 (file)
index 0000000..f0d94b9
--- /dev/null
@@ -0,0 +1,47 @@
+From abb54503a418273ce0e6033ced93bd075c4764c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 17:26:37 +0800
+Subject: forcedeth: fix UAF of txrx_stats in nv_remove
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
+
+nv_remove() frees the per-CPU txrx_stats before unregister_netdev().
+Until unregister completes, ndo_get_stats64, the NAPI/xmit data path,
+and nv_close()/drain may still access txrx_stats, leading to a
+use-after-free.
+
+Free the stats only after unregister_netdev().
+
+Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
+Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/nvidia/forcedeth.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
+index 5b0435d7bc395..58d3e55def486 100644
+--- a/drivers/net/ethernet/nvidia/forcedeth.c
++++ b/drivers/net/ethernet/nvidia/forcedeth.c
+@@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev)
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct fe_priv *np = netdev_priv(dev);
+-      free_percpu(np->txrx_stats);
+-
+       unregister_netdev(dev);
++      free_percpu(np->txrx_stats);
++
+       nv_restore_mac_addr(pci_dev);
+       /* restore any phy related changes */
+-- 
+2.53.0
+
diff --git a/queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch b/queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch
new file mode 100644 (file)
index 0000000..a4f8d16
--- /dev/null
@@ -0,0 +1,48 @@
+From d26d8ce659b53dc06a32d5dc7a6e90c4985d2f0b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 08:27:33 +0900
+Subject: fprobe: Fix module reference count leak on error in register_fprobe()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 ]
+
+In register_fprobe(), get_ips_from_filter() resolves target function
+addresses and increments module reference counts via try_module_get() for
+symbols in kernel modules. If get_ips_from_filter() fails on the second
+pass and returns an error, register_fprobe() returned directly without
+releasing module references acquired up to that point.
+
+Fix this by ensuring the cleanup loop executing module_put() runs even when
+get_ips_from_filter() returns a negative error.
+
+Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/
+
+Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/fprobe.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
+index f215990b90613..f681015413b83 100644
+--- a/kernel/trace/fprobe.c
++++ b/kernel/trace/fprobe.c
+@@ -961,10 +961,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
+               return -ENOMEM;
+       ret = get_ips_from_filter(filter, notfilter, addrs, mods, num);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = register_fprobe_ips(fp, addrs, ret);
++      if (ret >= 0)
++              ret = register_fprobe_ips(fp, addrs, ret);
+       for (int i = 0; i < num; i++) {
+               if (mods[i])
+-- 
+2.53.0
+
diff --git a/queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch b/queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch
new file mode 100644 (file)
index 0000000..b13d418
--- /dev/null
@@ -0,0 +1,63 @@
+From 983162d1ae405a51c6f5a177af70320dab9613da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 09:42:28 -0700
+Subject: gpio: gpio-by-pinctrl: Apply initial value in direction output
+ wrapper
+
+From: Alex Tran <alex.tran@oss.qualcomm.com>
+
+[ Upstream commit 67ff4bf723c8bd1f1b10450fa3e8f55762418104 ]
+
+After successfully configuring gpio pin as output, set the
+requested initial output value via the existing gpio set
+wrapper, so that the pin is not left at its previous level.
+
+Fixes: 7671f4949a6c ("gpio: gpio-by-pinctrl: add pinctrl based generic GPIO driver")
+Signed-off-by: Alex Tran <alex.tran@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Link: https://patch.msgid.link/20260724-gpio-pinctrl-output-set-val-v2-1-cad55d025636@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpio-by-pinctrl.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c
+index ddfdc479d38a8..b3b3c11376172 100644
+--- a/drivers/gpio/gpio-by-pinctrl.c
++++ b/drivers/gpio/gpio-by-pinctrl.c
+@@ -28,12 +28,6 @@ static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int off
+       return GPIO_LINE_DIRECTION_IN;
+ }
+-static int pin_control_gpio_direction_output(struct gpio_chip *chip,
+-                                           unsigned int offset, int val)
+-{
+-      return pinctrl_gpio_direction_output(chip, offset);
+-}
+-
+ static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset)
+ {
+       unsigned long config;
+@@ -56,6 +50,18 @@ static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset,
+       return pinctrl_gpio_set_config(chip, offset, config);
+ }
++static int pin_control_gpio_direction_output(struct gpio_chip *chip,
++                                           unsigned int offset, int val)
++{
++      int ret;
++
++      ret = pinctrl_gpio_direction_output(chip, offset);
++      if (ret)
++              return ret;
++
++      return pin_control_gpio_set(chip, offset, val);
++}
++
+ static int pin_control_gpio_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+-- 
+2.53.0
+
diff --git a/queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
new file mode 100644 (file)
index 0000000..16a46ae
--- /dev/null
@@ -0,0 +1,55 @@
+From 8c3d87466efaed739b0b93492e0fd130a505f5ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:23:08 +0530
+Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe()
+
+From: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+
+[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ]
+
+The memory allocated for priv->blob.data is not freed in the error paths
+that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as
+well as in the remove function. Fix that by using device managed action
+to free the memory on remove.
+
+Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling")
+Signed-off-by: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
+Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+index 969dddd3d6faf..0f4a6228a7488 100644
+--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c
++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
+@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val)
+       return 0;
+ }
++static void fops_buf_release(void *data)
++{
++      struct gpio_la_poll_priv *priv = data;
++
++      vfree(priv->blob.data);
++}
++
+ static int fops_buf_size_set(void *data, u64 val)
+ {
+       struct gpio_la_poll_priv *priv = data;
+@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
+               return ret;
+       fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
++      ret = devm_add_action_or_reset(dev, fops_buf_release, priv);
++      if (ret)
++              return ret;
+       priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN);
+       if (IS_ERR(priv->descs))
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
new file mode 100644 (file)
index 0000000..34b852b
--- /dev/null
@@ -0,0 +1,51 @@
+From 5e9064825c76c2d287c39df92a010fc9baa062e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:19 -0300
+Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
+
+When userspace configures 'auto_update_interval' to 0 via sysfs, the
+background kthread executes schedule_timeout_interruptible(0), which
+returns immediately.
+
+If 'num_temp_sensors' is concurrently or previously set to 0, the
+msleep_interruptible() delay inside adt7470_read_temperatures() also
+becomes 0. This combination forces the background thread into a tight,
+unbounded busy-loop, hogging the CPU and flooding the I2C bus with a
+continuous stream of transactions.
+
+Fix this vulnerability by raising the lower limit of the clamp_val in
+auto_update_interval_store() from 0 to 500 milliseconds. This guarantees
+a reasonable minimum sleep window between sensor updates, protecting the
+system from intentional or accidental I2C bus denial of service.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 62ec68ea0a406..0b19b0925d1c7 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+-      temp = clamp_val(temp, 0, 60000);
++      temp = clamp_val(temp, 500, 60000);
+       mutex_lock(&data->lock);
+       data->auto_update_interval = temp;
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
new file mode 100644 (file)
index 0000000..c54a362
--- /dev/null
@@ -0,0 +1,83 @@
+From b495e61d1c5cb59962d524a066c1e8569977d99b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:18 -0300
+Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
+
+adt7470_temp_write() and adt7470_pwm_write() update the driver's
+cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing
+the corresponding regmap_write(), and never check whether the write
+succeeded before committing that update. If the I2C transaction fails,
+the function correctly propagates the error to the caller, but the cache
+silently keeps the new value, which was never actually applied to the
+hardware. Subsequent reads then report a value that does not match the
+device state.
+
+Reorder both write paths to update the cache only after a successful
+regmap_write(), so the cache always reflects what was actually
+written to the hardware.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 481d51617f4be..62ec68ea0a406 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
+       switch (attr) {
+       case hwmon_temp_min:
+               mutex_lock(&data->lock);
+-              data->temp_min[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
++              if (!err)
++                      data->temp_min[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_temp_max:
+               mutex_lock(&data->lock);
+-              data->temp_max[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
++              if (!err)
++                      data->temp_max[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       default:
+@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+       case hwmon_pwm_input:
+               val = clamp_val(val, 0, 255);
+               mutex_lock(&data->lock);
+-              data->pwm[channel] = val;
+               err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
+-                                 data->pwm[channel]);
++                                 val);
++              if (!err)
++                      data->pwm[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_enable:
+@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
+               val--;
+               mutex_lock(&data->lock);
+-              data->pwm_automatic[channel] = val;
+               err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
+                                        pwm_auto_reg_mask,
+                                        val ? pwm_auto_reg_mask : 0);
++              if (!err)
++                      data->pwm_automatic[channel] = val;
+               mutex_unlock(&data->lock);
+               break;
+       case hwmon_pwm_freq:
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
new file mode 100644 (file)
index 0000000..888dee2
--- /dev/null
@@ -0,0 +1,80 @@
+From d2f4c36417bf591c232f3328f1cffda306e5001d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:23 -0300
+Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
+
+If the fan data becomes 0 between the FAN_DATA_VALID() check and the
+FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
+due to a race with a concurrent update of the cached fan value.
+
+Fix a TOCTOU issue by reading fan data once.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 1fbca4869b7b6..772d2a409bb5c 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
+ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
+ {
+       struct adt7470_data *data = adt7470_update_device(dev);
++      u16 fan_data;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       switch (attr) {
+       case hwmon_fan_input:
+-              if (FAN_DATA_VALID(data->fan[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan[channel]);
+               break;
+       case hwmon_fan_min:
+-              if (FAN_DATA_VALID(data->fan_min[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_min[channel]);
+               break;
+       case hwmon_fan_max:
+-              if (FAN_DATA_VALID(data->fan_max[channel]))
+-                      *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
+-              else
+-                      *val = 0;
++              fan_data = READ_ONCE(data->fan_max[channel]);
+               break;
+       case hwmon_fan_alarm:
+               *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+-              break;
++              return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
++      if (FAN_DATA_VALID(fan_data))
++              *val = FAN_PERIOD_TO_RPM(fan_data);
++      else
++              *val = 0;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
new file mode 100644 (file)
index 0000000..e7961a3
--- /dev/null
@@ -0,0 +1,115 @@
+From 1e256e05a1ce75b5947fbebcf5e19d05c0d3b8a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:17 -0300
+Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
+
+During adt7470_read_temperatures(), the driver temporarily switches
+the PWM channels to manual mode, performs the temperature collection,
+and then restores the original configuration registers.
+
+However, if an I2C transaction fails at any point after entering manual
+mode, the function aborts and returns immediately. This leaves the
+configuration registers un-restored, permanently trapping the fans in
+manual mode.
+
+Introduce a recovery path to ensure that the original PWM configuration
+registers are always restored, even when intermediate I2C operations
+fail.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 664349756dc2b..481d51617f4be 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
+ /* Probe for temperature sensors.  Assumes lock is held */
+ static int adt7470_read_temperatures(struct adt7470_data *data)
+ {
+-      unsigned long res;
++      struct device *dev = regmap_get_device(data->regmap);
++      u8 pwm[ADT7470_FAN_COUNT];
+       unsigned int pwm_cfg[2];
+-      int err;
++      unsigned long res;
++      int err, err2;
+       int i;
+-      u8 pwm[ADT7470_FAN_COUNT];
+       /* save pwm[1-4] config register */
+       err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
+@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
+                                ADT7470_PWM_AUTO_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* write pwm control to whatever it was */
+       err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+                               ADT7470_PWM_COUNT);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* start reading temperature sensors */
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
+       if (err < 0)
+-              return err;
++              goto out_restore;
+       /* Delay is 200ms * number of temp sensors. */
+       res = msleep_interruptible((data->num_temp_sensors >= 0 ?
+@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+                                ADT7470_T05_STB_MASK, 0);
+       if (err < 0)
+-              return err;
++              goto out_restore;
++out_restore:
+       /* restore pwm[1-4] config registers */
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+-      if (err < 0)
+-              return err;
+-      err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{1,2} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
++      err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
++      if (err2 < 0) {
++              dev_warn_ratelimited(dev,
++                                   "failed to restore PWM{3,4} config (%d)\n",
++                                   err2);
++
++              if (!err)
++                      err = err2;
++      }
++
+       if (err < 0)
+               return err;
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
new file mode 100644 (file)
index 0000000..3f05376
--- /dev/null
@@ -0,0 +1,57 @@
+From 9884c3f4849e2bbfb9835e43ad5944183eb9f4be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:24 -0300
+Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
+
+In pwm_auto_temp_store(), the parsed user input was missing bounds
+checks, allowing values > 0xF to overflow into the adjacent channel's
+bits. Furthermore, the value was being incorrectly written to the
+pwm_automatic state array instead of pwm_auto_temp.
+
+Fix this by rejecting values > 0xF with -EINVAL, and assigning the
+value to the correct array only after a successful I2C write.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 772d2a409bb5c..c45b984c02e6b 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       if (temp < 0)
+               return temp;
++      if (temp > 0xF)
++              return -EINVAL;
++
+       mutex_lock(&data->lock);
+-      data->pwm_automatic[attr->index] = temp;
+       if (!(attr->index % 2)) {
+               mask = 0xF0;
+@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
+       }
+       err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
++      if (!err)
++              data->pwm_auto_temp[attr->index] = temp;
++
+       mutex_unlock(&data->lock);
+       return err < 0 ? err : count;
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
new file mode 100644 (file)
index 0000000..abaf65c
--- /dev/null
@@ -0,0 +1,62 @@
+From 0e02b15889d2e78f613b1763c34d70007c4d25ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:20 -0300
+Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
+
+The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are
+currently defined with swapped bit values.
+
+According to Table 22 of the ADT7470 datasheet, the Fan Control Mode
+Configuration for register 0x69 follows the exact same bit position
+layout as register 0x68:
+- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
+- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
+- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
+- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
+
+Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
+
+This typo did not cause any functional bugs because these specific
+macros are never referenced in the driver code. Instead, the driver
+correctly applies the configuration by relying on the modulo parity of
+the channel index (e.g., `channel % 2`) to selectively apply either
+ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40).
+Since the bit layout is identical between the two configuration
+registers, the hardware is currently configured correctly.
+
+Fix the macro definitions to reflect the datasheet accurately and
+prevent future bugs or confusion during code review and refactoring.
+As this is a purely cosmetic fix with no functional impact, a backport
+to stable kernels is not necessary.
+
+Fixes: 6f9703d0be16 ("hwmon: add support for adt7470")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 428bd1d91e700..c6fc7d38d698c 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define               ADT7470_PWM1_AUTO_MASK          0x80
+ #define               ADT7470_PWM_AUTO_MASK           0xC0
+ #define ADT7470_REG_PWM34_CFG                 0x69
+-#define               ADT7470_PWM3_AUTO_MASK          0x40
+-#define               ADT7470_PWM4_AUTO_MASK          0x80
++#define               ADT7470_PWM4_AUTO_MASK          0x40
++#define               ADT7470_PWM3_AUTO_MASK          0x80
+ #define       ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+ #define ADT7470_REG_PWM_MIN_MAX_ADDR          0x6D
+ #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR    0x6E
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
new file mode 100644 (file)
index 0000000..72543b4
--- /dev/null
@@ -0,0 +1,71 @@
+From 3b972ceb36a2c659fe13f2793021211495a41b49 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:21 -0300
+Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
+
+During the conversion the alarm callback started interpreting the
+channel index as an alarm bitmask, resulting in incorrect alarm
+reporting. Compute the proper alarm bit instead.
+
+Reported-by: sashiko-bot@kernel.org
+Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org
+Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index 0b19b0925d1c7..428bd1d91e700 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+ #define ALARM2(x)             ((x) << 8)
++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
++ * 0x72, or BIT(0)..BIT(6) of data->alarm.
++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
++ * 0x73, or BIT(8)..BIT(10) of data->alarm.
++ */
++#define TEMP_ALARM_BIT(ch)    ({              \
++      typeof(ch) _ch = (ch);                  \
++      (1 << (_ch < 7 ? _ch : _ch + 1));       \
++})
++
++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
++ */
++#define FAN_ALARM_BIT(ch)     (1 << (12 + (ch)))
++
+ #define ADT7470_VENDOR                0x41
+ #define ADT7470_DEVICE                0x70
+ /* datasheet only mentions a revision 2 */
+@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
+               *val = 1000 * data->temp_max[channel];
+               break;
+       case hwmon_temp_alarm:
+-              *val = !!(data->alarm & channel);
++              *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val
+                       *val = 0;
+               break;
+       case hwmon_fan_alarm:
+-              *val = !!(data->alarm & (1 << (12 + channel)));
++              *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+               break;
+       default:
+               return -EOPNOTSUPP;
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch
new file mode 100644 (file)
index 0000000..8a1ec0c
--- /dev/null
@@ -0,0 +1,111 @@
+From bc6f271c5b91470fc8246c077c6d8d21a9539e24 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 21:22:22 -0300
+Subject: hwmon: (adt7470) Use cached PWM frequency value
+
+From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
+
+adt7470_pwm_read() currently ignores failures returned by
+pwm1_freq_get(). If the register read fails, the negative error code is
+returned through *val while the function itself reports success,
+potentially exposing a negative PWM frequency through sysfs.
+
+Fix this by using the cached PWM frequency maintained by the driver,
+eliminating the register access from the read path.
+
+Apart from the corrected error propagation and using the cached value,
+no functional change is intended.
+
+Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap")
+Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
+Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/adt7470.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
+index c6fc7d38d698c..1fbca4869b7b6 100644
+--- a/drivers/hwmon/adt7470.c
++++ b/drivers/hwmon/adt7470.c
+@@ -182,6 +182,7 @@ struct adt7470_data {
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
++      u32                     pwm_freq;
+       struct task_struct      *auto_update;
+       unsigned int            auto_update_interval;
+@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
+ }
+ /* These are the valid PWM frequencies to the nearest Hz */
+-static const int adt7470_freq_map[] = {
++static const u32 adt7470_freq_map[] = {
+       11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+ };
+@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
+               *val = 1 + data->pwm_automatic[channel];
+               break;
+       case hwmon_pwm_freq:
+-              *val = pwm1_freq_get(dev);
++              *val = data->pwm_freq;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
+ {
+       struct adt7470_data *data = dev_get_drvdata(dev);
+       unsigned int low_freq = ADT7470_CFG_LF;
++      u32 closest_freq;
+       int index;
+       int err;
+       /* Round the user value given to the closest available frequency */
+       index = find_closest(freq, adt7470_freq_map,
+                            ARRAY_SIZE(adt7470_freq_map));
++      closest_freq = adt7470_freq_map[index];
+       if (index >= 8) {
+               index -= 8;
+@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
+       err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+                                ADT7470_FREQ_MASK,
+                                index << ADT7470_FREQ_SHIFT);
++      if (err < 0)
++              goto out;
++
++      data->pwm_freq = closest_freq;
+ out:
+       mutex_unlock(&data->lock);
+@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client)
+       struct device *dev = &client->dev;
+       struct adt7470_data *data;
+       struct device *hwmon_dev;
++      int freq_val;
+       int err;
+       data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
+@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client)
+       if (err < 0)
+               return err;
++      freq_val = pwm1_freq_get(dev);
++      if (freq_val <= 0) {
++              err = freq_val < 0 ? freq_val : -EINVAL;
++              return err;
++      }
++
++      data->pwm_freq = (u32)freq_val;
++
+       /* Register sysfs hooks */
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adt7470_chip_info,
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch
new file mode 100644 (file)
index 0000000..757d306
--- /dev/null
@@ -0,0 +1,156 @@
+From 0368266b8f4cad2076e56c352d9c176f0529b85f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 10 Jun 2026 07:46:16 -0700
+Subject: hwmon: (ina2xx) Fix various overflow issues
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ]
+
+Sashiko reports several integer overflow problems in the ina2xx driver
+caused by unbounded multiplications and inadequate types for intermediate
+calculations.
+
+Specifically:
+- In ina2xx_get_value(), the return type is changed from int to long.
+  Intermediate calculations for current are now performed using 64-bit
+  types to prevent 32-bit integer overflow before the division by 1000.
+- When calculating power in ina2xx_get_value() and
+  sy24655_average_power_read(), interim values are cast to u64 and clamped
+  to LONG_MAX. This prevents overflow when regval or accumulator_24 is
+  multiplied by power_lsb_uW.
+- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t().
+  This safely avoids integer overflows when scaling user-provided values
+  for shunt voltage, bus voltage, power, and current limits.
+
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
+index 32bf4595bcb48..789eeb718b1ca 100644
+--- a/drivers/hwmon/ina2xx.c
++++ b/drivers/hwmon/ina2xx.c
+@@ -16,6 +16,7 @@
+ #include <linux/i2c.h>
+ #include <linux/init.h>
+ #include <linux/kernel.h>
++#include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+@@ -266,30 +267,34 @@ static u16 ina226_interval_to_reg(long interval)
+       return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits);
+ }
+-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+-                          unsigned int regval)
++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg,
++                           unsigned int regval)
+ {
+-      int val;
++      s64 val64;
++      long val;
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+               /* signed register */
+-              val = (s16)regval >> data->config->shunt_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
++              val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift,
++                                      data->config->shunt_div);
+               break;
+       case INA2XX_BUS_VOLTAGE:
+-              val = (regval >> data->config->bus_voltage_shift) *
+-                data->config->bus_voltage_lsb;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) *
++                                      data->config->bus_voltage_lsb, 1000);
+               break;
+       case INA2XX_POWER:
+-              val = regval * data->power_lsb_uW;
++              val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX);
+               break;
+       case INA2XX_CURRENT:
+               /* signed register, result in mA */
+-              val = ((s16)regval >> data->config->current_shift) *
++              val64 = (s64)((s16)regval >> data->config->current_shift) *
+                 data->current_lsb_uA;
+-              val = DIV_ROUND_CLOSEST(val, 1000);
++              if (val64 < 0)
++                      val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000);
++              else
++                      val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000);
++              val = clamp_val(val64, LONG_MIN, LONG_MAX);
+               break;
+       case INA2XX_CALIBRATION:
+               val = regval;
+@@ -378,27 +383,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
+  */
+ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
+ {
++      long limit;
++
+       switch (reg) {
+       case INA2XX_SHUNT_VOLTAGE:
+-              val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
+-              val *= data->config->shunt_div;
+-              val <<= data->config->shunt_voltage_shift;
+-              return clamp_val(val, 0, SHRT_MAX);
++              val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div));
++              return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift,
++                           SHRT_MAX);
+       case INA2XX_BUS_VOLTAGE:
+-              val = clamp_val(val, 0, 200000);
+-              val = (val * 1000) << data->config->bus_voltage_shift;
+-              val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, 130000);
++              return min_t(long,
++                           DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift,
++                                             data->config->bus_voltage_lsb),
++                           USHRT_MAX);
+       case INA2XX_POWER:
+-              val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW);
+-              val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
+-              return clamp_val(val, 0, USHRT_MAX);
++              val = min_t(long, val, LONG_MAX - data->power_lsb_uW);
++              return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX);
+       case INA2XX_CURRENT:
+-              val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
++              limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000;
++              val = min_t(long, val, limit);
+               /* signed register, result in mA */
+               val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
+-              val <<= data->config->current_shift;
+-              return clamp_val(val, SHRT_MIN, SHRT_MAX);
++              limit = SHRT_MAX >> data->config->current_shift;
++              return (u16)(min_t(long, val, limit) << data->config->current_shift);
+       default:
+               /* programmer goofed */
+               WARN_ON_ONCE(1);
+@@ -537,6 +544,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+       u8 template[6];
+       int ret;
+       long accumulator_24, sample_count;
++      u64 val64;
+       /* 48-bit register read */
+       ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
+@@ -555,7 +563,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
+               return 0;
+       }
+-      *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
++      *val = min_t(u64, val64, LONG_MAX);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
new file mode 100644 (file)
index 0000000..bfaaeb2
--- /dev/null
@@ -0,0 +1,54 @@
+From 3a4ff32d52308f94fbe743a863b0d59fd9d9f0fe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 15:27:28 -0700
+Subject: hwmon: (lm90) Only report alarms if driver is ready
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
+
+Userspace can read sysfs attributes before driver registration is complete,
+immediately after devm_hwmon_device_register_with_info() has been called.
+At that time, data->hwmon_dev is not yet initialized. This can trigger
+a NULL pointer access since lm90_update_device() and with it
+lm90_update_alarms_locked() will be called. This call schedules
+report_work and lm90_report_alarms(), which passes the still-NULL
+data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer
+dereference.
+
+Fix the problem by only scheduling the report and alert workers
+data->hwmon_dev is set.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/lm90.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
+index c78c96e1bd83f..c56dd958429be 100644
+--- a/drivers/hwmon/lm90.c
++++ b/drivers/hwmon/lm90.c
+@@ -1194,7 +1194,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+               check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+                       (data->config & 0x80);
+-              if (force || check_enable)
++              if (data->hwmon_dev && (force || check_enable))
+                       schedule_work(&data->report_work);
+               /*
+@@ -1202,7 +1202,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+                * alarms are all clear, and alerts are currently disabled.
+                * Otherwise (re)schedule worker if needed.
+                */
+-              if (check_enable) {
++              if (check_enable && data->hwmon_dev) {
+                       if (!(data->current_alarms & data->alert_alarms)) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               lm90_update_confreg(data, data->config & ~0x80);
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
new file mode 100644 (file)
index 0000000..3c6961c
--- /dev/null
@@ -0,0 +1,39 @@
+From 6c3e00e85e2bd6942e57a227fdfa0cee5effa45b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Feb 2025 12:27:15 -0800
+Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ]
+
+Coverity reports an out-of-bounds access when reading the minimum alarm
+voltage for the VGPIO channel. Add the missing return statement to fix
+the problem.
+
+Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282")
+Cc: Nuno Sa <nuno.sa@analog.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ltc4282.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
+index b9084424160d6..bdbf370233d71 100644
+--- a/drivers/hwmon/ltc4282.c
++++ b/drivers/hwmon/ltc4282.c
+@@ -375,8 +375,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
+                                                  channel, val);
+       case hwmon_in_min_alarm:
+               if (channel == LTC4282_CHAN_VGPIO)
+-                      ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
+-                                         LTC4282_GPIO_ALARM_L_MASK, val);
++                      return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
++                                                LTC4282_GPIO_ALARM_L_MASK, val);
+               return ltc4282_vdd_source_read_alm(st,
+                                                  LTC4282_VSOURCE_ALARM_L_MASK,
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch
new file mode 100644 (file)
index 0000000..b0f86b5
--- /dev/null
@@ -0,0 +1,90 @@
+From 667c8562186f9731a74dd5e1f52f6c7080af03d9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:14:36 -0700
+Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
+
+Unlike NCT6106, NCT6116 only has three temperature registers, and with
+it only three temperature source and temperature source configuration
+registers. The register addresses match those of NCT6106 and can be
+re-used.
+
+The code used a separate array to list the temperature source registers
+for NCT6116, but used the size of the NCT6106 register array to set
+the number of registers. The NCT6106 register array provides six addresses,
+while the temperature source register array for NCT6116 only provides three
+addresses. This causes a KASAN report.
+
+BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775]
+Read of size 2 at addr ffffffffc19561a6 by task modprobe/954
+...
+Call Trace:
+ dump_stack+0x7d/0xa7
+ print_address_description.constprop.0+0x1c/0x220
+ ? __kasan_kmalloc.constprop.0+0xc9/0xd0
+ ? __kmalloc_node_track_caller+0x194/0x5b0
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+ ? nct6775_probe+0x936/0x46f0 [nct6775]
+...
+
+Fix the problem by hard-coding the number of temperature and temperature
+configuration registers to three for NCT6116. Drop the unnecessary
+NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
+
+Reported-by: Florian Bezdeka <florian.bezdeka@siemens.com>
+Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index d668dc390def8..51253acff4b06 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+ static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+ static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+ static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+-      0xb0, 0xb1, 0xb2 };
+ static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+       0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+@@ -3652,7 +3650,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+                 = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+               data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+               data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+-              data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
++              data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+@@ -3666,13 +3664,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
+               reg_temp = NCT6106_REG_TEMP;
+               reg_temp_mon = NCT6106_REG_TEMP_MON;
+-              num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
++              num_reg_temp = 3;
+               num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
+               num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+               reg_temp_over = NCT6106_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+               reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+-              num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
++              num_reg_temp_config = 3;
+               reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+               reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
new file mode 100644 (file)
index 0000000..787cfc2
--- /dev/null
@@ -0,0 +1,74 @@
+From 82222d5d52e27681bfca9a1e955a0c5c304f32da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 13:35:37 -0700
+Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
+
+Sashiko reports:
+
+During initialization of the nct6116 chip, the driver sets data->pwm_num
+to 5. However, it assigns several NCT6106 register arrays (such as
+NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and
+NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP.
+These arrays only contain 3 elements.
+
+In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If
+data->has_pwm has bits 3 or 4 set (which is structurally possible for
+nct6116), the loop attempts to read elements at index 3 and 4 from these
+3-element arrays. This results in a global out-of-bounds read, which can
+be caught by KASAN.
+
+Furthermore, the driver uses these garbage out-of-bounds values as
+hardware register addresses for subsequent read and write operations. This
+leads to invalid hardware register access, potentially causing hardware
+misconfiguration or system crashes.
+
+The underlying problem is that the chip does support up to five fan
+control channels, but only the first three support weight control.
+Fix the problem by extending the affected weight register arrays with
+zeroed fields. The driver uses zeroed register addresses to determine
+if a register is supported or not, and skips accesses for unsupported
+registers.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116")
+Cc: Björn Gerhart <gerhart@posteo.de>
+Cc: Florian Bezdeka <florian.bezdeka@siemens.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nct6775-core.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
+index 51253acff4b06..94482c8092cda 100644
+--- a/drivers/hwmon/nct6775-core.c
++++ b/drivers/hwmon/nct6775-core.c
+@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
+ static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch
new file mode 100644 (file)
index 0000000..c2d015f
--- /dev/null
@@ -0,0 +1,53 @@
+From f8e0db1c876661a31709f95e1d6de5ca3d30d162 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 09:54:23 -0700
+Subject: hwmon: (nzxt-smart2) DMA-align output buffer
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
+
+Sashiko reports:
+
+When send_output_report() calls hid_hw_output_report(), the underlying USB
+HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
+
+When the DMA mapping flushes or invalidates the cacheline, it will corrupt
+the adjacent variables (mutex, update_interval) that were modified
+concurrently by the CPU. This causes memory corruption due to cacheline
+sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA
+API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings
+for this violation.
+
+Any operation that triggers send_output_report() (like setting a fan speed
+or updating the interval) causes the USB DMA mapping. On systems with
+non-coherent caches, this structural bug causes immediate and deterministic
+memory corruption.
+
+Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.")
+Cc: Aleksandr Mezin <mezin.alexander@gmail.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/nzxt-smart2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
+index e2316c46629d6..ff0c0bee0e839 100644
+--- a/drivers/hwmon/nzxt-smart2.c
++++ b/drivers/hwmon/nzxt-smart2.c
+@@ -203,7 +203,7 @@ struct drvdata {
+        */
+       struct mutex mutex;
+       long update_interval;
+-      u8 output_buffer[OUTPUT_REPORT_SIZE];
++      u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ };
+ static long scale_pwm_value(long val, long orig_max, long new_max)
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
new file mode 100644 (file)
index 0000000..1797fd0
--- /dev/null
@@ -0,0 +1,39 @@
+From 171284e3b2dcdf17662a1aedd30e27a4df159ada Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:41:40 -0700
+Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
+
+pmbus_update_byte_data() is supposed to return a negative error code or 0.
+However, if no change is made to the register, it actually returns the
+register value. This can result in problems if the calling code explicitly
+expects to see an error code or 0.
+
+Fix it to return 0 on success or the error code as expected.
+
+Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write")
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index 3143b9e0316c4..7f2d28e41d816 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -513,7 +513,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+       if (tmp != rv)
+               rv = _pmbus_write_byte_data(client, page, reg, tmp);
+-      return rv;
++      return rv < 0 ? rv : 0;
+ }
+ EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS");
+-- 
+2.53.0
+
diff --git a/queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch
new file mode 100644 (file)
index 0000000..b803c13
--- /dev/null
@@ -0,0 +1,76 @@
+From f8c7ec24da62e28f911e248170720123d1b1829b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 09:34:46 -0700
+Subject: hwmon: (sht3x) Fix unaligned accesses
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ]
+
+Sashiko reports:
+
+In sht3x_update_client(), the 16-bit temperature and humidity values are
+extracted from a stack-allocated byte array using be16_to_cpup(). The
+pointers passed to this function are calculated as buf and buf + 3. Since
+the difference between the two pointers is an odd number of bytes, at
+least one of them is guaranteed to be at an unaligned offset.
+
+This will trigger an alignment fault on strict-alignment architectures
+such as ARMv5 or SPARC, resulting in a kernel panic.
+
+Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(),
+and put_unaligned_be16() instead of cpu_to_be16().
+
+Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/sht3x.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
+index c2f6b73aa7f34..4d90f89a99297 100644
+--- a/drivers/hwmon/sht3x.c
++++ b/drivers/hwmon/sht3x.c
+@@ -21,6 +21,7 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/jiffies.h>
++#include <linux/unaligned.h>
+ /* commands (high repeatability mode) */
+ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 };
+@@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev)
+               if (ret)
+                       goto out;
+-              val = be16_to_cpup((__be16 *)buf);
++              val = get_unaligned_be16(buf);
+               data->temperature = sht3x_extract_temperature(val);
+-              val = be16_to_cpup((__be16 *)(buf + 3));
++              val = get_unaligned_be16(buf + 3);
+               data->humidity = sht3x_extract_humidity(val);
+               data->last_update = jiffies;
+       }
+@@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data)
+               if (ret)
+                       return ret;
+-              raw = be16_to_cpup((__be16 *)buffer);
++              raw = get_unaligned_be16(buffer);
+               temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
+               humidity = sht3x_extract_humidity(raw & 0xfe00);
+               data->temperature_limits[index] = temperature;
+@@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev,
+       raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
+       raw |= ((humidity * 42950) >> 16) & 0xfe00;
+-      *((__be16 *)position) = cpu_to_be16(raw);
++      put_unaligned_be16(raw, position);
+       position += SHT3X_WORD_LEN;
+       *position = crc8(sht3x_crc8_table,
+                        position - SHT3X_WORD_LEN,
+-- 
+2.53.0
+
diff --git a/queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch b/queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch
new file mode 100644 (file)
index 0000000..754a847
--- /dev/null
@@ -0,0 +1,89 @@
+From 3e1349816fe6957aba6600df6470dc803192835e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 13:50:06 +0200
+Subject: ice: suppress DPLL errors during reset recovery
+
+From: Przemyslaw Korba <przemyslaw.korba@intel.com>
+
+[ Upstream commit b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 ]
+
+During reset recovery, the admin queue returns EBUSY which is expected
+behavior. However, the DPLL subsystem was logging these as errors and
+incrementing the error counter, potentially leading to unnecessary
+warnings and even disabling the DPLL periodic worker if the threshold
+was reached.
+
+Suppress error logging and error counter increments when the admin
+queue returns EBUSY, as this is expected during reset recovery and
+not a real failure condition.
+
+test case:
+- ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared
+mac-shared phy-shared ram-shared
+- observe if dmesg EBUSY errors are gone
+
+Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu")
+Signed-off-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
+index 5f6f29142e301..14dfddc85d1c0 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
++++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
+@@ -784,7 +784,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+                                  ret,
+                                  libie_aq_str(pf->hw.adminq.sq_last_status),
+                                  pin_type_name[pin_type], pin->idx);
+-      else
++      else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
+               dev_err_ratelimited(ice_pf_to_dev(pf),
+                                   "err:%d %s failed to update %s pin:%u\n",
+                                   ret,
+@@ -2821,7 +2821,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf,
+       *phase_offset_pins_updated = 0;
+       ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
+                                              ARRAY_SIZE(meas));
+-      if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) {
++      if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN ||
++                  pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) {
+               return 0;
+       } else if (ret) {
+               dev_err(ice_pf_to_dev(pf),
+@@ -2883,10 +2884,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
+               d->dpll_idx, d->prev_input_idx, d->input_idx,
+               d->dpll_state, d->prev_dpll_state, d->mode);
+       if (ret) {
+-              dev_err(ice_pf_to_dev(pf),
+-                      "update dpll=%d state failed, ret=%d %s\n",
+-                      d->dpll_idx, ret,
+-                      libie_aq_str(pf->hw.adminq.sq_last_status));
++              /* EBUSY is expected during reset recovery, don't log error */
++              if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
++                      dev_err(ice_pf_to_dev(pf),
++                              "update dpll=%d state failed, ret=%d %s\n",
++                              d->dpll_idx, ret,
++                              libie_aq_str(pf->hw.adminq.sq_last_status));
+               return ret;
+       }
+       if (init) {
+@@ -2955,7 +2958,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
+           d->periodic_counter % dp->phase_offset_monitor_period == 0)
+               ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf);
+       if (ret) {
+-              d->cgu_state_acq_err_num++;
++              /* EBUSY is expected during reset recovery */
++              if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
++                      d->cgu_state_acq_err_num++;
+               /* stop rescheduling this worker */
+               if (d->cgu_state_acq_err_num >
+                   ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
+-- 
+2.53.0
+
diff --git a/queue-7.1/idpf-adjust-txq-ring-count-minimum.patch b/queue-7.1/idpf-adjust-txq-ring-count-minimum.patch
new file mode 100644 (file)
index 0000000..30a83c6
--- /dev/null
@@ -0,0 +1,72 @@
+From 27140040be288f073f72f0d7b28ffcfeb3b40e26 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 30 Jun 2026 16:56:19 -0700
+Subject: idpf: adjust TxQ ring count minimum
+
+From: Joshua Hay <joshua.a.hay@intel.com>
+
+[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ]
+
+Set the TxQ ring count minimum to 128 descriptors. Any lower than this,
+and the queue will stall and trigger Tx timeouts in flow based
+scheduling mode. This is because next_to_clean might never be updated.
+
+In flow based scheduling mode, next_to_clean is only updated after a
+descriptor completion is processed, i.e. after the RE bit is set in the
+last descriptor of a Tx packet. This will never happen with a ring size
+of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value
+of last_re is initialized/set to, the calculated gap will be at most 63
+and never trigger the RE bit.
+
+Even a ring size of 96 does not solve this. Because of how infrequent
+next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED
+will be much smaller on average. This increases the chance the queue
+will be stopped because a multi-descriptor packet, e.g. a large LSO
+packet, does not see enough resources on the ring. In this case, the
+queue will trigger the stop logic. The queue permanently stalls because
+there is no chance for a descriptor completion to update next_to_clean
+since it is dependent on a packet being sent.
+
+Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool")
+Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +----
+ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +-
+ 2 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+index f6b3b15364ff6..bddb601ee411c 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+@@ -3097,10 +3097,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
+               tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
+               tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
+-              /* Set the RE bit to periodically "clean" the descriptor ring.
+-               * MIN_GAP is set to MIN_RING size to ensure it will be set at
+-               * least once each time around the ring.
+-               */
++              /* Set the RE bit periodically to "clean" the descriptor ring */
+               if (idpf_tx_splitq_need_re(tx_q)) {
+                       tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE;
+                       tx_q->txq_grp->num_completions_pending++;
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+index 4be5b3b6d3ed2..908dfa28674eb 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+@@ -21,7 +21,7 @@
+ /* Mailbox Queue */
+ #define IDPF_MAX_MBXQ                         1
+-#define IDPF_MIN_TXQ_DESC                     64
++#define IDPF_MIN_TXQ_DESC                     128
+ #define IDPF_MIN_RXQ_DESC                     64
+ #define IDPF_MIN_TXQ_COMPLQ_DESC              256
+ #define IDPF_MAX_QIDS                         256
+-- 
+2.53.0
+
diff --git a/queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch b/queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch
new file mode 100644 (file)
index 0000000..3ea3944
--- /dev/null
@@ -0,0 +1,112 @@
+From faf5ffbd80fc25fa6558db830c2c6b61c9760a15 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 17:57:54 -0400
+Subject: idpf: bound interrupt-vector register fill to the allocated array
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 9f7007ee9858c99aa43101bc8352c672fee85644 ]
+
+idpf_get_reg_intr_vecs() fills the caller-allocated reg_vals[] array from
+the VIRTCHNL2_OP_ALLOC_VECTORS reply in adapter->req_vec_chunks, bounding
+its inner loop only by the per-chunk num_vectors. The array is sized
+separately: idpf_intr_reg_init() allocates
+kzalloc_objs(struct idpf_vec_regs, total_vecs) from
+caps.num_allocated_vectors and only checks the returned count after the
+fill. The sum of per-chunk num_vectors is never reconciled against
+total_vecs, so a reply with a small num_allocated_vectors but chunks
+summing higher writes past the end of reg_vals[].
+
+Impact: a control plane (a PF or hypervisor device model) that returns a
+VIRTCHNL2_OP_ALLOC_VECTORS reply whose per-chunk num_vectors sum exceeds
+num_allocated_vectors writes struct idpf_vec_regs entries past the end of
+the reg_vals kmalloc allocation (KASAN slab-out-of-bounds write).
+
+Bound the fill loop to the array capacity passed in by the callers,
+mirroring the sibling idpf_vport_get_q_reg(). The existing
+num_regs < num_vecs check then rejects an undersized reply without the
+out-of-bounds write happening first.
+
+Fixes: d4d558718266 ("idpf: initialize interrupts and enable vport")
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_dev.c      | 2 +-
+ drivers/net/ethernet/intel/idpf/idpf_vf_dev.c   | 2 +-
+ drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 5 +++--
+ drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 2 +-
+ 4 files changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c
+index 1a0c71c95ef12..4079a787657f1 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_dev.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c
+@@ -87,7 +87,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
+       if (!reg_vals)
+               return -ENOMEM;
+-      num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
++      num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
+       if (num_regs < num_vecs) {
+               err = -EINVAL;
+               goto free_reg_vals;
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
+index a07d7e808ca9b..6726084f6cfa0 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
+@@ -86,7 +86,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
+       if (!reg_vals)
+               return -ENOMEM;
+-      num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
++      num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
+       if (num_regs < num_vecs) {
+               err = -EINVAL;
+               goto free_reg_vals;
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+index dc5ad784f456f..8bd6cca64c9bf 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+@@ -1318,11 +1318,12 @@ idpf_vport_init_queue_reg_chunks(struct idpf_vport_config *vport_config,
+  * idpf_get_reg_intr_vecs - Get vector queue register offset
+  * @adapter: adapter structure to get the vector chunks
+  * @reg_vals: Register offsets to store in
++ * @num_vecs: number of entries the @reg_vals array can hold
+  *
+  * Return: number of registers that got populated
+  */
+ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
+-                         struct idpf_vec_regs *reg_vals)
++                         struct idpf_vec_regs *reg_vals, int num_vecs)
+ {
+       struct virtchnl2_vector_chunks *chunks;
+       struct idpf_vec_regs reg_val;
+@@ -1346,7 +1347,7 @@ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
+               dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing);
+               itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing);
+-              for (i = 0; i < num_vec; i++) {
++              for (i = 0; i < num_vec && num_regs < num_vecs; i++) {
+                       reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg;
+                       reg_vals[num_regs].itrn_reg = reg_val.itrn_reg;
+                       reg_vals[num_regs].itrn_index_spacing =
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+index 6876e3ed9d1be..9b1c9c86f6eac 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
++++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+@@ -104,7 +104,7 @@ int idpf_vc_core_init(struct idpf_adapter *adapter);
+ void idpf_vc_core_deinit(struct idpf_adapter *adapter);
+ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
+-                         struct idpf_vec_regs *reg_vals);
++                         struct idpf_vec_regs *reg_vals, int num_vecs);
+ int idpf_queue_reg_init(struct idpf_vport *vport,
+                       struct idpf_q_vec_rsrc *rsrc,
+                       struct idpf_queue_id_reg_info *chunks);
+-- 
+2.53.0
+
diff --git a/queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
new file mode 100644 (file)
index 0000000..3daf3f2
--- /dev/null
@@ -0,0 +1,41 @@
+From bf598cd402f521bd4a3671078351c1a74a67217c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Jul 2026 01:03:32 -0400
+Subject: idpf: Fix mailbox IRQ name leak on request failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ]
+
+idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling
+request_irq(). On success, the name is released later through
+kfree(free_irq()), but request_irq() failure returns without freeing it.
+
+Free the allocated name on the request_irq() failure path.
+
+Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request")
+Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+index cf966fe6c759c..bb81e620c5c87 100644
+--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
+@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "IRQ request for mailbox failed, error: %d\n", err);
+-
++              kfree(name);
+               return err;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch b/queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch
new file mode 100644 (file)
index 0000000..4ef9730
--- /dev/null
@@ -0,0 +1,77 @@
+From 94e591b29b40b36555d34bd8cc685f3e6a63ee37 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jun 2026 14:52:29 +0200
+Subject: iomap: add a separate bio_set for iomap_split_ioend
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 ]
+
+iomap_split_ioend can split bios that already come from
+iomap_ioend_bioset and thus deadlock when the bioset is exhausted.
+
+Add a separate bio_set to avoid this deadlock.
+
+Christian Brauner <brauner@kernel.org> says:
+Mark iomap_ioend_split_bioset static as it is only used in ioend.c,
+fixing the sparse warning reported by the kernel test robot.
+
+Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers")
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/iomap/ioend.c | 21 +++++++++++++++++++--
+ 1 file changed, 19 insertions(+), 2 deletions(-)
+
+diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
+index 2d5611f6cc57d..9fe0aea8e8d9b 100644
+--- a/fs/iomap/ioend.c
++++ b/fs/iomap/ioend.c
+@@ -13,6 +13,7 @@
+ struct bio_set iomap_ioend_bioset;
+ EXPORT_SYMBOL_GPL(iomap_ioend_bioset);
++static struct bio_set iomap_ioend_split_bioset;
+ struct iomap_ioend *iomap_init_ioend(struct inode *inode,
+               struct bio *bio, loff_t file_offset, u16 ioend_flags)
+@@ -485,7 +486,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend,
+       sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT,
+                       i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT;
+-      split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset);
++      split = bio_split(bio, sector_offset, GFP_NOFS,
++                      &iomap_ioend_split_bioset);
+       if (IS_ERR(split))
+               return ERR_CAST(split);
+       split->bi_private = bio->bi_private;
+@@ -508,8 +510,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend);
+ static int __init iomap_ioend_init(void)
+ {
+-      return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
++      const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE);
++      int error;
++
++      error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries,
+                          offsetof(struct iomap_ioend, io_bio),
+                          BIOSET_NEED_BVECS);
++      if (error)
++              return error;
++      error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries,
++                         offsetof(struct iomap_ioend, io_bio),
++                         BIOSET_NEED_BVECS);
++      if (error)
++              goto out_exit_ioend_bioset;
++      return 0;
++
++out_exit_ioend_bioset:
++      bioset_exit(&iomap_ioend_bioset);
++      return error;
+ }
+ fs_initcall(iomap_ioend_init);
+-- 
+2.53.0
+
diff --git a/queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch b/queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch
new file mode 100644 (file)
index 0000000..079a44a
--- /dev/null
@@ -0,0 +1,67 @@
+From 544dc386a40d83f3012437d4f25d678ebcce2967 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Jul 2026 22:36:11 -0700
+Subject: iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a
+ vDEVICE
+
+From: Nicolin Chen <nicolinc@nvidia.com>
+
+[ Upstream commit c3b8ee84a965058b41275069d4696f37a8b14bf6 ]
+
+arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID
+taken from master->streams[0], assuming a device has exactly one stream. A
+device with several streams gets only its first one mapped, so a guest vSID
+invalidation cannot reach the others' ATC and IOTLB entries; a device with
+none makes master->streams a ZERO_SIZE_PTR, read out of bounds.
+
+Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP
+when master->num_streams is not one, rather than mapping it silently.
+
+Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object")
+Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Assisted-by: Claude:claude-opus-4-8
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c   | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+index ddae0b07c76b5..b7a4a1b5eddd5 100644
+--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+@@ -297,6 +297,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid)
+       return ret;
+ }
++static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev)
++{
++      struct device *dev = iommufd_vdevice_to_device(vdev);
++      struct arm_smmu_master *master = dev_iommu_priv_get(dev);
++
++      /*
++       * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so
++       * more streams would leave the rest stale and none reads out of bounds.
++       */
++      if (master->num_streams != 1)
++              return -EOPNOTSUPP;
++      return 0;
++}
++
+ /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */
+ struct arm_vsmmu_invalidation_cmd {
+       union {
+@@ -403,6 +417,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu,
+ static const struct iommufd_viommu_ops arm_vsmmu_ops = {
+       .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
+       .cache_invalidate = arm_vsmmu_cache_invalidate,
++      .vdevice_init = arm_vsmmu_vdevice_init,
+ };
+ size_t arm_smmu_get_viommu_size(struct device *dev,
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch b/queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch
new file mode 100644 (file)
index 0000000..50111f5
--- /dev/null
@@ -0,0 +1,47 @@
+From 4ab88db6bdf5a427193904847ac8b6d47c146fa6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:53:39 -0400
+Subject: ipv6: release fib6_null_entry on subtree failure
+
+From: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
+
+[ Upstream commit 93cad1f6bd1e27c75c4a5ab000c2a2fc01181ccf ]
+
+When adding a source-specific route creates a new subtree, fib6_add()
+installs fib6_null_entry as the temporary leaf of the new subtree root
+and takes a fib6_info reference for that holder.
+
+If adding the first source leaf fails, the code frees the just allocated
+subtree root but leaves that hold behind. fib6_null_entry is a per-netns
+sentinel and is freed directly at netns teardown, so this does not keep
+the object alive. However, it leaves its visible refcount permanently
+elevated and can eventually saturate the refcount on repeated failures.
+
+Drop the null-entry reference before freeing the unlinked subtree root.
+
+Fixes: 5ea715289af6 ("ipv6: broadly use fib6_info_hold() helper")
+Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://patch.msgid.link/20260727185339.1545169-1-shuangpeng.kernel@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ip6_fib.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
+index 414fc3c567360..b4641bfffdf3f 100644
+--- a/net/ipv6/ip6_fib.c
++++ b/net/ipv6/ip6_fib.c
+@@ -1495,6 +1495,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
+                                  root, and then (in failure) stale node
+                                  in main tree.
+                                */
++                              fib6_info_release(info->nl_net->ipv6.fib6_null_entry);
+                               node_free_immediate(info->nl_net, sfn);
+                               err = PTR_ERR(sn);
+                               goto failure;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch b/queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch
new file mode 100644 (file)
index 0000000..f153dcc
--- /dev/null
@@ -0,0 +1,344 @@
+From 731ea8a6eacc3a11e5144d0f10b86307d4e49bb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 19:52:33 +0800
+Subject: ipvs: adjust double hashing when fwd method changes
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 712d2993bea555f1f09cd53cbdb25714f28e85db ]
+
+Synced conns can be created with one forwarding method
+and later updated with different one after the dest
+server is configured. This needs adjusting the hashing
+for node hn1 because only MASQ supports double hashing.
+
+Modify conn_tab_lock() to support seeking for hash node
+hn0 together with adding for hn1. By this way we can
+safely modify the forwarding method and hn1.hash_key
+under bucket lock for the first node hn0. The forwarding
+method is also protected by cp->lock as it is part of
+cp->flags.
+
+Fix the usage of stale idx/idx2 values in conn_tab_lock
+after jumping to the retry label. Instead, use idx/idx2
+values just to order the locking for the old/new tables.
+
+Reported-by: Zhiling Zou <roxy520tt@gmail.com>
+Link: https://patch.msgid.link/1b914f41d725bc064c9ba9830dc8169329737270.1782540466.git.roxy520tt@gmail.com/
+Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com
+Fixes: f20c73b0460d ("ipvs: use more keys for connection hashing")
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/ipvs/ip_vs_conn.c | 189 +++++++++++++++++++++++++-------
+ 1 file changed, 147 insertions(+), 42 deletions(-)
+
+diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
+index b457dd2f6bc89..26f2233b34b04 100644
+--- a/net/netfilter/ipvs/ip_vs_conn.c
++++ b/net/netfilter/ipvs/ip_vs_conn.c
+@@ -70,25 +70,45 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly;
+  * bucket or hash table
+  * - hash table resize works like rehash but always rehashes into new table
+  * - bit lock on bucket serializes all operations that modify the chain
++ * - on resize, bucket from the old table is locked before bucket from the
++ * new table
+  * - cp->lock protects conn fields like cp->flags, cp->dest
+  */
+-/* Lock conn_tab bucket for conn hash/unhash, not for rehash */
++/**
++ * conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash
++ * @t:                hash table for hn0, new_tbl when new_hash=true
++ * @t2:               hash table for hn1, new_tbl when new_hash2=true
++ * @cp:               connection
++ * @hash_key: hash key for hn0
++ * @hash_key2:        hash key for hn1
++ * @use2:     using hn1 (double hashing) based on the forwarding method
++ * @new_hash: mode for hn0, hash node (true) or seek node (false)
++ * @new_hash2:        mode for hn1, hash node (true) or seek node (false)
++ * @head_ret: returned head for hn0
++ * @head2_ret:        returned head for hn1
++ *
++ * We support 3 modes:
++ * - seek mode for both nodes, used for unhashing
++ * - hash mode for both nodes, used for hashing
++ * - seek hn0 and hash hn1, used when forwarding method is changed
++ */
+ static __always_inline void
+-conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
+-            u32 hash_key2, bool use2, bool new_hash,
+-            struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret)
++conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp,
++            u32 hash_key, u32 hash_key2, bool use2, bool new_hash,
++            bool new_hash2, struct hlist_bl_head **head_ret,
++            struct hlist_bl_head **head2_ret)
+ {
+       struct hlist_bl_head *head, *head2;
+       u32 hash_key_new, hash_key_new2;
+-      struct ip_vs_rht *t2 = t;
+-      u32 idx, idx2;
++      int idx = 0, idx2 = 0;
++
++      /* Advance idx2 when new_hash is not set but hash_key2
++       * is for new table
++       */
++      if (new_hash2 && use2 && t != t2)
++              idx2++;
+-      idx = hash_key & t->mask;
+-      if (use2)
+-              idx2 = hash_key2 & t->mask;
+-      else
+-              idx2 = idx;
+       if (!new_hash) {
+               /* We need to lock the bucket in the right table */
+@@ -100,46 +120,45 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
+                        * both nodes in different tables, use idx/idx2
+                        * for proper lock ordering for heads.
+                        */
+-                      idx = hash_key & t->mask;
+-                      idx |= IP_VS_RHT_TABLE_ID_MASK;
+-              }
+-              if (use2) {
+-                      if (!ip_vs_rht_same_table(t2, hash_key2)) {
+-                              /* It is already moved to new table */
+-                              t2 = rcu_dereference(t2->new_tbl);
+-                              idx2 = hash_key2 & t2->mask;
+-                              idx2 |= IP_VS_RHT_TABLE_ID_MASK;
+-                      }
+-              } else {
+-                      idx2 = idx;
++                      idx++;
+               }
+       }
++      if (use2 && !new_hash2 && !ip_vs_rht_same_table(t2, hash_key2)) {
++              /* It is already moved to new table */
++              t2 = rcu_dereference(t2->new_tbl);
++              idx2++;
++      }
++      if (!use2)
++              idx2 = idx;
+       head = t->buckets + (hash_key & t->mask);
+       head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head;
+-      local_bh_disable();
+-      /* Do not touch seqcount, this is a safe operation */
+-
+-      if (idx <= idx2) {
++      if (idx > idx2 || (head > head2 && idx == idx2)) {
++              hlist_bl_lock(head2);
+               hlist_bl_lock(head);
+-              if (head != head2)
+-                      hlist_bl_lock(head2);
+       } else {
+-              hlist_bl_lock(head2);
+               hlist_bl_lock(head);
++              if (head != head2)
++                      hlist_bl_lock(head2);
+       }
+       if (!new_hash) {
++              bool changed;
++
+               /* Ensure hash_key is read under lock */
+               hash_key_new = READ_ONCE(cp->hn0.hash_key);
+-              hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
++              changed = hash_key != hash_key_new;
++              if (use2 && !new_hash2) {
++                      hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
++                      changed |= hash_key2 != hash_key_new2;
++              } else {
++                      hash_key_new2 = hash_key2;
++              }
+               /* Hash changed ? */
+-              if (hash_key != hash_key_new ||
+-                  (hash_key2 != hash_key_new2 && use2)) {
++              if (changed) {
+                       if (head != head2)
+                               hlist_bl_unlock(head2);
+                       hlist_bl_unlock(head);
+-                      local_bh_enable();
+                       hash_key = hash_key_new;
+                       hash_key2 = hash_key_new2;
+                       goto retry;
+@@ -155,7 +174,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head,
+       if (head != head2)
+               hlist_bl_unlock(head2);
+       hlist_bl_unlock(head);
+-      local_bh_enable();
+ }
+ static void ip_vs_conn_expire(struct timer_list *t);
+@@ -268,8 +286,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
+               use2 = false;
+       }
+-      conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */,
+-                    &head, &head2);
++      local_bh_disable();
++      conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */,
++                    true /* new_hash2 */, &head, &head2);
+       cp->flags |= IP_VS_CONN_F_HASHED;
+       WRITE_ONCE(cp->hn0.hash_key, hash_key);
+@@ -280,6 +299,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
+               hlist_bl_add_head_rcu(&cp->hn1.node, head2);
+       conn_tab_unlock(head, head2);
++      local_bh_enable();
+       ret = 1;
+       /* Schedule resizing if load increases */
+@@ -306,18 +326,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
+               return refcount_dec_if_one(&cp->refcnt);
+       rcu_read_lock();
++      local_bh_disable();
+       t = rcu_dereference(ipvs->conn_tab);
+       hash_key = READ_ONCE(cp->hn0.hash_key);
+       hash_key2 = READ_ONCE(cp->hn1.hash_key);
+       use2 = ip_vs_conn_use_hash2(cp);
+-      conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */,
+-                    &head, &head2);
++      conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */,
++                    false /* new_hash2 */, &head, &head2);
+       if (cp->flags & IP_VS_CONN_F_HASHED) {
+               /* Decrease refcnt and unlink conn only if we are last user */
+-              if (refcount_dec_if_one(&cp->refcnt)) {
++              if (use2 == ip_vs_conn_use_hash2(cp) &&
++                  refcount_dec_if_one(&cp->refcnt)) {
+                       hlist_bl_del_rcu(&cp->hn0.node);
+                       if (use2)
+                               hlist_bl_del_rcu(&cp->hn1.node);
+@@ -328,6 +350,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
+       conn_tab_unlock(head, head2);
++      local_bh_enable();
+       rcu_read_unlock();
+       return ret;
+@@ -632,6 +655,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
+       int ntbl;
+       int dir;
++restart:
+       /* No packets from inside, so we can do it in 2 steps. */
+       dir = use2 ? 1 : 0;
+@@ -686,6 +710,23 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
+       /* Protect the cp->flags modification */
+       spin_lock_bh(&cp->lock);
++      /* Recheck the forwarding method under lock */
++      if (use2 != ip_vs_conn_use_hash2(cp)) {
++              use2 = !use2;
++              if (use2) {
++                      spin_unlock_bh(&cp->lock);
++                      /* Restart with new use2 value */
++                      goto restart;
++              }
++              if (dir) {
++                      /* Not started yet, so just skip dir 1 */
++                      spin_unlock_bh(&cp->lock);
++                      dir--;
++                      goto next_dir;
++              }
++              /* Just finish dir 0 */
++      }
++
+       /* Lock seqcount only for the old bucket, even if we are on new table
+        * because it affects the del operation, not the adding.
+        */
+@@ -752,6 +793,61 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
+               goto next_dir;
+ }
++/* Change forwarding method for hashed conn */
++static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags)
++{
++      struct netns_ipvs *ipvs = cp->ipvs;
++      struct hlist_bl_head *head, *head2;
++      u32 hash2, hash_key, hash_key2;
++      struct ip_vs_rht *t, *t2;
++
++      /* See ip_vs_conn_use_hash2() for reference */
++      if ((cp->flags & IP_VS_CONN_F_TEMPLATE) ||
++          /* No change in double hashing ? */
++          (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ) ==
++          ((new_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_MASQ)) {
++              cp->flags = new_flags;
++              return;
++      }
++      t = rcu_dereference(ipvs->conn_tab);
++      if (ip_vs_conn_use_hash2(cp)) {
++              /* Stop double hashing */
++              hash_key = READ_ONCE(cp->hn0.hash_key);
++              hash_key2 = READ_ONCE(cp->hn1.hash_key);
++
++              conn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */,
++                            false /* new_hash */, false /* new_hash2 */,
++                            &head, &head2);
++
++              /* Keep both hash keys in same table */
++              hash_key = READ_ONCE(cp->hn0.hash_key);
++              WRITE_ONCE(cp->hn1.hash_key, hash_key);
++              hlist_bl_del_rcu(&cp->hn1.node);
++              cp->flags = new_flags;
++
++              conn_tab_unlock(head, head2);
++      } else {
++              /* Start double hashing */
++
++              hash_key = READ_ONCE(cp->hn0.hash_key);
++
++              t2 = rcu_dereference(t->new_tbl);
++              hash2 = ip_vs_conn_hashkey_conn(t2, cp, true);
++              hash_key2 = ip_vs_rht_build_hash_key(t2, hash2);
++
++              /* Change the forwarding method under locked hn0 */
++              conn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */,
++                            false /* new_hash */, true /* new_hash2 */,
++                            &head, &head2);
++
++              WRITE_ONCE(cp->hn1.hash_key, hash_key2);
++              cp->flags = new_flags;
++              hlist_bl_add_head_rcu(&cp->hn1.node, head2);
++
++              conn_tab_unlock(head, head2);
++      }
++}
++
+ /* Get default load factor to map conn_count/u_thresh to t->size */
+ static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs)
+ {
+@@ -1021,9 +1117,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
+                       conn_flags &= ~IP_VS_CONN_F_INACTIVE;
+               /* connections inherit forwarding method from dest */
+               flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT);
++              flags |= conn_flags;
++              /* Changing forwarding method for hashed conn can
++               * happen only under locks
++               */
++              if (cp->flags & IP_VS_CONN_F_HASHED)
++                      ip_vs_conn_change_fwd_mask(cp, flags);
++              else
++                      cp->flags = flags;
++      } else {
++              flags |= conn_flags;
++              cp->flags = flags;
+       }
+-      flags |= conn_flags;
+-      cp->flags = flags;
+       cp->dest = dest;
+       IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d "
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch b/queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch
new file mode 100644 (file)
index 0000000..5509917
--- /dev/null
@@ -0,0 +1,54 @@
+From 94cf5f0855f58eb6a447178567f3c55ca5e080a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:25:39 +0300
+Subject: ipvs: clear the nfct flag under lock
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit da7d894c41d5910daae2b8ffa024c52ff0a4df6a ]
+
+Sashiko warns that cp->flags should be changed under cp->lock
+
+Fixes: 35dfb013149f ("ipvs: queue delayed work to expire no destination connections if expire_nodest_conn=1")
+Fixes: f0a5e4d7a594 ("ipvs: allow connection reuse for unconfirmed conntrack")
+Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/ipvs/ip_vs_core.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index 7efa209a517b1..6b79e0c4d9e28 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -2194,8 +2194,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               }
+               if (resched) {
+-                      if (!old_ct)
++                      if (!old_ct) {
++                              spin_lock_bh(&cp->lock);
+                               cp->flags &= ~IP_VS_CONN_F_NFCT;
++                              spin_unlock_bh(&cp->lock);
++                      }
+                       if (!atomic_read(&cp->n_control))
+                               ip_vs_conn_expire_now(cp);
+                       __ip_vs_conn_put(cp);
+@@ -2211,8 +2214,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (sysctl_expire_nodest_conn(ipvs)) {
+                       bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
+-                      if (!old_ct)
++                      if (!old_ct) {
++                              spin_lock_bh(&cp->lock);
+                               cp->flags &= ~IP_VS_CONN_F_NFCT;
++                              spin_unlock_bh(&cp->lock);
++                      }
+                       ip_vs_conn_expire_now(cp);
+                       __ip_vs_conn_put(cp);
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
new file mode 100644 (file)
index 0000000..f324119
--- /dev/null
@@ -0,0 +1,335 @@
+From 68ebdd27d3132fe39091ad9e9b0e9cda545bfb3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:17 +0300
+Subject: ipvs: do not mangle ICMP replies for non-first fragments
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
+
+Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the
+payload for embedded non-first IPv4 fragments. The problem is
+in the very old inverted pp->dont_defrag check which should not
+continue when embedded is a non-first TCP/UDP/SCTP fragment.
+
+Check for embedded non-first fragment is also missing from
+ip_vs_out_icmp_v6(), it is needed before any connection
+lookups that expect ports after the network headers.
+
+Drop the blocking code from ip_vs_in_icmp_v6() which prevents
+ICMPv6 from local clients to use non-MASQ forwarding.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h             | 11 +++---
+ net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++---------------------
+ net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++----
+ 3 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 7b556ce03602e..8d98f7e0a9fb2 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1953,8 +1953,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, unsigned int toff,
+-                  unsigned int wlen, unsigned int hooknum,
+-                  struct ip_vs_iphdr *ciph);
++                  unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1968,8 +1967,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, unsigned int toff,
+-                     unsigned int wlen, unsigned int hooknum,
+-                     struct ip_vs_iphdr *ciph);
++                     unsigned int hooknum, struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -2041,12 +2039,13 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir, unsigned int toff);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff,
++                  bool has_ports);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph);
++                     bool has_ports, struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index cd5eb71543ec8..7efa209a517b1 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -924,7 +924,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout, unsigned int toff)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff,
++                  bool has_ports)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+       struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+@@ -944,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+-          IPPROTO_SCTP == ciph->protocol) {
++      if (has_ports) {
+               __be16 *ports = (void *)ciph + ciph->ihl*4;
+               if (inout)
+@@ -970,18 +970,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int inout, unsigned int toff,
+-                     struct ip_vs_iphdr *ciph)
++                     bool has_ports, struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      int protocol;
+       struct icmp6hdr *icmph;
+       struct ipv6hdr *cih;
+       icmph = (struct icmp6hdr *)(skb->data + toff);
+       cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ciph->protocol;
+-
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+               cih->daddr = cp->vaddr.in6;
+@@ -991,9 +988,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!ciph->fragoffs &&
+-          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
+-           protocol == IPPROTO_SCTP)) {
++      if (has_ports) {
+               __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+@@ -1035,6 +1030,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       unsigned int ctoff = ciph->len;
++      bool has_ports = false;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+@@ -1048,17 +1044,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+       }
+       if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+-          ciph->protocol == IPPROTO_SCTP)
++          ciph->protocol == IPPROTO_SCTP) {
+               ctoff += 2 * sizeof(__u16);
++              has_ports = true;
++      }
+       if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -1142,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1207,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!pp)
+               return NF_ACCEPT;
++      /* Is the embedded protocol header present? */
++      if (unlikely(ciph.fragoffs && !pp->dont_defrag))
++              return NF_ACCEPT;
++
+       /* The embedded headers contain source and dest in reverse order */
+       cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
+                            ipvs, AF_INET6, skb, &ciph);
+@@ -1865,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       pp = pd->pp;
+       /* Is the embedded protocol header present? */
+-      if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
+-                   pp->dont_defrag))
++      if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
+@@ -1874,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       offset2 = offset;
+       ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
+-      offset = ciph.len;
+       /* The embedded headers contain source and dest in reverse order.
+        * For IPIP/UDP/GRE tunnel this is error for request, not for reply.
+@@ -1968,11 +1967,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+-          IPPROTO_SCTP == cih->protocol)
+-              offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
+-                                &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       pp = pd->pp;
+-      /* Cannot handle fragmented embedded protocol */
+-      if (ciph.fragoffs)
++      /* Is the embedded protocol header present? */
++      if (ciph.fragoffs && !pp->dont_defrag)
+               return NF_ACCEPT;
+       IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+@@ -2057,13 +2052,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               new_cp = true;
+       }
+-      /* VS/TUN, VS/DR and LOCALNODE just let it go */
+-      if ((hooknum == NF_INET_LOCAL_OUT) &&
+-          (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+-              verdict = NF_ACCEPT;
+-              goto out;
+-      }
+-
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+@@ -2079,14 +2067,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+-      /* Need to mangle contained IPv6 header in ICMPv6 packet */
+-      offset = ciph.len;
+-      if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+-          IPPROTO_SCTP == ciph.protocol)
+-              offset += 2 * sizeof(__u16); /* Also mangle ports */
+-
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
+-                                   &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
+ out:
+       if (likely(!new_cp))
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index c23401c789de3..0b0c5304993a9 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1503,13 +1503,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+       int local;
+       int rt_mode, was_input;
++      bool has_ports = false;
++      unsigned int wlen;
+       /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
+          forwarded directly here, because there is no need to
+@@ -1565,6 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1572,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1589,10 +1597,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               struct ip_vs_protocol *pp, unsigned int toff,
+-              unsigned int wlen, unsigned int hooknum,
+-              struct ip_vs_iphdr *ciph)
++              unsigned int hooknum, struct ip_vs_iphdr *ciph)
+ {
++      bool has_ports = false;
+       struct rt6_info *rt;    /* Route to the other host */
++      unsigned int wlen;
+       int rc;
+       int local;
+       int rt_mode;
+@@ -1650,6 +1659,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+               goto tx_error;
+       }
++      wlen = ciph->len;
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP) {
++              wlen += 2 * sizeof(__u16); /* Also mangle ports */
++              has_ports = true;
++      }
++
+       /* copy-on-write the packet before mangling it */
+       if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+@@ -1657,7 +1673,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch
new file mode 100644 (file)
index 0000000..ecb81b6
--- /dev/null
@@ -0,0 +1,624 @@
+From 839381e040ed167c0eb45aab70c834d12ddcebf0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:16 +0300
+Subject: ipvs: fix places with wrong packet offsets
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
+
+The offsets we use to packet headers and payloads should be
+based on skb->data. We even already respect non-zero
+network offset in ip_vs_fill_iph_skb() but some places
+do it wrongly and support only zero offset which is expected
+for the IP layer where IPVS has hooks.
+
+Change all places that instead of skb->data use offsets based
+on the network header (skb_network_header, ip_hdr, etc) because
+this doubles the network offset as noted by Sashiko.
+
+For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header
+parsing done by the caller.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   |  15 +--
+ net/netfilter/ipvs/ip_vs_app.c        |   4 +-
+ net/netfilter/ipvs/ip_vs_core.c       | 133 +++++++++++++-------------
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   4 +-
+ net/netfilter/ipvs/ip_vs_xmit.c       |  26 ++---
+ 7 files changed, 97 insertions(+), 93 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 591bf5b4b894f..7b556ce03602e 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -1952,8 +1952,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                  struct ip_vs_protocol *pp, int offset,
+-                  unsigned int hooknum, struct ip_vs_iphdr *iph);
++                  struct ip_vs_protocol *pp, unsigned int toff,
++                  unsigned int wlen, unsigned int hooknum,
++                  struct ip_vs_iphdr *ciph);
+ void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1966,8 +1967,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+ int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-                     struct ip_vs_protocol *pp, int offset,
+-                     unsigned int hooknum, struct ip_vs_iphdr *iph);
++                     struct ip_vs_protocol *pp, unsigned int toff,
++                     unsigned int wlen, unsigned int hooknum,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ #ifdef CONFIG_SYSCTL
+@@ -2039,11 +2041,12 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
+ }
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int dir);
++                  struct ip_vs_conn *cp, int dir, unsigned int toff);
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                     struct ip_vs_conn *cp, int dir);
++                     struct ip_vs_conn *cp, int dir, unsigned int toff,
++                     struct ip_vs_iphdr *ciph);
+ #endif
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
+index b0e00be85cb12..11cbdbaf561da 100644
+--- a/net/netfilter/ipvs/ip_vs_app.c
++++ b/net/netfilter/ipvs/ip_vs_app.c
+@@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+@@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
+       if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
+               return 0;
+-      th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
++      th = (struct tcphdr *)(skb->data + ipvsh->len);
+       /*
+        *      Remember seq number in case this pkt gets resized
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index c8b512725e6e0..cd5eb71543ec8 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -924,13 +924,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
+  * - inout: 1=in->out, 0=out->in
+  */
+ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                  struct ip_vs_conn *cp, int inout, unsigned int toff)
+ {
+       struct iphdr *iph        = ip_hdr(skb);
+-      unsigned int icmp_offset = iph->ihl*4;
+-      struct icmphdr *icmph    = (struct icmphdr *)(skb_network_header(skb) +
+-                                                    icmp_offset);
++      struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
+       struct iphdr *ciph       = (struct iphdr *)(icmph + 1);
++      unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+       if (inout) {
+               iph->saddr = cp->vaddr.ip;
+@@ -957,48 +956,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->checksum = 0;
+-      icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
++      icmph->checksum = ip_vs_checksum_complete(skb, toff);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered outgoing ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered outgoing ICMP");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
+-                      "Forwarding altered incoming ICMP");
++              IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
++                            "Forwarding altered incoming ICMP");
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+-                  struct ip_vs_conn *cp, int inout)
++                     struct ip_vs_conn *cp, int inout, unsigned int toff,
++                     struct ip_vs_iphdr *ciph)
+ {
+       struct ipv6hdr *iph      = ipv6_hdr(skb);
+-      unsigned int icmp_offset = 0;
+-      unsigned int offs        = 0; /* header offset*/
+       int protocol;
+       struct icmp6hdr *icmph;
+-      struct ipv6hdr *ciph;
+-      unsigned short fragoffs;
++      struct ipv6hdr *cih;
+-      ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+-      icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+-      offs = icmp_offset + sizeof(struct icmp6hdr);
+-      ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
++      icmph = (struct icmp6hdr *)(skb->data + toff);
++      cih = (struct ipv6hdr *)(skb->data + ciph->off);
+-      protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
++      protocol = ciph->protocol;
+       if (inout) {
+               iph->saddr = cp->vaddr.in6;
+-              ciph->daddr = cp->vaddr.in6;
++              cih->daddr = cp->vaddr.in6;
+       } else {
+               iph->daddr = cp->daddr.in6;
+-              ciph->saddr = cp->daddr.in6;
++              cih->saddr = cp->daddr.in6;
+       }
+       /* the TCP/UDP/SCTP port */
+-      if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-                        IPPROTO_SCTP == protocol)) {
+-              __be16 *ports = (void *)(skb_network_header(skb) + offs);
++      if (!ciph->fragoffs &&
++          (protocol == IPPROTO_TCP  || protocol == IPPROTO_UDP ||
++           protocol == IPPROTO_SCTP)) {
++              __be16 *ports = (void *)(skb->data + ciph->len);
+               IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+                             ntohs(inout ? ports[1] : ports[0]),
+@@ -1011,19 +1007,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+       /* And finally the ICMP checksum */
+       icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
+-                                            skb->len - icmp_offset,
++                                            skb->len - toff,
+                                             IPPROTO_ICMPV6, 0);
+-      skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
++      skb->csum_start = skb_headroom(skb) + toff;
+       skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       if (inout)
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered outgoing ICMPv6");
+       else
+-              IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
+-                            (void *)ciph - (void *)iph,
++              IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
+                             "Forwarding altered incoming ICMPv6");
+ }
+ #endif
+@@ -1033,37 +1027,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+  */
+ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               union nf_inet_addr *snet,
+-                              __u8 protocol, struct ip_vs_conn *cp,
++                              struct ip_vs_conn *cp,
+                               struct ip_vs_protocol *pp,
+-                              unsigned int offset, unsigned int ihl,
+-                              unsigned int hooknum)
++                              struct ip_vs_iphdr *ciph,
++                              unsigned int toff, unsigned int hooknum)
+ {
+       int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
++      unsigned int ctoff = ciph->len;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
++      if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+               goto out;
+       }
+-      if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+-          IPPROTO_SCTP == protocol)
+-              offset += 2 * sizeof(__u16);
+-      if (skb_ensure_writable(skb, offset))
++      if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
++          ciph->protocol == IPPROTO_SCTP)
++              ctoff += 2 * sizeof(__u16);
++      if (skb_ensure_writable(skb, ctoff))
+               goto out;
+ #ifdef CONFIG_IP_VS_IPV6
+       if (af == AF_INET6)
+-              ip_vs_nat_icmp_v6(skb, pp, cp, 1);
++              ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph);
+       else
+ #endif
+-              ip_vs_nat_icmp(skb, pp, cp, 1);
++              ip_vs_nat_icmp(skb, pp, cp, 1, toff);
+       if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
+               goto out;
+@@ -1091,9 +1086,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+  *    Currently handles error types - unreachable, quench, ttl exceeded.
+  */
+ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+-                        int *related, unsigned int hooknum)
++                        int *related, unsigned int hooknum,
++                        struct ip_vs_iphdr *ipvsh)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1108,17 +1103,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = ipvsh->len;
++      offset = ipvsh->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &ipvsh->saddr.ip, &ipvsh->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1137,7 +1134,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       /* Now find the contained IP header */
+       offset += sizeof(_icmph);
+       cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+-      if (cih == NULL)
++      if (!(cih && cih->version == 4 && cih->ihl >= 5))
+               return NF_ACCEPT; /* The packet looks wrong, ignore */
+       pp = ip_vs_proto_get(cih->protocol);
+@@ -1160,9 +1157,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       if (!cp)
+               return NF_ACCEPT;
+-      snet.ip = iph->saddr;
+-      return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
+-                                  pp, ciph.len, ihl, hooknum);
++      snet.ip = ipvsh->saddr.ip;
++      return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
++                                  hooknum);
+ }
+ #ifdef CONFIG_IP_VS_IPV6
+@@ -1175,7 +1172,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+       struct ip_vs_conn *cp;
+       struct ip_vs_protocol *pp;
+       union nf_inet_addr snet;
+-      unsigned int offset;
+       *related = 1;
+       ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
+@@ -1218,9 +1214,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               return NF_ACCEPT;
+       snet.in6 = ciph.saddr.in6;
+-      offset = ciph.len;
+-      return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+-                                  pp, offset, ipvsh->len, hooknum);
++      return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
++                                  ipvsh->len, hooknum);
+ }
+ #endif
+@@ -1546,7 +1541,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
+ #endif
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+-                      int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
++                      int verdict = ip_vs_out_icmp(ipvs, skb, &related,
++                                                   hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -1754,9 +1750,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
+  */
+ static int
+ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+-            unsigned int hooknum)
++            unsigned int hooknum, struct ip_vs_iphdr *iph)
+ {
+-      struct iphdr *iph;
+       struct icmphdr  _icmph, *ic;
+       struct iphdr    _ciph, *cih;    /* The ip header contained within the ICMP */
+       struct ip_vs_iphdr ciph;
+@@ -1766,7 +1761,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       unsigned int offset, offset2, ihl, verdict;
+       bool tunnel, new_cp = false;
+       union nf_inet_addr *raddr;
+-      char *outer_proto = "IPIP";
++      char *outer_proto __maybe_unused = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
+@@ -1776,17 +1771,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (ip_is_fragment(ip_hdr(skb))) {
+               if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
+                       return NF_STOLEN;
++              if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
++                      return NF_ACCEPT;
+       }
+-      iph = ip_hdr(skb);
+-      offset = ihl = iph->ihl * 4;
++      ihl = iph->len;
++      offset = iph->len;
+       ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+       if (ic == NULL)
+               return NF_DROP;
+       IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
+                 ic->type, ntohs(icmp_id(ic)),
+-                &iph->saddr, &iph->daddr);
++                &iph->saddr.ip, &iph->daddr.ip);
+       /*
+        * Work through seeing if this is for us.
+@@ -1903,7 +1900,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+           !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+-                        &iph->saddr);
++                        &iph->saddr.ip);
+               goto out;
+       }
+@@ -1974,7 +1971,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
+           IPPROTO_SCTP == cih->protocol)
+               offset += 2 * sizeof(__u16);
+-      verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum,
++                                &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -2087,7 +2085,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+           IPPROTO_SCTP == ciph.protocol)
+               offset += 2 * sizeof(__u16); /* Also mangle ports */
+-      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
++      verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum,
++                                   &ciph);
+ out:
+       if (likely(!new_cp))
+@@ -2166,7 +2165,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
+               if (unlikely(iph.protocol == IPPROTO_ICMP)) {
+                       int related;
+                       int verdict = ip_vs_in_icmp(ipvs, skb, &related,
+-                                                  hooknum);
++                                                  hooknum, &iph);
+                       if (related)
+                               return verdict;
+@@ -2302,6 +2301,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+                  const struct nf_hook_state *state)
+ {
+       struct netns_ipvs *ipvs = net_ipvs(state->net);
++      struct ip_vs_iphdr iphdr;
+       int r;
+       /* ipvs enabled in this netns ? */
+@@ -2311,10 +2311,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+       if (state->pf == NFPROTO_IPV4) {
+               if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
+                       return NF_ACCEPT;
++              ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
+ #ifdef CONFIG_IP_VS_IPV6
+       } else {
+-              struct ip_vs_iphdr iphdr;
+-
+               ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
+               if (iphdr.protocol != IPPROTO_ICMPV6)
+@@ -2324,7 +2323,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
+ #endif
+       }
+-      return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
++      return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
+ }
+ static const struct nf_hook_ops ip_vs_ops4[] = {
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index f6f732b7dfa86..3dbd3096e1637 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      sctph = (void *) skb_network_header(skb) + sctphoff;
++      sctph = (void *)skb->data + sctphoff;
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index 533fce3e5e4e4..99a286fdc90c6 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->source = cp->vport;
+       /* Adjust TCP checksums */
+@@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      tcph = (void *)skb_network_header(skb) + tcphoff;
++      tcph = (void *)skb->data + tcphoff;
+       tcph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index de3597347542e..f32785682402d 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->source = cp->vport;
+       /*
+@@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                       payload_csum = true;
+       }
+-      udph = (void *)skb_network_header(skb) + udphoff;
++      udph = (void *)skb->data + udphoff;
+       udph->dest = cp->dport;
+       /*
+diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
+index 9fef4335da13f..c23401c789de3 100644
+--- a/net/netfilter/ipvs/ip_vs_xmit.c
++++ b/net/netfilter/ipvs/ip_vs_xmit.c
+@@ -1502,8 +1502,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+  */
+ int
+ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *iph)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rtable   *rt;    /* Route to the other host */
+       int rc;
+@@ -1515,7 +1516,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, iph);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1533,7 +1534,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
+-                                 NULL, iph);
++                                 NULL, ciph);
+       if (local < 0)
+               goto tx_error;
+       rt = skb_rtable(skb);
+@@ -1565,13 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp(skb, pp, cp, 0);
++      ip_vs_nat_icmp(skb, pp, cp, 0, toff);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+@@ -1587,8 +1588,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+ #ifdef CONFIG_IP_VS_IPV6
+ int
+ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+-              struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+-              struct ip_vs_iphdr *ipvsh)
++              struct ip_vs_protocol *pp, unsigned int toff,
++              unsigned int wlen, unsigned int hooknum,
++              struct ip_vs_iphdr *ciph)
+ {
+       struct rt6_info *rt;    /* Route to the other host */
+       int rc;
+@@ -1600,7 +1602,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+          translate address/port back */
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
+               if (cp->packet_xmit)
+-                      rc = cp->packet_xmit(skb, cp, pp, ipvsh);
++                      rc = cp->packet_xmit(skb, cp, pp, ciph);
+               else
+                       rc = NF_ACCEPT;
+               /* do not touch skb anymore */
+@@ -1617,7 +1619,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
+                 IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
+       local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
+-                                    &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
++                                    &cp->daddr.in6, NULL, ciph, 0, rt_mode);
+       if (local < 0)
+               goto tx_error;
+       rt = dst_rt6_info(skb_dst(skb));
+@@ -1649,13 +1651,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+       }
+       /* copy-on-write the packet before mangling it */
+-      if (skb_ensure_writable(skb, offset))
++      if (skb_ensure_writable(skb, wlen))
+               goto tx_error;
+       if (skb_cow(skb, rt->dst.dev->hard_header_len))
+               goto tx_error;
+-      ip_vs_nat_icmp_v6(skb, pp, cp, 0);
++      ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph);
+       /* Another hack: avoid icmp_send in ip_fragment */
+       skb->ignore_df = 1;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ipvs-fix-the-checksum-validations.patch b/queue-7.1/ipvs-fix-the-checksum-validations.patch
new file mode 100644 (file)
index 0000000..f43dbfe
--- /dev/null
@@ -0,0 +1,389 @@
+From 4392d1ee25269fc2546185750de904197af9b07b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:15:15 +0300
+Subject: ipvs: fix the checksum validations
+
+From: Julian Anastasov <ja@ssi.bg>
+
+[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
+
+ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6
+packets from clients. In fact, as for TCP/UDP we should
+validate the checksum for ICMP packets only when we
+mangle the packets on MASQ or on reply for tunnel.
+
+Also, Sashiko points out that handle_response_icmp() being
+common for IPv4 and IPv6 is missing the pseudo-header
+calculation while validating ICMPv6 messages from real
+servers which is a problem if checksum is not validated
+by the hardware.
+
+Fix the problems by creating ip_vs_checksum_common_check()
+helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6.
+Rely on the nf_checksum() for validating the ICMP messages
+but use it also for TCP and UDP.
+
+Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
+
+IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum
+validation on LOCAL_OUT (local clients or local real
+servers) and on FORWARD (traffic from servers on LAN).
+Do it only on LOCAL_IN, in case nf_checksum() is not
+called on PRE_ROUTING.
+
+Also, ip_vs_checksum_complete() can be marked static.
+
+Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6")
+Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/ip_vs.h                   | 31 +++++++++++++++--
+ net/netfilter/ipvs/ip_vs_core.c       | 20 +++++++++--
+ net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++----
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  | 44 +++++------------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  | 50 ++++++---------------------
+ 5 files changed, 74 insertions(+), 86 deletions(-)
+
+diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
+index 62433e48b1f9a..591bf5b4b894f 100644
+--- a/include/net/ip_vs.h
++++ b/include/net/ip_vs.h
+@@ -25,7 +25,9 @@
+ #include <linux/netfilter.h>          /* for union nf_inet_addr */
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>                       /* for struct ipv6hdr */
++#include <net/route.h>
+ #include <net/ipv6.h>
++#include <net/ip6_fib.h>
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ #include <net/netfilter/nf_conntrack.h>
+ #endif
+@@ -2044,8 +2046,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+-
+ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
+ {
+       __be32 diff[2] = { ~old, new };
+@@ -2071,6 +2071,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
+       return csum_partial(diff, sizeof(diff), oldsum);
+ }
++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
++{
++      /* Checksum unnecessary or already validated? */
++      if (skb_csum_unnecessary(skb))
++              return false;
++      /* LOCAL_OUT ? */
++      if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
++              return false;
++      /* !LOCAL_IN (FORWARD) ? */
++      if (af == AF_INET6) {
++              if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
++                      return false;
++      } else {
++              if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
++                      return false;
++      }
++      return true;
++}
++
++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
++                                             int offset, int proto, int af)
++{
++      if (!ip_vs_checksum_needed(skb, af))
++              return true;
++      return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
++}
++
+ /* Forget current conntrack (unconfirmed) and attach notrack entry */
+ static inline void ip_vs_notrack(struct sk_buff *skb)
+ {
+diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
+index bafab93451d03..c8b512725e6e0 100644
+--- a/net/netfilter/ipvs/ip_vs_core.c
++++ b/net/netfilter/ipvs/ip_vs_core.c
+@@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
+ #endif
+-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+ {
+       return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
+ }
+@@ -1038,13 +1038,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
+                               unsigned int offset, unsigned int ihl,
+                               unsigned int hooknum)
+ {
++      int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
+       unsigned int verdict = NF_DROP;
+       if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+               goto after_nat;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) {
+               /* Failed checksum! */
+               IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
+                             IP_VS_DBG_ADDR(af, snet));
+@@ -1898,7 +1899,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
+       verdict = NF_DROP;
+       /* Ensure the checksum is correct */
+-      if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
++      if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
++          !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
+               /* Failed checksum! */
+               IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+                         &iph->saddr);
+@@ -2064,6 +2066,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
+               goto out;
+       }
++      verdict = NF_DROP;
++
++      /* Ensure the checksum is correct */
++      if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
++          !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
++                                       AF_INET6)) {
++              /* Failed checksum! */
++              IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
++                        &iph->saddr);
++              goto out;
++      }
++
+       /* do the statistics and put it back */
+       ip_vs_in_stats(cp, skb);
+diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+index c67317be17dfa..f6f732b7dfa86 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -11,7 +11,7 @@
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff);
++              struct ip_vs_iphdr *iph);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
++              if (!sctp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-              unsigned int sctphoff)
++              struct ip_vs_iphdr *iph)
+ {
++      unsigned int sctphoff = iph->len;
+       struct sctphdr *sh;
+       __le32 cmp, val;
++      if (!ip_vs_checksum_needed(skb, af))
++              return 1;
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+       if (val != cmp) {
+               /* CRC failure, dump it. */
+-              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                              "Failed checksum for");
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
+               return 0;
+       }
+       return 1;
+diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+index f86b763efcc4d..533fce3e5e4e4 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -29,7 +29,7 @@
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /* Call application helper if needed */
+@@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
++              if (!tcp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int tcphoff)
++             struct ip_vs_iphdr *iph)
+ {
+-      switch (skb->ip_summed) {
+-      case CHECKSUM_NONE:
+-              skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+-              fallthrough;
+-      case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-              if (af == AF_INET6) {
+-                      if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                          &ipv6_hdr(skb)->daddr,
+-                                          skb->len - tcphoff,
+-                                          IPPROTO_TCP,
+-                                          skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              } else
+-#endif
+-                      if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                            ip_hdr(skb)->daddr,
+-                                            skb->len - tcphoff,
+-                                            ip_hdr(skb)->protocol,
+-                                            skb->csum)) {
+-                              IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                               "Failed checksum for");
+-                              return 0;
+-                      }
+-              break;
+-      default:
+-              /* No need to checksum. */
+-              break;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+-
+       return 1;
+ }
+diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
+index 58f9e255927e2..de3597347542e 100644
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -25,7 +25,7 @@
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff);
++             struct ip_vs_iphdr *iph);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp, udphoff))
++              if (!udp_csum_check(cp->af, skb, pp, iph))
+                       return 0;
+               /*
+@@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ static int
+ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+-             unsigned int udphoff)
++             struct ip_vs_iphdr *iph)
+ {
+       struct udphdr _udph, *uh;
+-      uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
++      uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+       if (uh == NULL)
+               return 0;
+-      if (uh->check != 0) {
+-              switch (skb->ip_summed) {
+-              case CHECKSUM_NONE:
+-                      skb->csum = skb_checksum(skb, udphoff,
+-                                               skb->len - udphoff, 0);
+-                      fallthrough;
+-              case CHECKSUM_COMPLETE:
+-#ifdef CONFIG_IP_VS_IPV6
+-                      if (af == AF_INET6) {
+-                              if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+-                                                  &ipv6_hdr(skb)->daddr,
+-                                                  skb->len - udphoff,
+-                                                  IPPROTO_UDP,
+-                                                  skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      } else
+-#endif
+-                              if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
+-                                                    ip_hdr(skb)->daddr,
+-                                                    skb->len - udphoff,
+-                                                    ip_hdr(skb)->protocol,
+-                                                    skb->csum)) {
+-                                      IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+-                                                       "Failed checksum for");
+-                                      return 0;
+-                              }
+-                      break;
+-              default:
+-                      /* No need to checksum. */
+-                      break;
+-              }
++      if (!uh->check)
++              return 1;
++      if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
++              IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
++                               "Failed checksum for");
++              return 0;
+       }
+       return 1;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
new file mode 100644 (file)
index 0000000..3181c69
--- /dev/null
@@ -0,0 +1,64 @@
+From a03473866411d7e8c2a1b1d742b366131655cb1f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:03 -0400
+Subject: keys: fix out-of-bounds read in keyring_get_key_chunk()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
+
+For description-level chunks keyring_get_key_chunk() advances the read
+pointer by level * sizeof(long) past the inline prefix but only
+bounds-checks the prefix, so a long enough key description is read past
+its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
+offset and bounds-check the description against it before reading.
+
+The walk only reaches a description-level chunk when two keys collide
+through the hash, x, type and domain_tag chunks, so this is reached from
+an unprivileged add_key(2) with a crafted pair of same-type keys whose
+index hashes collide; KASAN reports a slab-out-of-bounds read.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 7a2ee0ded7c93..085f7a743354c 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+       unsigned long chunk = 0;
+       const u8 *d;
+       int desc_len = index_key->desc_len, n = sizeof(chunk);
++      unsigned int offset;
+       level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
+       switch (level) {
+@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               return (unsigned long)index_key->domain_tag;
+       default:
+               level -= 4;
+-              if (desc_len <= sizeof(index_key->desc))
++              offset = sizeof(index_key->desc) + level * sizeof(long);
++              if (desc_len <= offset)
+                       return 0;
+-              d = index_key->description + sizeof(index_key->desc);
+-              d += level * sizeof(long);
+-              desc_len -= sizeof(index_key->desc);
++              d = index_key->description + offset;
++              desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
+               do {
+-- 
+2.53.0
+
diff --git a/queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
new file mode 100644 (file)
index 0000000..aa65c3e
--- /dev/null
@@ -0,0 +1,63 @@
+From 9b04ac2309139aa32d2d9a7a7d49729383c674e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:15:04 -0400
+Subject: keys: make keyring key-chunk byte order agree with
+ keyring_diff_objects()
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
+
+keyring_get_key_chunk() loads description bytes into the index chunk low
+address first, while keyring_diff_objects() numbers the first differing
+bit from the low end and folds the absolute byte index into the level
+without removing the inline-prefix offset the level already carries.
+The two disagree on byte order and bit position, so the array can be
+told two keys first differ at a bit that does not differ in the chunk
+the walker uses, letting crafted descriptions collide into one node.
+
+Load the chunk in the order keyring_diff_objects() assumes and drop the
+inline-prefix length when folding the byte index into the level.  This
+only changes the in-memory ordering used to place keys within a keyring;
+add, search and read of non-colliding keys are unaffected.
+
+Fixes: f771fde82051 ("keys: Simplify key description management")
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/keyring.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/security/keys/keyring.c b/security/keys/keyring.c
+index 085f7a743354c..15bf4af8f2821 100644
+--- a/security/keys/keyring.c
++++ b/security/keys/keyring.c
+@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
+               desc_len -= offset;
+               if (desc_len > n)
+                       desc_len = n;
++              d += desc_len;
+               do {
+                       chunk <<= 8;
+-                      chunk |= *d++;
++                      chunk |= *--d;
+               } while (--desc_len > 0);
+               return chunk;
+       }
+@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)
+       return -1;
+ differ_plus_i:
+-      level += i;
++      level += i - (int)sizeof(a->desc);
+ differ:
+       i = level * 8 + __ffs(seg_a ^ seg_b);
+       return i;
+-- 
+2.53.0
+
diff --git a/queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
new file mode 100644 (file)
index 0000000..297ab3a
--- /dev/null
@@ -0,0 +1,96 @@
+From 45f7ac188ea3e0393e7447921593f3c98d9c8f65 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:22:30 +0300
+Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return
+ type
+
+From: Fabrice Derepas <fabrice.derepas@canonical.com>
+
+[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ]
+
+Two correctness and type-hygiene issues exist in the DCP trusted keys
+implementation.
+
+First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob
+without checking if it exceeds MAX_KEY_SIZE.  If a crafted blob provides a
+payload_len larger than 128, the subsequent do_aead_crypto() call writes
+past the end of the p->key array into the adjacent p->blob buffer within
+the same struct trusted_key_payload -- the caller's own input, not
+unrelated kernel memory.  While not exploitable, this violates strict array
+bounds and triggers static analyzers.  Fix this by adding a validation
+check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the
+length, matching the checks already done in trusted_core.c.
+
+Second, calc_blob_len() calculates a sum in size_t that truncates to
+unsigned int on 64-bit platforms.  Because the DCP hardware is only present
+on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally
+equivalent in production, making this truncation harmless in practice.
+Nevertheless, updating the return type to size_t (and subsequently updating
+'blen' in the seal/unseal paths) resolves type-narrowing warnings and
+improves overall code hygiene.
+
+Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys")
+Signed-off-by: Fabrice Derepas <fabrice.derepas@canonical.com>
+Reviewed-by: David Gstir <david@sigma-star.at>
+Reviewed-by: Richard Weinberger <richard@nod.at>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c
+index 7b6eb655df0cb..c078adebe190e 100644
+--- a/security/keys/trusted-keys/trusted_dcp.c
++++ b/security/keys/trusted-keys/trusted_dcp.c
+@@ -69,7 +69,7 @@ static bool skip_zk_test;
+ module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
+ MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
+-static unsigned int calc_blob_len(unsigned int payload_len)
++static size_t calc_blob_len(unsigned int payload_len)
+ {
+       return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
+ }
+@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key)
+ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key;
+       blen = calc_blob_len(p->key_len);
+@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+ {
+       struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+-      int blen, ret;
++      size_t blen;
++      int ret;
+       u8 *plain_blob_key = NULL;
+       if (b->fmt_version != DCP_BLOB_VERSION) {
+@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+       }
+       p->key_len = le32_to_cpu(b->payload_len);
++      if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) {
++              ret = -EINVAL;
++              goto out;
++      }
++
+       blen = calc_blob_len(p->key_len);
+       if (blen != p->blob_len) {
+-              pr_err("DCP blob has bad length: %i != %i\n", blen,
++              pr_err("DCP blob has bad length: %zu != %u\n", blen,
+                      p->blob_len);
+               ret = -EINVAL;
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
new file mode 100644 (file)
index 0000000..c0ba9e3
--- /dev/null
@@ -0,0 +1,50 @@
+From 9e912bb30ff00c1a612395a63dae37630fd570f8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:04:19 +0900
+Subject: ksmbd: fix use-after-free in __close_file_table_ids()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
+
+A ksmbd_file can remain alive after logical close while another session
+holds a temporary reference obtained through ksmbd_lookup_fd_inode().
+ksmbd_close_fd() currently marks the file closed and drops the idr-owned
+reference, but leaves the pointer published in the closing session's idr
+until the final reference is dropped.
+
+If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final()
+supplies the foreign session's file table to __ksmbd_close_fd(). The object
+is then freed without being removed from its owner's idr, and the owner
+session later dereferences the stale pointer during file-table teardown.
+
+Remove the volatile id from the owner's idr while ksmbd_close_fd() still
+holds that table's lock, and clear volatile_id before dropping
+the idr-owned reference. A later foreign final put then only performs
+physical destruction and cannot remove the object from the wrong table.
+
+Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp")
+Reported-by: Yunseong Kim <yunseong.kim@est.tech>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index b12c0bb527e41..c7d49aac7470a 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -595,6 +595,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      idr_remove(ft->idr, id);
++                      fp->volatile_id = KSMBD_NO_FID;
+                       closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ksmbd-return-success-for-deferred-final-close.patch b/queue-7.1/ksmbd-return-success-for-deferred-final-close.patch
new file mode 100644 (file)
index 0000000..f38fb40
--- /dev/null
@@ -0,0 +1,64 @@
+From d48c0eb66c502f69fd3d102841ac4fabd122dd04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jun 2026 19:41:08 +0900
+Subject: ksmbd: return success for deferred final close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ]
+
+ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
+reference. If another in-flight request still holds a reference, the final
+close is deferred until that request drops its reference.
+
+The function currently returns -EINVAL in that deferred-final-close case
+because fp is cleared when the reference count does not reach zero.  That
+turns a valid close into STATUS_FILE_CLOSED.
+
+smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
+closes the same directory handle before receiving the find response.
+The query holds a reference while it builds the response, so close must
+mark the handle closed and return success even though final teardown is
+delayed. Track whether the handle was successfully transitioned to
+FP_CLOSED and return success when only the final close is deferred.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs_cache.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index aa0924c9fdf96..b12c0bb527e41 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -580,6 +580,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+ {
+       struct ksmbd_file       *fp;
+       struct ksmbd_file_table *ft;
++      bool closed = false;
+       if (!has_file_id(id))
+               return 0;
+@@ -594,6 +595,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+                       fp = NULL;
+               else {
+                       fp->f_state = FP_CLOSED;
++                      closed = true;
+                       if (!atomic_dec_and_test(&fp->refcount))
+                               fp = NULL;
+               }
+@@ -601,7 +603,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+       write_unlock(&ft->lock);
+       if (!fp)
+-              return -EINVAL;
++              return closed ? 0 : -EINVAL;
+       __put_fd_final(work, fp);
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch b/queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch
new file mode 100644 (file)
index 0000000..0d5d802
--- /dev/null
@@ -0,0 +1,53 @@
+From b7a07da076fa181a9ef91d75a9365cb147f87528 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 10:32:01 +0900
+Subject: ksmbd: use memcmp() to compare ClientGUIDs
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e8bb506e6ef749ac0336f3e579d8d02396b7d832 ]
+
+ClientGUID is a fixed-size binary value and can contain embedded NUL
+bytes. strncmp() stops comparing at the first NUL byte, so different
+ClientGUID values can incorrectly be treated as equal.
+
+Use memcmp() in SMB3 multichannel session binding and
+FSCTL_VALIDATE_NEGOTIATE_INFO to compare all SMB2_CLIENT_GUID_SIZE
+bytes.
+
+Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
+Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
+Reported-by: Samu <nomomentomori@gmail.com>
+Suggested-by: Samu <nomomentomori@gmail.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>
+---
+ fs/smb/server/smb2pdu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index c29850170b315..5df4a8cc4d2e6 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1792,7 +1792,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
+                       goto out_err;
+               }
+-              if (strncmp(conn->ClientGUID, sess->ClientGUID,
++              if (memcmp(conn->ClientGUID, sess->ClientGUID,
+                           SMB2_CLIENT_GUID_SIZE)) {
+                       rc = -ENOENT;
+                       goto out_err;
+@@ -8186,7 +8186,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
+               goto err_out;
+       }
+-      if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) {
++      if (memcmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch b/queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch
new file mode 100644 (file)
index 0000000..2ad3b3d
--- /dev/null
@@ -0,0 +1,67 @@
+From c53677d86b6c396043d8d594206430cf7249e7db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 10:52:38 +0100
+Subject: KVM: arm64: Add missing hyp_enter when trapping sysreg
+
+From: Vincent Donnefort <vdonnefort@google.com>
+
+[ Upstream commit e7821048e8d72a94b1fb7422f8b45aa374ff076f ]
+
+Add a missing hypervisor event call for hyp_enter on sysreg trapping,
+causing an unbalanced hyp_enter/hyp_exit.
+
+The enum hyp_enter_exit_reason is not ABI, so we can keep the ERET
+reasons at the end for clarity.
+
+Fixes: 696dfec22b8e ("KVM: arm64: Add hyp_enter/hyp_exit events to nVHE/pKVM hyp")
+Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
+Reviewed-by: Fuad Tabba <tabba@google.com>
+Tested-by: Fuad Tabba <tabba@google.com>
+Link: https://patch.msgid.link/20260617095238.1530121-1-vdonnefort@google.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/kvm_hypevents.h | 1 +
+ arch/arm64/kvm/hyp/nvhe/hyp-main.c     | 1 +
+ arch/arm64/kvm/hyp_trace.c             | 1 +
+ 3 files changed, 3 insertions(+)
+
+diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
+index 743c49bd878f7..5f6e6789d1211 100644
+--- a/arch/arm64/include/asm/kvm_hypevents.h
++++ b/arch/arm64/include/asm/kvm_hypevents.h
+@@ -12,6 +12,7 @@
+ enum hyp_enter_exit_reason {
+       HYP_REASON_SMC,
+       HYP_REASON_HVC,
++      HYP_REASON_SYS,
+       HYP_REASON_PSCI,
+       HYP_REASON_HOST_ABORT,
+       HYP_REASON_GUEST_EXIT,
+diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+index a0da08caa6c27..4cd321c27d042 100644
+--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
++++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+@@ -925,6 +925,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)
+               handle_host_mem_abort(host_ctxt);
+               break;
+       case ESR_ELx_EC_SYS64:
++              trace_hyp_enter(host_ctxt, HYP_REASON_SYS);
+               if (handle_host_mte(esr))
+                       break;
+               fallthrough;
+diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
+index 3f4ba2034a155..210807edacf7f 100644
+--- a/arch/arm64/kvm/hyp_trace.c
++++ b/arch/arm64/kvm/hyp_trace.c
+@@ -409,6 +409,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason)
+       static const char strs[][12] = {
+               "smc",
+               "hvc",
++              "sys",
+               "psci",
+               "host_abort",
+               "guest_exit",
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch b/queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch
new file mode 100644 (file)
index 0000000..b16507c
--- /dev/null
@@ -0,0 +1,85 @@
+From bbdbf62ecec70e4f922b63542c838f1cf8fbc5b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 11:51:00 +0100
+Subject: KVM: arm64: Fix hyp_trace clock disabling
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Vincent Donnefort <vdonnefort@google.com>
+
+[ Upstream commit bbece712cfc7f286b2908ac120dcf700279d87eb ]
+
+Fix the disable path in hyp_trace_clock_enable(), which fell through to
+re-initialize and reschedule the clock after cancelling the work. Return
+early instead.
+
+While at it, cleanup hyp_trace_clock::lock which is unused and
+hyp_trace_clock::running which is redundant: the trace_remote framework
+already serializes calls to the callback enable_tracing.
+
+Fixes: b22888917fa4 ("KVM: arm64: Sync boot clock with the nVHE/pKVM hyp")
+Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
+Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev> (✓ DKIM/linux.dev)
+Link: https://patch.msgid.link/20260715105100.3178255-1-vdonnefort@google.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/kvm/hyp_trace.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
+index c4b3ee5521313..ac6a2db317323 100644
+--- a/arch/arm64/kvm/hyp_trace.c
++++ b/arch/arm64/kvm/hyp_trace.c
+@@ -37,8 +37,6 @@ static struct hyp_trace_clock {
+       u32                     shift;
+       struct delayed_work     work;
+       struct completion       ready;
+-      struct mutex            lock;
+-      bool                    running;
+ } hyp_clock;
+ static void __hyp_clock_work(struct work_struct *work)
+@@ -110,12 +108,9 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl
+ {
+       struct system_time_snapshot snap;
+-      if (hyp_clock->running == enable)
+-              return;
+-
+       if (!enable) {
+               cancel_delayed_work_sync(&hyp_clock->work);
+-              hyp_clock->running = false;
++              return;
+       }
+       ktime_get_snapshot(&snap);
+@@ -128,7 +123,6 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl
+       INIT_DELAYED_WORK(&hyp_clock->work, __hyp_clock_work);
+       schedule_delayed_work(&hyp_clock->work, msecs_to_jiffies(CLOCK_INIT_MS));
+       wait_for_completion(&hyp_clock->ready);
+-      hyp_clock->running = true;
+ }
+ /* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */
+@@ -304,9 +298,15 @@ static void hyp_trace_unload(struct trace_buffer_desc *desc, void *priv)
+ static int hyp_trace_enable_tracing(bool enable, void *priv)
+ {
++      int ret;
++
+       hyp_trace_clock_enable(&hyp_clock, enable);
+-      return kvm_call_hyp_nvhe(__tracing_enable, enable);
++      ret = kvm_call_hyp_nvhe(__tracing_enable, enable);
++      if (ret)
++              hyp_trace_clock_enable(&hyp_clock, !enable);
++
++      return ret;
+ }
+ static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv)
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch b/queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch
new file mode 100644 (file)
index 0000000..f773580
--- /dev/null
@@ -0,0 +1,69 @@
+From 58adc51661cb8bba4fd7dba4fc699a0aae6e6524 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 12:48:19 +0100
+Subject: KVM: arm64: Fix hyp_trace_desc allocation size in hyp_trace_load()
+
+From: Vincent Donnefort <vdonnefort@google.com>
+
+[ Upstream commit ca28278d10ec234592989ad370d58294b9d43e0f ]
+
+The footprint calculated for struct hyp_trace_desc sizes only
+trace_buffer_desc and do not take into account the other fields. It
+worked so far thanks to the follow-up PAGE_ALIGN().
+
+Fix the descriptor size and while at it, enforce an overflow check after
+PAGE_ALIGN().
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp")
+Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
+Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
+Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
+Link: https://patch.msgid.link/20260710114819.2689386-3-vdonnefort@google.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/kvm/hyp_trace.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
+index 73c1731392c74..3f4ba2034a155 100644
+--- a/arch/arm64/kvm/hyp_trace.c
++++ b/arch/arm64/kvm/hyp_trace.c
+@@ -229,18 +229,22 @@ static int hyp_trace_buffer_share_hyp(struct hyp_trace_buffer *trace_buffer)
+ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv)
+ {
+       struct hyp_trace_buffer *trace_buffer = priv;
++      size_t desc_size, tb_desc_size;
+       struct hyp_trace_desc *desc;
+-      size_t desc_size;
+       int ret;
+       if (WARN_ON(trace_buffer->desc))
+               return ERR_PTR(-EINVAL);
+-      desc_size = trace_buffer_desc_size(size, num_possible_cpus());
++      tb_desc_size = trace_buffer_desc_size(size, num_possible_cpus());
++      desc_size = size_add(tb_desc_size, offsetof(struct hyp_trace_desc, trace_buffer_desc));
+       if (desc_size == SIZE_MAX)
+               return ERR_PTR(-E2BIG);
+       desc_size = PAGE_ALIGN(desc_size);
++      if (!desc_size)
++              return ERR_PTR(-E2BIG);
++
+       desc = (struct hyp_trace_desc *)alloc_pages_exact(desc_size, GFP_KERNEL);
+       if (!desc)
+               return ERR_PTR(-ENOMEM);
+@@ -256,7 +260,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv)
+       if (ret)
+               goto err_free_desc;
+-      ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, desc_size, size,
++      ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, tb_desc_size, size,
+                                       cpu_possible_mask);
+       if (ret)
+               goto err_free_backing;
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch b/queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch
new file mode 100644 (file)
index 0000000..9d934f1
--- /dev/null
@@ -0,0 +1,59 @@
+From d3c78e1994556d8dcb2e0c86400e831a966bcfec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 12:48:18 +0100
+Subject: KVM: arm64: Fix potential leak in
+ hyp_trace_buffer_alloc_bpages_backing
+
+From: Vincent Donnefort <vdonnefort@google.com>
+
+[ Upstream commit df7a9d376f7a388ecfacf8dfe0b5819ddfba6972 ]
+
+In the very unlikely event of a failure in __map_hyp, the allocated
+backing pages are leaked in hyp_trace_buffer_alloc_bpages_backing(). Fix
+this by freeing the pages on error.
+
+Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
+Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
+Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
+Link: https://patch.msgid.link/20260710114819.2689386-2-vdonnefort@google.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/kvm/hyp_trace.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
+index ac6a2db317323..73c1731392c74 100644
+--- a/arch/arm64/kvm/hyp_trace.c
++++ b/arch/arm64/kvm/hyp_trace.c
+@@ -154,6 +154,7 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_
+       int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1;
+       size_t backing_size;
+       void *start;
++      int ret;
+       backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages *
+                                 num_possible_cpus());
+@@ -162,10 +163,16 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_
+       if (!start)
+               return -ENOMEM;
++      ret = __map_hyp(start, backing_size);
++      if (ret) {
++              free_pages_exact(start, backing_size);
++              return ret;
++      }
++
+       trace_buffer->desc->bpages_backing_start = (unsigned long)start;
+       trace_buffer->desc->bpages_backing_size = backing_size;
+-      return __map_hyp(start, backing_size);
++      return ret;
+ }
+ static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer)
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch b/queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch
new file mode 100644 (file)
index 0000000..f5e082a
--- /dev/null
@@ -0,0 +1,121 @@
+From d39715e84a55145c825661d4c02a445d95ab0512 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:03:54 +0100
+Subject: KVM: arm64: Reject guest_memfd memslots when the VM has MTE
+
+From: Alexandru Elisei <alexandru.elisei@arm.com>
+
+[ Upstream commit 679d7201c1f09e37fa1c12ce28d84079c17fc87f ]
+
+The user cannot use MTE on VMAs created by mapping a guest_memfd file,
+as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED.
+
+When creating a guest_memfd backed memslot,
+kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for
+the VM and if guest_memfd has been mapped in a VMA that intersects the
+memslot.
+
+However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly
+states that the only condition for userspace_addr is for it to be a legal
+userspace address, but the mapping is not required to be valid nor
+populated at memslot creation.
+
+If userspace sets userspace_addr to an address that hasn't been mapped, or
+if userspace_addr belongs to a VMA that isn't backed by the guest_memfd
+file, or if the VMA doesn't intersect the memslot, memslot creation is
+successful and KVM ends up with a VM with MTE and guest_memfd-backed
+memslots.
+
+The same happens if the order is reversed: when userspace enables MTE, KVM
+does not check if memslots backed by guest_memfd are already present.
+
+Fix both issues by rejecting guest_memfd-backed memslots when MTE is
+enabled, and by rejecting MTE when guest_memfd-backed memslots are already
+present.
+
+Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory")
+Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
+Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
+Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
+Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/virt/kvm/api.rst |  6 ++++++
+ arch/arm64/kvm/arm.c           | 25 +++++++++++++++++++------
+ arch/arm64/kvm/mmu.c           |  4 ++++
+ 3 files changed, 29 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
+index 52bbbb553ce10..934ec29093e36 100644
+--- a/Documentation/virt/kvm/api.rst
++++ b/Documentation/virt/kvm/api.rst
+@@ -8401,6 +8401,12 @@ When this capability is enabled all memory in memslots must be mapped as
+ attempts to create a memslot with an invalid mmap will result in an
+ -EINVAL return.
++``guest_memfd``, even though it is an anonymous file, is not supported with MTE.
++Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability
++is enabled, or attempting to enable the MTE capability after
++``guest_memfd``-backed memslots have been created, will result in an -EINVAL
++return.
++
+ When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to
+ perform a bulk copy of tags to/from the guest.
+diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
+index 9ffd5d4079e6c..00df8783c30df 100644
+--- a/arch/arm64/kvm/arm.c
++++ b/arch/arm64/kvm/arm.c
+@@ -148,14 +148,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
+               set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
+                       &kvm->arch.flags);
+               break;
+-      case KVM_CAP_ARM_MTE:
+-              mutex_lock(&kvm->lock);
+-              if (system_supports_mte() && !kvm->created_vcpus) {
+-                      r = 0;
+-                      set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
++      case KVM_CAP_ARM_MTE: {
++              struct kvm_memory_slot *memslot;
++              int bkt;
++
++              guard(mutex)(&kvm->lock);
++              if (!system_supports_mte() || kvm->created_vcpus)
++                      break;
++
++              r = 0;
++              guard(mutex)(&kvm->slots_lock);
++              kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) {
++                      if (kvm_slot_has_gmem(memslot)) {
++                              r = -EINVAL;
++                              break;
++                      }
+               }
+-              mutex_unlock(&kvm->lock);
++              if (r == 0)
++                      set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
+               break;
++
++      }
+       case KVM_CAP_ARM_SYSTEM_SUSPEND:
+               r = 0;
+               set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags);
+diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
+index 6e95514d5a76e..726827c85c732 100644
+--- a/arch/arm64/kvm/mmu.c
++++ b/arch/arm64/kvm/mmu.c
+@@ -2625,6 +2625,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
+       if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
+               return -EINVAL;
++      /* guest_memfd is incompatible with MTE. */
++      if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm))
++              return -EINVAL;
++
+       hva = new->userspace_addr;
+       reg_end = hva + (new->npages << PAGE_SHIFT);
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch b/queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch
new file mode 100644 (file)
index 0000000..8b0fa10
--- /dev/null
@@ -0,0 +1,196 @@
+From fb73c581f2842790299234ab782624a60383c734 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 12:51:37 +0200
+Subject: KVM: arm64: vgic: Fix race between LPI release and re-registration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Carlos López <clopez@suse.de>
+
+[ Upstream commit cbfe2b24a1ea9de35032dbdd100fdc700f5be92d ]
+
+Fix a potential race between decrementing an LPI's reference count and
+evicting that structure from the LPI xarray.
+
+LPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa).
+When the reference count of an LPI structure drops to zero,
+vgic_release_lpi_locked() removes the structure from the xarray and
+frees it under the xarray lock.
+
+However, the release of an LPI can race with a concurrent LPI
+re-registration with the same INTID via vgic_add_lpi() on another CPU,
+since the reference count drop and the xarray eviction are not performed
+in a single atomic step. This can happen e.g. if the guest issues a
+DISCARD while the LPI is still referenced from a vCPU's active-pending
+list (ap_list), and the same INTID is re-mapped via MAPTI.
+
+Particularly, vgic_release_lpi_locked() is called from two distinct
+paths: direct release via vgic_put_irq(), and deferred release via
+vgic_release_deleted_lpis(). During direct release, the issue can result
+in deleting a newly registered LPI from the xarray:
+
+  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)
+  ====================                    =====================
+  vgic_put_irq()
+      __vgic_put_irq()
+          refcount_dec_and_test()
+                                          vgic_add_lpi()
+                                              xa_lock_irqsave()
+                                              old_irq = xa_load(.., intid)
+                                              vgic_try_get_irq_ref(old_irq) == false
+                        new IRQ inserted -->  __xa_store(.., intid, ..)
+                                              xa_unlock_irqrestore()
+  xa_lock_irqsave();
+  vgic_release_lpi_locked()
+      __xa_erase(.., irq->intid)   <-- BUG: new IRQ is erased
+      kfree_rcu(old_irq)
+
+During the deferred release path, the old IRQ can be leaked:
+
+  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)
+  ====================                    =====================
+  vgic_put_irq_norelease()
+      __vgic_put_irq()
+          refcount_dec_and_test()
+      irq->pending_release = true
+                                          vgic_add_lpi()
+                                              xa_lock_irqsave()
+                                              old_irq = xa_load(.., intid)
+                                              vgic_try_get_irq_ref(oldirq) == false
+                 BUG: old IRQ overwritten --> __xa_store(.., intid, ..)
+                                              xa_unlock_irqrestore()
+
+  vgic_release_deleted_lpis()
+      xa_lock_irqsave()
+      xa_for_each() { .. } <-- old IRQ with pending_release = true
+                               is gone, so it cannot be released
+
+To fix the direct release path, move the reference count drop inside
+the xarray lock, making sure that vgic_add_lpi() never encounters the
+to-be-released LPI.
+
+In the deferred release path, the refcount drop must happen under a raw
+spinlock, so the xarray lock cannot be grabbed, and the same solution
+does not work. Instead, update vgic_add_lpi(), so that if it evicts
+an LPI from the xarray, it takes on the responsibility of freeing it.
+Consequently, an LPI may now be freed concurrently after a deferred
+release drops the refcount, so accessing the pending_release field is no
+longer safe from use-after-free. Delete all uses of the flag, and update
+vgic_release_deleted_lpis() to identify orphaned LPIs purely based on
+their refcount.
+
+Reported-by: Claude:claude-opus-4-6
+Fixes: 3a08a6ca7c37 ("KVM: arm64: vgic-v3: Use bare refcount for VGIC LPIs")
+Fixes: d54594accf73 ("KVM: arm64: vgic-v3: Erase LPIs from xarray outside of raw spinlocks")
+Signed-off-by: Carlos López <clopez@suse.de>
+Link: https://patch.msgid.link/20260715105137.3973823-4-clopez@suse.de
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/kvm/vgic/vgic-its.c | 24 ++++++++++++++++--------
+ arch/arm64/kvm/vgic/vgic.c     | 18 ++++++++----------
+ include/kvm/arm_vgic.h         |  3 ---
+ 3 files changed, 24 insertions(+), 21 deletions(-)
+
+diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
+index 7abb685c65465..e9ef7686a9524 100644
+--- a/arch/arm64/kvm/vgic/vgic-its.c
++++ b/arch/arm64/kvm/vgic/vgic-its.c
+@@ -116,18 +116,26 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
+               kfree(irq);
+               irq = oldirq;
+       } else {
+-              ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0));
+-      }
+-
+-      xa_unlock_irqrestore(&dist->lpi_xa, flags);
++              /*
++               * The entry is either empty or contains a dead LPI (refcount=0)
++               * from the deferred release path, pending cleanup by
++               * vgic_release_deleted_lpis(). Evict and free it if present.
++               */
++              oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0);
++              ret = xa_err(oldirq);
++              if (ret) {
++                      xa_unlock_irqrestore(&dist->lpi_xa, flags);
++                      kfree(irq);
+-      if (ret) {
+-              xa_release(&dist->lpi_xa, intid);
+-              kfree(irq);
++                      return ERR_PTR(ret);
++              }
+-              return ERR_PTR(ret);
++              if (oldirq && !WARN_ON_ONCE(refcount_read(&oldirq->refcount)))
++                      kfree_rcu(oldirq, rcu);
+       }
++      xa_unlock_irqrestore(&dist->lpi_xa, flags);
++
+       /*
+        * We "cache" the configuration table entries in our struct vgic_irq's.
+        * However we only have those structs for mapped IRQs, so we read in
+diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
+index 4cd818ce90933..7fb9057fdd7ee 100644
+--- a/arch/arm64/kvm/vgic/vgic.c
++++ b/arch/arm64/kvm/vgic/vgic.c
+@@ -147,11 +147,7 @@ static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
+ static __must_check bool vgic_put_irq_norelease(struct kvm *kvm, struct vgic_irq *irq)
+ {
+-      if (!__vgic_put_irq(kvm, irq))
+-              return false;
+-
+-      irq->pending_release = true;
+-      return true;
++      return __vgic_put_irq(kvm, irq);
+ }
+ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
+@@ -168,12 +164,14 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
+               guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock);
+       }
+-      if (!__vgic_put_irq(kvm, irq))
++      if (!irq_is_lpi(kvm, irq->intid))
+               return;
+-      xa_lock_irqsave(&dist->lpi_xa, flags);
+-      vgic_release_lpi_locked(dist, irq);
+-      xa_unlock_irqrestore(&dist->lpi_xa, flags);
++      if (refcount_dec_and_lock_irqsave(&irq->refcount,
++                                        &dist->lpi_xa.xa_lock, &flags)) {
++              vgic_release_lpi_locked(dist, irq);
++              xa_unlock_irqrestore(&dist->lpi_xa, flags);
++      }
+ }
+ static void vgic_release_deleted_lpis(struct kvm *kvm)
+@@ -185,7 +183,7 @@ static void vgic_release_deleted_lpis(struct kvm *kvm)
+       xa_lock_irqsave(&dist->lpi_xa, flags);
+       xa_for_each(&dist->lpi_xa, intid, irq) {
+-              if (irq->pending_release)
++              if (!refcount_read(&irq->refcount))
+                       vgic_release_lpi_locked(dist, irq);
+       }
+diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
+index 1388dc6028a9a..700d6c519d133 100644
+--- a/include/kvm/arm_vgic.h
++++ b/include/kvm/arm_vgic.h
+@@ -242,9 +242,6 @@ struct vgic_irq {
+                                        * affinity reg (v3).
+                                        */
+-      bool pending_release:1;         /* Used for LPIs only, unreferenced IRQ
+-                                       * pending a release */
+-
+       bool pending_latch:1;           /* The pending latch state used to calculate
+                                        * the pending state for both level
+                                        * and edge triggered IRQs. */
+-- 
+2.53.0
+
diff --git a/queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch b/queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch
new file mode 100644 (file)
index 0000000..59e0eef
--- /dev/null
@@ -0,0 +1,76 @@
+From a5e52309cb95c494c782e3c9979dc9583a279cdb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 12:51:38 +0200
+Subject: KVM: arm64: vgic: Mitigate potential LPI registration failure
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Carlos López <clopez@suse.de>
+
+[ Upstream commit 21f12496fdd357ad4e1fcdd07dc80ab7378f7d24 ]
+
+Mitigate a potential failure when inserting a new LPI into the VGIC LPI
+xarray.
+
+When vgic_add_lpi() is preparing to register a new LPI, it pre-allocates
+an xarray entry using xa_reserve_irq(), so that it can later perform the
+insertion under the xarray lock without allocating.
+
+However, since xa_reserve_irq() is called before acquiring such lock,
+there is a potential race where xa_reserve_irq() observes a populated
+entry, thus not performing the allocation, and another CPU removes that
+entry before the xarray lock is grabbed to perform the insertion.
+
+  CPU0 (Adding new LPI)                      CPU1 (Releasing LPI)
+  =====================                      ===================
+  vgic_add_lpi()
+      /* Entry populated, does not allocate */
+      xa_reserve_irq(.., intid, ..)
+                                            vgic_release_deleted_lpis()
+                                                xa_lock_irqsave()
+                                                vgic_release_lpi_locked()
+                          xarray node freed --> __xa_erase(.., intid)
+                                                xa_unlock_irqrestore()
+      xa_lock_irqsave()
+      xa_load(.., intid) == NULL
+      vgic_try_get_irq_ref(NULL) == false
+      __xa_store(.., intid, irq, 0) <-- xarray node was freed, gfp=0
+                                        cannot allocate, returns -ENOMEM
+
+This can happen e.g. if the guest issues a DISCARD while the LPI is
+still referenced from a vCPU's active-pending list (ap_list), and the
+same INTID is re-mapped via MAPTI.
+
+Mitigate this by passing GFP_NOWAIT to __xa_store(), so that the
+allocation can happen under the lock in the rare case that this
+condition is hit. Add __GFP_ACCOUNT as well to match xa_reserve_irq()'s
+flags.
+
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 1d6f83f60f79 ("KVM: arm64: vgic: Store LPIs in an xarray")
+Signed-off-by: Carlos López <clopez@suse.de>
+Link: https://patch.msgid.link/20260715105137.3973823-5-clopez@suse.de
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/kvm/vgic/vgic-its.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
+index e9ef7686a9524..c15f1867adc20 100644
+--- a/arch/arm64/kvm/vgic/vgic-its.c
++++ b/arch/arm64/kvm/vgic/vgic-its.c
+@@ -121,7 +121,8 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
+                * from the deferred release path, pending cleanup by
+                * vgic_release_deleted_lpis(). Evict and free it if present.
+                */
+-              oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0);
++              oldirq = __xa_store(&dist->lpi_xa, intid, irq,
++                                  GFP_NOWAIT | __GFP_ACCOUNT);
+               ret = xa_err(oldirq);
+               if (ret) {
+                       xa_unlock_irqrestore(&dist->lpi_xa, flags);
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch b/queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch
new file mode 100644 (file)
index 0000000..7b78ba7
--- /dev/null
@@ -0,0 +1,51 @@
+From 64b50107e8669e2205e470bf25aec71d4bf94157 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:49 +0000
+Subject: mshv: Fix duplicate GSI detection for GSI 0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 649dd135491945afa544351e3e6d4a727de87020 ]
+
+The duplicate routing entry check in mshv_update_routing_table() uses
+guest_irq_num != 0 to detect whether a GSI slot is already occupied.
+This fails for GSI 0 because its guest_irq_num is 0 both when the slot
+is unused (zero-initialized) and when legitimately assigned. As a
+result, duplicate entries for GSI 0 are silently accepted, with the
+second entry overwriting the first — corrupting the routing table
+without any error reported to userspace.
+
+While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing
+in practice, the check is semantically wrong — it conflates
+"uninitialized" with "GSI number 0." Use girq_entry_valid instead,
+which is explicitly set to true when an entry is populated and remains
+zero for unused slots regardless of the GSI number.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_irq.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c
+index b3142c84dcbc2..65a4ffc82d566 100644
+--- a/drivers/hv/mshv_irq.c
++++ b/drivers/hv/mshv_irq.c
+@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition,
+               /*
+                * Allow only one to one mapping between GSI and MSI routing.
+                */
+-              if (girq->guest_irq_num != 0) {
++              if (girq->girq_entry_valid) {
+                       r = -EINVAL;
+                       goto out;
+               }
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch b/queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch
new file mode 100644 (file)
index 0000000..bd9d553
--- /dev/null
@@ -0,0 +1,84 @@
+From 72c2bb4bfe716ba75f8a6af16acb8354c109a95f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:43 +0000
+Subject: mshv: Fix level-triggered check on uninitialized data
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 ]
+
+In mshv_irqfd_assign(), the level-triggered validation for resample
+irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before
+mshv_irqfd_update() has populated the field. Since the irqfd struct is
+zero-allocated, level_triggered is always 0 at that point, causing the
+check to always reject resample irqfds with -EINVAL. This makes
+level-triggered interrupt resampling — used to avoid interrupt storms
+with assigned devices — completely non-functional.
+
+Move the check after the mshv_irqfd_update() call, which resolves the
+IRQ routing entry and populates irqfd_lapic_irq with the actual trigger
+mode.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c | 25 ++++++++++++++-----------
+ 1 file changed, 14 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 5995a62aff8d8..047e5bd432381 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+       init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
+       spin_lock_irq(&pt->pt_irqfds_lock);
++      ret = 0;
++      hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
++              if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
++                      continue;
++              /* This fd is used for another irq already. */
++              ret = -EBUSY;
++              spin_unlock_irq(&pt->pt_irqfds_lock);
++              goto fail;
++      }
++
++      idx = srcu_read_lock(&pt->pt_irq_srcu);
++      mshv_irqfd_update(pt, irqfd);
++
+ #if IS_ENABLED(CONFIG_X86)
+       if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
+           !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
+@@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
+                * Otherwise return with failure
+                */
+               spin_unlock_irq(&pt->pt_irqfds_lock);
++              srcu_read_unlock(&pt->pt_irq_srcu, idx);
+               ret = -EINVAL;
+               goto fail;
+       }
+ #endif
+-      ret = 0;
+-      hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
+-              if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
+-                      continue;
+-              /* This fd is used for another irq already. */
+-              ret = -EBUSY;
+-              spin_unlock_irq(&pt->pt_irqfds_lock);
+-              goto fail;
+-      }
+-      idx = srcu_read_lock(&pt->pt_irq_srcu);
+-      mshv_irqfd_update(pt, irqfd);
+       hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list);
+       spin_unlock_irq(&pt->pt_irqfds_lock);
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch b/queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch
new file mode 100644 (file)
index 0000000..ef90b72
--- /dev/null
@@ -0,0 +1,46 @@
+From 0965d8b05892ab74ed324cba10f11d810ccabeae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:44:37 +0000
+Subject: mshv: Fix missing error code on VP allocation failure
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit f546be6a19d24d02be576d8617cb26c7acb61594 ]
+
+In mshv_partition_ioctl_create_vp(), when kzalloc for the VP struct
+fails, the code jumps to the cleanup path without setting ret. At that
+point ret is 0 from the preceding successful mshv_vp_stats_map() call,
+so the function returns success to userspace despite having failed to
+create the VP. No fd is installed and no VP is registered in pt_vp_array,
+but userspace has no way to know the operation failed.
+
+Set ret to -ENOMEM before jumping to the cleanup path.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_root_main.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
+index 146726cc4e9ba..644f9b10cbba4 100644
+--- a/drivers/hv/mshv_root_main.c
++++ b/drivers/hv/mshv_root_main.c
+@@ -1117,8 +1117,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+               goto unmap_ghcb_page;
+       vp = kzalloc_obj(*vp);
+-      if (!vp)
++      if (!vp) {
++              ret = -ENOMEM;
+               goto unmap_stats_pages;
++      }
+       vp->vp_partition = mshv_partition_get(partition);
+       if (!vp->vp_partition) {
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch b/queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch
new file mode 100644 (file)
index 0000000..ca584ed
--- /dev/null
@@ -0,0 +1,68 @@
+From 406196c1969de2558a7686241b941cd8e887e78f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:15 +0000
+Subject: mshv: Fix race in mshv_irqfd_deassign
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 0762262ac3e70f65b3bb843fe892f8bac1562d08 ]
+
+mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list
+require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign()
+omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(),
+which does take the lock before calling mshv_irqfd_deactivate().
+
+Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons
+the node pointers rather than resetting them. Since
+mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev ==
+NULL), a poisoned node still appears active. If a concurrent path calls
+mshv_irqfd_deactivate() again on the same irqfd, the guard fails to
+prevent a double hlist_del() on poisoned pointers.
+
+Fix both issues:
+- Add the missing spin_lock_irq/spin_unlock_irq around the list
+  traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release().
+- Use hlist_del_init() instead of hlist_del() so the node is properly
+  marked as unhashed after removal, making the is_active guard reliable.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 90959f639dc32..5995a62aff8d8 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -284,7 +284,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd)
+       if (!mshv_irqfd_is_active(irqfd))
+               return;
+-      hlist_del(&irqfd->irqfd_hnode);
++      hlist_del_init(&irqfd->irqfd_hnode);
+       queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown);
+ }
+@@ -541,13 +541,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt,
+       if (IS_ERR(eventfd))
+               return PTR_ERR(eventfd);
++      spin_lock_irq(&pt->pt_irqfds_lock);
+       hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list,
+                                 irqfd_hnode) {
+               if (irqfd->irqfd_eventfd_ctx == eventfd &&
+                   irqfd->irqfd_irqnum == args->gsi)
+-
+                       mshv_irqfd_deactivate(irqfd);
+       }
++      spin_unlock_irq(&pt->pt_irqfds_lock);
+       eventfd_ctx_put(eventfd);
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch b/queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch
new file mode 100644 (file)
index 0000000..b44653d
--- /dev/null
@@ -0,0 +1,50 @@
+From 4315ec4df9a385b17c7c611c7b6ce74899701742 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:43:59 +0000
+Subject: mshv: Fix sleeping under spinlock in mshv_portid_alloc
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit a9708e550d11a53b22d932cdbfaa10c26346a2d0 ]
+
+idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a
+spinlock. GFP_KERNEL allows the allocator to sleep, triggering a
+sleeping-while-atomic bug.
+
+Fix by using idr_preload(GFP_KERNEL) before taking the lock to
+pre-allocate memory in a sleepable context, then idr_alloc() with
+GFP_NOWAIT inside the spinlock-protected section.
+
+Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_portid_table.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c
+index c349af1f0aaac..6f59b3e376247 100644
+--- a/drivers/hv/mshv_portid_table.c
++++ b/drivers/hv/mshv_portid_table.c
+@@ -40,12 +40,14 @@ mshv_port_table_fini(void)
+ int
+ mshv_portid_alloc(struct port_table_info *info)
+ {
+-      int ret = 0;
++      int ret;
++      idr_preload(GFP_KERNEL);
+       idr_lock(&port_table_idr);
+       ret = idr_alloc(&port_table_idr, info, PORTID_MIN,
+-                      PORTID_MAX, GFP_KERNEL);
++                      PORTID_MAX, GFP_NOWAIT);
+       idr_unlock(&port_table_idr);
++      idr_preload_end();
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch b/queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch
new file mode 100644 (file)
index 0000000..95013ac
--- /dev/null
@@ -0,0 +1,94 @@
+From d8e8579dc9e92dcadcc554905b3693591d41b507 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 00:28:11 +0000
+Subject: mshv: Order pt_vp_array publish against irqfd assertion path
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit b098dc869219c15dc49bf9cf63fb5fc1481d3373 ]
+
+mshv_partition_ioctl_create_vp() initialises a VP struct (allocations,
+mutex_init, init_waitqueue_head, page mappings) and then publishes the
+pointer into partition->pt_vp_array.  Several ISR paths read this array
+locklessly: the intercept ISR, the two scheduler ISRs, and
+mshv_try_assert_irq_fast() on the irqfd fast path.
+
+Of these, only mshv_try_assert_irq_fast() can structurally race the
+publish.  It runs from an eventfd waker without holding pt_mutex, and
+MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to
+refer to an existing VP at registration time.  A user can therefore
+register an irqfd targeting a yet-to-be-created VP, then trigger
+mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the
+same index.  On weakly-ordered architectures the reader can observe a
+non-NULL pointer in pt_vp_array before the initialising stores to the
+VP struct become visible, leading to use of partially-initialised
+fields (e.g. vp_register_page).
+
+The other ISR readers cannot reach this race: the hypervisor will not
+generate intercept or scheduler messages for a VP that has never been
+told to run, and the user can only call MSHV_RUN_VP on the VP fd
+returned by MSHV_CREATE_VP, which by construction is returned after
+the publish.  Leave those readers as plain loads.
+
+Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish
+the pointer, and pair it with smp_load_acquire() in
+mshv_try_assert_irq_fast().  On x86 these compile to plain accesses
+under TSO; on ARM64 they emit one-instruction acquire/release barriers,
+acceptable on this fast path.
+
+The destroy-side path (destroy_partition() clearing pt_vp_array[i] to
+NULL after kfree(vp)) has a separate ordering and lifetime concern
+that is out of scope here.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_eventfd.c   | 9 ++++++++-
+ drivers/hv/mshv_root_main.c | 8 +++++++-
+ 2 files changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
+index 047e5bd432381..06aef99c8298d 100644
+--- a/drivers/hv/mshv_eventfd.c
++++ b/drivers/hv/mshv_eventfd.c
+@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd)
+               return -EOPNOTSUPP;
+ #endif
+-      vp = partition->pt_vp_array[irq->lapic_apic_id];
++      /*
++       * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp().
++       * MSHV_IRQFD does not require the target lapic_apic_id to refer to an
++       * existing VP, so this read can race a concurrent VP creation; the
++       * acquire ensures that a non-NULL pointer implies the VP's
++       * initialising stores are visible.
++       */
++      vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]);
+       if (!vp->vp_register_page)
+               return -EOPNOTSUPP;
+diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
+index 644f9b10cbba4..8a15448e2ace9 100644
+--- a/drivers/hv/mshv_root_main.c
++++ b/drivers/hv/mshv_root_main.c
+@@ -1157,7 +1157,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+       /* already exclusive with the partition mutex for all ioctls */
+       partition->pt_vp_count++;
+-      partition->pt_vp_array[args.vp_index] = vp;
++      /*
++       * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which
++       * can run concurrently from an irqfd waker without holding pt_mutex.
++       * The release ensures the VP's initialising stores are visible to any
++       * reader that observes a non-NULL pointer in pt_vp_array.
++       */
++      smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
+       goto out;
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch b/queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch
new file mode 100644 (file)
index 0000000..a7c9e20
--- /dev/null
@@ -0,0 +1,96 @@
+From f68291f633c5200f9d42e71ce955bf50d5207226 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 15:44:32 +0000
+Subject: mshv: Publish VP to pt_vp_array before installing the file descriptor
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+
+[ Upstream commit 72e3b0311aa90568a4b42a64445d1d3e1dc5a34d ]
+
+mshv_partition_ioctl_create_vp() called anon_inode_getfd() before
+publishing the new VP into partition->pt_vp_array.  anon_inode_getfd()
+includes fd_install(), so the fd was live in current->files before the
+publish ran.
+
+A concurrent MSHV_RUN_VP ioctl on that fd does not serialise against the
+in-progress MSHV_CREATE_VP — it takes vp->vp_mutex, not the partition
+mutex.  Once the VP starts running and traps, mshv_intercept_isr() can look
+up partition->pt_vp_array[vp_index] and observe NULL, silently dropping the
+intercept message.
+
+Split the fd creation: reserve an fd with get_unused_fd_flags(), create the
+file with anon_inode_getfile(), publish the VP via smp_store_release(), and
+finally call fd_install() as the userspace-visibility commit point.
+
+Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
+Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
+Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_root_main.c | 29 ++++++++++++++++++++++-------
+ 1 file changed, 22 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
+index 8a15448e2ace9..cc2cfce2aefdb 100644
+--- a/drivers/hv/mshv_root_main.c
++++ b/drivers/hv/mshv_root_main.c
+@@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+       struct mshv_vp *vp;
+       struct page *intercept_msg_page, *register_page, *ghcb_page;
+       struct hv_stats_page *stats_pages[2];
++      struct file *file;
++      int fd;
+       long ret;
+       if (copy_from_user(&args, arg, sizeof(args)))
+@@ -1146,14 +1148,18 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+       if (ret)
+               goto put_partition;
+-      /*
+-       * Keep anon_inode_getfd last: it installs fd in the file struct and
+-       * thus makes the state accessible in user space.
+-       */
+-      ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
+-                             O_RDWR | O_CLOEXEC);
+-      if (ret < 0)
++      fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
++      if (fd < 0) {
++              ret = fd;
+               goto remove_debugfs_vp;
++      }
++
++      file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp,
++                                O_RDWR | O_CLOEXEC);
++      if (IS_ERR(file)) {
++              ret = PTR_ERR(file);
++              goto put_unused_vp_fd;
++      }
+       /* already exclusive with the partition mutex for all ioctls */
+       partition->pt_vp_count++;
+@@ -1165,8 +1171,17 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
+        */
+       smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
++      /*
++       * fd_install() is the userspace-visibility commit point.  Must be the
++       * last operation that can fail or be observed.
++       */
++      fd_install(fd, file);
++      ret = fd;
++
+       goto out;
++put_unused_vp_fd:
++      put_unused_fd(fd);
+ remove_debugfs_vp:
+       mshv_debugfs_vp_remove(vp);
+ put_partition:
+-- 
+2.53.0
+
diff --git a/queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch b/queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch
new file mode 100644 (file)
index 0000000..01c2f95
--- /dev/null
@@ -0,0 +1,35 @@
+From b984fce105ff09d0a680c3ec7dae788d3789283b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jul 2026 09:28:52 +0800
+Subject: mshv_vtl: fix fd leak in mshv_ioctl_create_vtl()
+
+From: Yi Xie <xieyi@kylinos.cn>
+
+[ Upstream commit ec08dd5b9ffa52980908805bdc8a55a8f1bc6667 ]
+
+put_unused_fd() if anon_inode_getfile() fails.
+
+Fixes: 7bfe3b8ea6e30 ("Drivers: hv: Introduce mshv_vtl driver")
+Signed-off-by: Yi Xie <xieyi@kylinos.cn>
+Reviewed-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hv/mshv_vtl_main.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c
+index c194007014678..3ce4b4d6c11eb 100644
+--- a/drivers/hv/mshv_vtl_main.c
++++ b/drivers/hv/mshv_vtl_main.c
+@@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev)
+       file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops,
+                                 vtl, O_RDWR);
+       if (IS_ERR(file)) {
++              put_unused_fd(fd);
+               kfree(vtl);
+               return PTR_ERR(file);
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
new file mode 100644 (file)
index 0000000..fc45b63
--- /dev/null
@@ -0,0 +1,40 @@
+From 804a670dea2e3fe8ff24d9123b9655541084a2f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 06:26:05 +0000
+Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames
+
+From: David Corvaglia <david@corvaglia.dev>
+
+[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
+
+oui is a pointer, so sizeof(oui) is the pointer size. The MRA
+Option TLV thus advertises a wrong length (15 vs 10 on x86_64),
+causing misparsing of the frame on peers. Fix is to replace
+with sizeof(*oui).
+
+Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA")
+Signed-off-by: David Corvaglia <david@corvaglia.dev>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bridge/br_mrp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
+index f1aa67f7a0510..460be392af477 100644
+--- a/net/bridge/br_mrp.c
++++ b/net/bridge/br_mrp.c
+@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+               struct br_mrp_oui_hdr *oui = NULL;
+               u8 length;
+-              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) +
++              length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) +
+                       MRP_OPT_PADDING;
+               br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length);
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
new file mode 100644 (file)
index 0000000..fac869a
--- /dev/null
@@ -0,0 +1,78 @@
+From c83e25c52e059d9ebe6dc9ac65e02fe9c986daab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 07:29:01 +0000
+Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
+
+When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry
+under memory pressure or tree size caps, redirect handlers previously fell
+back to sending un-rate-limited ICMP/NDISC Redirect messages.
+
+In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL.
+In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into
+inet_peer_xrlim_allow(), which returned true when peer == NULL.
+
+Because ICMP/NDISC Redirects are not part of the default global rate limit
+mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates
+an un-rate-limited ICMP packet storm.
+
+Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and
+ndisc_send_redirect() when peer is NULL.
+
+Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/route.c      | 2 --
+ net/ipv6/ip6_output.c | 2 +-
+ net/ipv6/ndisc.c      | 2 ++
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 3d62d45d84bda..7cc9a7336c30a 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
+       peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
+       if (!peer) {
+               rcu_read_unlock();
+-              icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
+-                        rt_nexthop(rt, ip_hdr(skb)->daddr));
+               return;
+       }
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index b5d9c57fe439f..634f581e17114 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -641,7 +641,7 @@ int ip6_forward(struct sk_buff *skb)
+               /* Limit redirects both by destination (here)
+                  and by source (inside ndisc_send_redirect)
+                */
+-              if (inet_peer_xrlim_allow(peer, 1*HZ))
++              if (peer && inet_peer_xrlim_allow(peer, 1*HZ))
+                       ndisc_send_redirect(skb, target);
+               rcu_read_unlock();
+       } else {
+diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
+index f867ec8d3d905..fe36b3f512850 100644
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1707,6 +1707,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
+       }
+       peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
++      if (!peer)
++              goto release;
+       ret = inet_peer_xrlim_allow(peer, 1*HZ);
+       if (!ret)
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
new file mode 100644 (file)
index 0000000..9d95278
--- /dev/null
@@ -0,0 +1,53 @@
+From 279e8507cf6d5ca3ac5617a7dcef628cb0ac3243 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:14 +0100
+Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ]
+
+bus->read() returns a negative errno on failure, but
+mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT
+into 0xff92, and returns success. The garbage word is then consumed as
+register data, and read-modify-write cycles write it back to the
+switch. Check both reads and propagate their errors.
+
+The same defect existed in mt7530_mii_read() since the driver was
+introduced and moved into the regmap backend unchanged.
+
+Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530-mdio.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c
+index 11ea924a9f357..784dd58a71589 100644
+--- a/drivers/net/dsa/mt7530-mdio.c
++++ b/drivers/net/dsa/mt7530-mdio.c
+@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
+       if (ret < 0)
+               return ret;
+-      lo = bus->read(bus, priv->mdiodev->addr, r);
+-      hi = bus->read(bus, priv->mdiodev->addr, 0x10);
++      ret = bus->read(bus, priv->mdiodev->addr, r);
++      if (ret < 0)
++              return ret;
++      lo = ret;
++
++      ret = bus->read(bus, priv->mdiodev->addr, 0x10);
++      if (ret < 0)
++              return ret;
++      hi = ret;
+       *val = (hi << 16) | (lo & 0xffff);
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch
new file mode 100644 (file)
index 0000000..bacca5b
--- /dev/null
@@ -0,0 +1,104 @@
+From 8a7afb22ba457cf399ecca41c6a194610e24d6f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:21 +0100
+Subject: net: dsa: mt7530: error out on failed reads in ATC/VTCR command
+ polling
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit ed9adac35b8fac635f40e28461505e2e5b6c8fcc ]
+
+mt7530_fdb_cmd() and mt7530_vlan_cmd() poll the command register
+through a helper which returns 0 when the underlying read fails. A
+failed bus transaction thus clears ATC_BUSY/VTCR_BUSY and is treated
+as successful command completion, and the subsequent ATC_INVALID and
+VTCR_INVALID checks are defeated the same way.
+
+Poll using regmap_read_poll_timeout(), which stops on read errors and
+propagates them, and check the completion status read as well. Take
+the MDIO bus lock across the sequence as the switch regmap is set up
+with locking disabled.
+
+Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
+Fixes: 83163f7dca56 ("net: dsa: mediatek: add VLAN support for MT7530")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/eea1d8f15c54375b3770c23e09fb3217df487169.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index 3c2a3029b10cf..292cde961f1af 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -248,15 +248,20 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
+ {
+       u32 val;
+       int ret;
+-      struct mt7530_dummy_poll p;
+       /* Set the command operating upon the MAC address entries */
+       val = ATC_BUSY | ATC_MAT(0) | cmd;
+       mt7530_write(priv, MT7530_ATC, val);
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC);
+-      ret = readx_poll_timeout(_mt7530_read, &p, val,
+-                               !(val & ATC_BUSY), 20, 20000);
++      mt7530_mutex_lock(priv);
++
++      ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val,
++                                     !(val & ATC_BUSY), 20, 20000);
++      if (!ret)
++              ret = regmap_read(priv->regmap, MT7530_ATC, &val);
++
++      mt7530_mutex_unlock(priv);
++
+       if (ret < 0) {
+               dev_err(priv->dev, "reset timeout\n");
+               return ret;
+@@ -265,7 +270,6 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
+       /* Additional sanity for read command if the specified
+        * entry is invalid
+        */
+-      val = mt7530_read(priv, MT7530_ATC);
+       if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID))
+               return -EINVAL;
+@@ -1626,22 +1630,26 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ static int
+ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 val;
+       int ret;
+       val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
+       mt7530_write(priv, MT7530_VTCR, val);
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
+-      ret = readx_poll_timeout(_mt7530_read, &p, val,
+-                               !(val & VTCR_BUSY), 20, 20000);
++      mt7530_mutex_lock(priv);
++
++      ret = regmap_read_poll_timeout(priv->regmap, MT7530_VTCR, val,
++                                     !(val & VTCR_BUSY), 20, 20000);
++      if (!ret)
++              ret = regmap_read(priv->regmap, MT7530_VTCR, &val);
++
++      mt7530_mutex_unlock(priv);
++
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               return ret;
+       }
+-      val = mt7530_read(priv, MT7530_VTCR);
+       if (val & VTCR_INVALID) {
+               dev_err(priv->dev, "read VTCR invalid\n");
+               return -EINVAL;
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
new file mode 100644 (file)
index 0000000..66b29a6
--- /dev/null
@@ -0,0 +1,191 @@
+From e6df9521a62ccf179d4d3f0d3684b475e9d8cf95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 05:52:29 +0100
+Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ]
+
+The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through
+a helper which returns 0 when the underlying read fails, so a failed
+bus transaction clears MT7531_PHY_ACS_ST and the access carries on,
+returning garbage PHY register data to phylib.
+
+Poll using regmap_read_poll_timeout(), which stops on read errors and
+propagates them. These functions hold the MDIO bus lock across the
+whole sequence, so the unlocked regmap accesses remain correct. Remove
+the now-unused _mt7530_unlocked_read().
+
+Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 58 ++++++++++++++--------------------------
+ 1 file changed, 20 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index 292cde961f1af..aa33d94e11b5f 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -191,12 +191,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+       mt7530_mutex_unlock(priv);
+ }
+-static u32
+-_mt7530_unlocked_read(struct mt7530_dummy_poll *p)
+-{
+-      return mt7530_mii_read(p->priv, p->reg);
+-}
+-
+ static u32
+ _mt7530_read(struct mt7530_dummy_poll *p)
+ {
+@@ -553,16 +547,13 @@ static int
+ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+                       int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 reg, val;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -572,8 +563,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -583,8 +574,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad);
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -601,16 +592,13 @@ static int
+ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+                        int regnum, u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       u32 val, reg;
+       int ret;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -620,8 +608,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | regnum;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -631,8 +619,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+             MT7531_MDIO_DEV_ADDR(devad) | data;
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -647,16 +635,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+ static int
+ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 val;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -667,8 +652,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+       mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
+-                               !(val & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
++                                     !(val & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -685,16 +670,13 @@ static int
+ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+                        u16 data)
+ {
+-      struct mt7530_dummy_poll p;
+       int ret;
+       u32 reg;
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
+-
+       mt7530_mutex_lock(priv);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+@@ -705,8 +687,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+       mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+-      ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
+-                               !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++      ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
++                                     !(reg & MT7531_PHY_ACS_ST), 20, 100000);
+       if (ret < 0) {
+               dev_err(priv->dev, "poll timeout\n");
+               goto out;
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
new file mode 100644 (file)
index 0000000..c755e4c
--- /dev/null
@@ -0,0 +1,40 @@
+From 212d8f91595abb774fe22c749ac838eeb5777ed4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 13:57:35 +0800
+Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in
+ poll_controller
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ]
+
+mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq
+cookie), but mtk_poll_controller incorrectly passed the net_device *.
+Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled
+would then crash.
+
+Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 5d291e50a47bb..351444fb48716 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3467,7 +3467,7 @@ static void mtk_poll_controller(struct net_device *dev)
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+-      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev);
++      mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth);
+       mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
new file mode 100644 (file)
index 0000000..00a786d
--- /dev/null
@@ -0,0 +1,48 @@
+From 3041b203a27af72da213522eb1df9196babb0f4e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:46:57 +0800
+Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets
+
+From: Jiawen Wu <jiawenwu@trustnetic.com>
+
+[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ]
+
+When TX VLAN hardware offload is disabled, VLAN tags are embedded in
+the packet payload (software VLAN). Previously, the driver failed to
+set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission.
+
+This missing flag caused the txgbe FDIR ATR logic to fall through to the
+default hash calculation path. This resulted in asymmetric hash values
+for Tx and Rx flows, preventing return packets from being steered to the
+same queue as the transmit packets.
+
+Fix this by detecting software VLANs via eth_type_vlan(skb->protocol)
+and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects
+the correct hashing algorithm to maintain Tx/Rx queue symmetry.
+
+Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support")
+Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+index 5c2cd5756f8a9..a553365b69943 100644
+--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+@@ -1606,6 +1606,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
+       if (skb_vlan_tag_present(skb)) {
+               tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT;
+               tx_flags |= WX_TX_FLAGS_HW_VLAN;
++      } else if (eth_type_vlan(skb->protocol)) {
++              tx_flags |= WX_TX_FLAGS_SW_VLAN;
+       }
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch b/queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch
new file mode 100644 (file)
index 0000000..6aa7d62
--- /dev/null
@@ -0,0 +1,763 @@
+From 49271e7baabbdf4f4b581ad306738f0f2a50bdaa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Jun 2026 17:57:10 -0700
+Subject: net: mana: Create separate EQs for each vPort
+
+From: Long Li <longli@microsoft.com>
+
+[ Upstream commit fa1a3b7bcd161028e038025c1a4a8963b2f56a95 ]
+
+To prepare for assigning vPorts to dedicated MSI-X vectors, remove EQ
+sharing among the vPorts and create dedicated EQs for each vPort.
+
+Move the EQ definition from struct mana_context to struct mana_port_context
+and update related support functions. Export mana_create_eq() and
+mana_destroy_eq() for use by the MANA RDMA driver.
+
+RSS QPs now take a vport reference via pd->vport_use_count to ensure
+EQs outlive all QP consumers. The vport must already be configured by
+a raw QP before an RSS QP can be created. EQs are only destroyed when
+the last QP (raw or RSS) on the PD releases its reference.
+
+Restrict each vport to a single RSS QP. The hardware only supports one
+steering configuration (indirection table / hash key) per vport, and
+mana_disable_vport_rx() on QP destroy disables RX globally for the
+vport. Previously, creating a second RSS QP would silently overwrite
+the first QP's steering config and destroy would blackhole all traffic.
+This is now explicitly rejected with -EBUSY. Existing applications
+(DPDK being the primary RDMA consumer) always create one RSS QP per
+vport, so no real-world flows are affected.
+
+Reject cross-port PD sharing for both raw and RSS QPs. Since EQs and
+vport configuration are per-port, a PD is bound to the port used by
+its first raw QP. Subsequent QPs on the same PD must use the same
+port or the creation fails with -EINVAL. Previously this was silently
+broken: with shared EQs it appeared to work, but with per-vPort EQs
+a cross-port PD would cause wrong-port EQ teardown and corruption.
+DPDK creates one PD per port so no existing flows are affected.
+
+Serialize mana_set_channels() and the async per-port queue reset
+handler against RDMA vport configuration to prevent RDMA from claiming
+the vport during the detach/attach window. A channel_changing flag is
+set under apc->vport_mutex before detach and checked by
+mana_cfg_vport() when called from the RDMA path, blocking RDMA from
+grabbing the vport during the entire window. When the port is down
+and RDMA already holds the vport, the channel change is rejected with
+-EBUSY.
+
+Signed-off-by: Long Li <longli@microsoft.com>
+Link: https://patch.msgid.link/20260605005717.2059954-2-longli@microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e67cc80b50f5 ("net: mana: Return error code from mana_create_rxq()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/hw/mana/main.c             |  40 ++++--
+ drivers/infiniband/hw/mana/mana_ib.h          |  14 ++
+ drivers/infiniband/hw/mana/qp.c               |  68 ++++++++-
+ drivers/net/ethernet/microsoft/mana/mana_en.c | 135 +++++++++++-------
+ .../ethernet/microsoft/mana/mana_ethtool.c    |  23 ++-
+ include/net/mana/mana.h                       |  15 +-
+ 6 files changed, 228 insertions(+), 67 deletions(-)
+
+diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c
+index 307ae01bf26f3..0e1727c53d5d2 100644
+--- a/drivers/infiniband/hw/mana/main.c
++++ b/drivers/infiniband/hw/mana/main.c
+@@ -20,8 +20,10 @@ void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd,
+       pd->vport_use_count--;
+       WARN_ON(pd->vport_use_count < 0);
+-      if (!pd->vport_use_count)
++      if (!pd->vport_use_count) {
++              mana_destroy_eq(mpc);
+               mana_uncfg_vport(mpc);
++      }
+       mutex_unlock(&pd->vport_mutex);
+ }
+@@ -40,13 +42,27 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd,
+       pd->vport_use_count++;
+       if (pd->vport_use_count > 1) {
++              /* Reject cross-port PD sharing. EQs and vport config
++               * are per-port, so the PD must stay bound to the port
++               * that was configured on the first raw QP creation.
++               */
++              if (pd->vport_port != port) {
++                      pd->vport_use_count--;
++                      mutex_unlock(&pd->vport_mutex);
++                      ibdev_dbg(&dev->ib_dev,
++                                "PD already bound to port %u\n",
++                                pd->vport_port);
++                      return -EINVAL;
++              }
+               ibdev_dbg(&dev->ib_dev,
+                         "Skip as this PD is already configured vport\n");
+               mutex_unlock(&pd->vport_mutex);
+               return 0;
+       }
+-      err = mana_cfg_vport(mpc, pd->pdn, doorbell_id);
++      pd->vport_port = port;
++
++      err = mana_cfg_vport(mpc, pd->pdn, doorbell_id, true);
+       if (err) {
+               pd->vport_use_count--;
+               mutex_unlock(&pd->vport_mutex);
+@@ -55,15 +71,23 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd,
+               return err;
+       }
+-      mutex_unlock(&pd->vport_mutex);
+-      pd->tx_shortform_allowed = mpc->tx_shortform_allowed;
+-      pd->tx_vp_offset = mpc->tx_vp_offset;
++      err = mana_create_eq(mpc);
++      if (err) {
++              mana_uncfg_vport(mpc);
++              pd->vport_use_count--;
++      } else {
++              pd->tx_shortform_allowed = mpc->tx_shortform_allowed;
++              pd->tx_vp_offset = mpc->tx_vp_offset;
++      }
++
++      mutex_unlock(&pd->vport_mutex);
+-      ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n",
+-                mpc->port_handle, pd->pdn, doorbell_id);
++      if (!err)
++              ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n",
++                        mpc->port_handle, pd->pdn, doorbell_id);
+-      return 0;
++      return err;
+ }
+ int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
+diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h
+index c9c94e86a72bd..da05966aff191 100644
+--- a/drivers/infiniband/hw/mana/mana_ib.h
++++ b/drivers/infiniband/hw/mana/mana_ib.h
+@@ -102,6 +102,20 @@ struct mana_ib_pd {
+       struct mutex vport_mutex;
+       int vport_use_count;
++      /* Port bound to this PD for raw QP usage. Only valid when
++       * vport_use_count > 0. A PD can only be associated with a
++       * single physical port because per-port EQs and vport
++       * configuration are tied to the PD's refcount.
++       */
++      u32 vport_port;
++
++      /* Only one RSS QP is allowed per vport because each RSS QP
++       * overwrites the vport steering config (indirection table /
++       * hash key) and mana_disable_vport_rx() on destroy would
++       * blackhole traffic for any other RSS QP on the same vport.
++       */
++      bool has_rss_qp;
++
+       bool tx_shortform_allowed;
+       u32 tx_vp_offset;
+ };
+diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c
+index 0fbcf449c134b..d3ee30b64f53a 100644
+--- a/drivers/infiniband/hw/mana/qp.c
++++ b/drivers/infiniband/hw/mana/qp.c
+@@ -79,6 +79,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+                                struct ib_qp_init_attr *attr,
+                                struct ib_udata *udata)
+ {
++      struct mana_ib_pd *mana_pd = container_of(pd, struct mana_ib_pd, ibpd);
+       struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
+       struct mana_ib_dev *mdev =
+               container_of(pd->device, struct mana_ib_dev, ib_dev);
+@@ -155,6 +156,30 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+       qp->port = port;
++      /* Take a reference on the vport to ensure EQs outlive this QP.
++       * The vport must already be configured by a raw QP on the
++       * same port — cross-port PD sharing is not supported.
++       * Only one RSS QP per vport is allowed because each one
++       * overwrites the steering config and destroy disables RX
++       * globally.
++       */
++      mutex_lock(&mana_pd->vport_mutex);
++      if (!mana_pd->vport_use_count || mana_pd->vport_port != port) {
++              mutex_unlock(&mana_pd->vport_mutex);
++              ret = -EINVAL;
++              goto fail;
++      }
++      if (mana_pd->has_rss_qp) {
++              mutex_unlock(&mana_pd->vport_mutex);
++              ibdev_dbg(&mdev->ib_dev,
++                        "Only one RSS QP per vport is supported\n");
++              ret = -EBUSY;
++              goto fail;
++      }
++      mana_pd->vport_use_count++;
++      mana_pd->has_rss_qp = true;
++      mutex_unlock(&mana_pd->vport_mutex);
++
+       for (i = 0; i < ind_tbl_size; i++) {
+               struct mana_obj_spec wq_spec = {};
+               struct mana_obj_spec cq_spec = {};
+@@ -171,13 +196,19 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+               cq_spec.gdma_region = cq->queue.gdma_region;
+               cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE;
+               cq_spec.modr_ctx_id = 0;
+-              eq = &mpc->ac->eqs[cq->comp_vector];
++              /* Map comp_vector to a per-vPort EQ. The modulo handles
++               * the case where the RDMA-advertised num_comp_vectors
++               * exceeds this port's num_queues (e.g. after ethtool -L
++               * reduces it), remapping to an available EQ rather than
++               * failing the QP creation.
++               */
++              eq = &mpc->eqs[cq->comp_vector % mpc->num_queues];
+               cq_spec.attached_eq = eq->eq->id;
+               ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ,
+                                        &wq_spec, &cq_spec, &wq->rx_object);
+               if (ret)
+-                      goto fail;
++                      goto free_vport;
+               /* The GDMA regions are now owned by the WQ object */
+               wq->queue.gdma_region = GDMA_INVALID_DMA_REGION;
+@@ -199,7 +230,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+               ret = mana_ib_install_cq_cb(mdev, cq);
+               if (ret) {
+                       mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
+-                      goto fail;
++                      goto free_vport;
+               }
+       }
+       resp.num_entries = i;
+@@ -210,7 +241,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+                                        ucmd.rx_hash_key_len,
+                                        ucmd.rx_hash_key);
+       if (ret)
+-              goto fail;
++              goto free_vport;
+       ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
+       if (ret) {
+@@ -226,7 +257,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+ err_disable_vport_rx:
+       mana_disable_vport_rx(mpc);
+-fail:
++free_vport:
+       while (i-- > 0) {
+               ibwq = ind_tbl->ind_tbl[i];
+               ibcq = ibwq->cq;
+@@ -237,6 +268,13 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
+               mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
+       }
++      mutex_lock(&mana_pd->vport_mutex);
++      mana_pd->has_rss_qp = false;
++      mutex_unlock(&mana_pd->vport_mutex);
++
++      mana_ib_uncfg_vport(mdev, mana_pd, port);
++
++fail:
+       kfree(mana_ind_table);
+       return ret;
+@@ -299,7 +337,7 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
+       err = mana_ib_cfg_vport(mdev, port, pd, mana_ucontext->doorbell);
+       if (err)
+-              return -ENODEV;
++              return err;
+       qp->port = port;
+@@ -321,7 +359,14 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
+       cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE;
+       cq_spec.modr_ctx_id = 0;
+       eq_vec = send_cq->comp_vector;
+-      eq = &mpc->ac->eqs[eq_vec];
++      if (!mpc->eqs) {
++              err = -EINVAL;
++              goto err_destroy_queue;
++      }
++      /* Map comp_vector to a per-vPort EQ. See comment in
++       * mana_ib_create_qp_rss() for the modulo rationale.
++       */
++      eq = &mpc->eqs[eq_vec % mpc->num_queues];
+       cq_spec.attached_eq = eq->eq->id;
+       err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec,
+@@ -785,14 +830,17 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp,
+ {
+       struct mana_ib_dev *mdev =
+               container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
++      struct ib_pd *ibpd = qp->ibqp.pd;
+       struct mana_port_context *mpc;
+       struct net_device *ndev;
++      struct mana_ib_pd *pd;
+       struct mana_ib_wq *wq;
+       struct ib_wq *ibwq;
+       int i;
+       ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port);
+       mpc = netdev_priv(ndev);
++      pd = container_of(ibpd, struct mana_ib_pd, ibpd);
+       /* Disable vPort RX steering before destroying RX WQ objects.
+        * Otherwise firmware still routes traffic to the destroyed queues,
+@@ -817,6 +865,12 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp,
+               mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
+       }
++      mutex_lock(&pd->vport_mutex);
++      pd->has_rss_qp = false;
++      mutex_unlock(&pd->vport_mutex);
++
++      mana_ib_uncfg_vport(mdev, pd, qp->port);
++
+       return 0;
+ }
+diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
+index 352398ae0376e..9d8c91d36b3bc 100644
+--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
+@@ -309,11 +309,18 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work)
+       rtnl_lock();
++      /* Block RDMA from grabbing the vport during the detach/attach
++       * window, same as mana_set_channels().
++       */
++      mutex_lock(&apc->vport_mutex);
++      apc->channel_changing = true;
++      mutex_unlock(&apc->vport_mutex);
++
+       /* Pre-allocate buffers to prevent failure in mana_attach later */
+       err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
+       if (err) {
+               netdev_err(ndev, "Insufficient memory for reset post tx stall detection\n");
+-              goto out;
++              goto clear_flag;
+       }
+       err = mana_detach(ndev, false);
+@@ -328,7 +335,11 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work)
+ dealloc_pre_rxbufs:
+       mana_pre_dealloc_rxbufs(apc);
+-out:
++clear_flag:
++      mutex_lock(&apc->vport_mutex);
++      apc->channel_changing = false;
++      mutex_unlock(&apc->vport_mutex);
++
+       rtnl_unlock();
+ }
+@@ -1301,7 +1312,7 @@ void mana_uncfg_vport(struct mana_port_context *apc)
+ EXPORT_SYMBOL_NS(mana_uncfg_vport, "NET_MANA");
+ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
+-                 u32 doorbell_pg_id)
++                 u32 doorbell_pg_id, bool check_channel_changing)
+ {
+       struct mana_config_vport_resp resp = {};
+       struct mana_config_vport_req req = {};
+@@ -1326,7 +1337,8 @@ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
+        * Ethernet usage on the same port.
+        */
+       mutex_lock(&apc->vport_mutex);
+-      if (apc->vport_use_count > 0) {
++      if (apc->vport_use_count > 0 ||
++          (check_channel_changing && apc->channel_changing)) {
+               mutex_unlock(&apc->vport_mutex);
+               return -EBUSY;
+       }
+@@ -1621,78 +1633,84 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
+ }
+ EXPORT_SYMBOL_NS(mana_destroy_wq_obj, "NET_MANA");
+-static void mana_destroy_eq(struct mana_context *ac)
++void mana_destroy_eq(struct mana_port_context *apc)
+ {
++      struct mana_context *ac = apc->ac;
+       struct gdma_context *gc = ac->gdma_dev->gdma_context;
+       struct gdma_queue *eq;
+       int i;
+-      if (!ac->eqs)
++      if (!apc->eqs)
+               return;
+-      debugfs_remove_recursive(ac->mana_eqs_debugfs);
+-      ac->mana_eqs_debugfs = NULL;
++      debugfs_remove_recursive(apc->mana_eqs_debugfs);
++      apc->mana_eqs_debugfs = NULL;
+-      for (i = 0; i < gc->max_num_queues; i++) {
+-              eq = ac->eqs[i].eq;
++      for (i = 0; i < apc->num_queues; i++) {
++              eq = apc->eqs[i].eq;
+               if (!eq)
+                       continue;
+               mana_gd_destroy_queue(gc, eq);
+       }
+-      kfree(ac->eqs);
+-      ac->eqs = NULL;
++      kfree(apc->eqs);
++      apc->eqs = NULL;
+ }
++EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA");
+-static void mana_create_eq_debugfs(struct mana_context *ac, int i)
++static void mana_create_eq_debugfs(struct mana_port_context *apc, int i)
+ {
+-      struct mana_eq eq = ac->eqs[i];
++      struct mana_eq eq = apc->eqs[i];
+       char eqnum[32];
+       sprintf(eqnum, "eq%d", i);
+-      eq.mana_eq_debugfs = debugfs_create_dir(eqnum, ac->mana_eqs_debugfs);
++      eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs);
+       debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head);
+       debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail);
+       debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops);
+ }
+-static int mana_create_eq(struct mana_context *ac)
++int mana_create_eq(struct mana_port_context *apc)
+ {
+-      struct gdma_dev *gd = ac->gdma_dev;
++      struct gdma_dev *gd = apc->ac->gdma_dev;
+       struct gdma_context *gc = gd->gdma_context;
+       struct gdma_queue_spec spec = {};
+       int err;
+       int i;
+-      ac->eqs = kzalloc_objs(struct mana_eq, gc->max_num_queues);
+-      if (!ac->eqs)
++      if (WARN_ON(apc->eqs))
++              return -EEXIST;
++      apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues);
++      if (!apc->eqs)
+               return -ENOMEM;
+       spec.type = GDMA_EQ;
+       spec.monitor_avl_buf = false;
+       spec.queue_size = EQ_SIZE;
+       spec.eq.callback = NULL;
+-      spec.eq.context = ac->eqs;
++      spec.eq.context = apc->eqs;
+       spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
+-      ac->mana_eqs_debugfs = debugfs_create_dir("EQs", gc->mana_pci_debugfs);
++      apc->mana_eqs_debugfs =
++              debugfs_create_dir("EQs", apc->mana_port_debugfs);
+-      for (i = 0; i < gc->max_num_queues; i++) {
++      for (i = 0; i < apc->num_queues; i++) {
+               spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
+-              err = mana_gd_create_mana_eq(gd, &spec, &ac->eqs[i].eq);
++              err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq);
+               if (err) {
+                       dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err);
+                       goto out;
+               }
+-              mana_create_eq_debugfs(ac, i);
++              mana_create_eq_debugfs(apc, i);
+       }
+       return 0;
+ out:
+-      mana_destroy_eq(ac);
++      mana_destroy_eq(apc);
+       return err;
+ }
++EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA");
+ static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq)
+ {
+@@ -2484,7 +2502,7 @@ static int mana_create_txq(struct mana_port_context *apc,
+               spec.monitor_avl_buf = false;
+               spec.queue_size = cq_size;
+               spec.cq.callback = mana_schedule_napi;
+-              spec.cq.parent_eq = ac->eqs[i].eq;
++              spec.cq.parent_eq = apc->eqs[i].eq;
+               spec.cq.context = cq;
+               err = mana_gd_create_mana_wq_cq(gd, &spec, &cq->gdma_cq);
+               if (err)
+@@ -2882,13 +2900,12 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
+ static int mana_add_rx_queues(struct mana_port_context *apc,
+                             struct net_device *ndev)
+ {
+-      struct mana_context *ac = apc->ac;
+       struct mana_rxq *rxq;
+       int err = 0;
+       int i;
+       for (i = 0; i < apc->num_queues; i++) {
+-              rxq = mana_create_rxq(apc, i, &ac->eqs[i], ndev);
++              rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
+               if (!rxq) {
+                       err = -ENOMEM;
+                       netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
+@@ -2907,9 +2924,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
+       return err;
+ }
+-static void mana_destroy_vport(struct mana_port_context *apc)
++static void mana_destroy_rxqs(struct mana_port_context *apc)
+ {
+-      struct gdma_dev *gd = apc->ac->gdma_dev;
+       struct mana_rxq *rxq;
+       u32 rxq_idx;
+@@ -2924,8 +2940,12 @@ static void mana_destroy_vport(struct mana_port_context *apc)
+                       apc->rxqs[rxq_idx] = NULL;
+               }
+       }
++}
++
++static void mana_destroy_vport(struct mana_port_context *apc)
++{
++      struct gdma_dev *gd = apc->ac->gdma_dev;
+-      mana_destroy_txq(apc);
+       mana_uncfg_vport(apc);
+       if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
+@@ -2946,11 +2966,14 @@ static int mana_create_vport(struct mana_port_context *apc,
+                       return err;
+       }
+-      err = mana_cfg_vport(apc, gd->pdid, gd->doorbell);
+-      if (err)
++      err = mana_cfg_vport(apc, gd->pdid, gd->doorbell, false);
++      if (err) {
++              if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
++                      mana_pf_deregister_hw_vport(apc);
+               return err;
++      }
+-      return mana_create_txq(apc, net);
++      return 0;
+ }
+ static int mana_rss_table_alloc(struct mana_port_context *apc)
+@@ -3236,21 +3259,36 @@ int mana_alloc_queues(struct net_device *ndev)
+       err = mana_create_vport(apc, ndev);
+       if (err) {
+-              netdev_err(ndev, "Failed to create vPort %u : %d\n", apc->port_idx, err);
++              netdev_err(ndev, "Failed to create vPort %u : %d\n",
++                         apc->port_idx, err);
+               return err;
+       }
++      err = mana_create_eq(apc);
++      if (err) {
++              netdev_err(ndev, "Failed to create EQ on vPort %u: %d\n",
++                         apc->port_idx, err);
++              goto destroy_vport;
++      }
++
++      err = mana_create_txq(apc, ndev);
++      if (err) {
++              netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n",
++                         apc->port_idx, err);
++              goto destroy_eq;
++      }
++
+       err = netif_set_real_num_tx_queues(ndev, apc->num_queues);
+       if (err) {
+               netdev_err(ndev,
+                          "netif_set_real_num_tx_queues () failed for ndev with num_queues %u : %d\n",
+                          apc->num_queues, err);
+-              goto destroy_vport;
++              goto destroy_txq;
+       }
+       err = mana_add_rx_queues(apc, ndev);
+       if (err)
+-              goto destroy_vport;
++              goto destroy_rxq;
+       apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
+@@ -3259,7 +3297,7 @@ int mana_alloc_queues(struct net_device *ndev)
+               netdev_err(ndev,
+                          "netif_set_real_num_rx_queues () failed for ndev with num_queues %u : %d\n",
+                          apc->num_queues, err);
+-              goto destroy_vport;
++              goto destroy_rxq;
+       }
+       mana_rss_table_init(apc);
+@@ -3267,19 +3305,25 @@ int mana_alloc_queues(struct net_device *ndev)
+       err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
+       if (err) {
+               netdev_err(ndev, "Failed to configure RSS table: %d\n", err);
+-              goto destroy_vport;
++              goto destroy_rxq;
+       }
+       if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) {
+               err = mana_pf_register_filter(apc);
+               if (err)
+-                      goto destroy_vport;
++                      goto destroy_rxq;
+       }
+       mana_chn_setxdp(apc, mana_xdp_get(apc));
+       return 0;
++destroy_rxq:
++      mana_destroy_rxqs(apc);
++destroy_txq:
++      mana_destroy_txq(apc);
++destroy_eq:
++      mana_destroy_eq(apc);
+ destroy_vport:
+       mana_destroy_vport(apc);
+       return err;
+@@ -3390,6 +3434,9 @@ static int mana_dealloc_queues(struct net_device *ndev)
+       mana_fence_rqs(apc);
+       /* Even in err case, still need to cleanup the vPort */
++      mana_destroy_rxqs(apc);
++      mana_destroy_txq(apc);
++      mana_destroy_eq(apc);
+       mana_destroy_vport(apc);
+       return 0;
+@@ -3716,12 +3763,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
+       INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
+-      err = mana_create_eq(ac);
+-      if (err) {
+-              dev_err(dev, "Failed to create EQs: %d\n", err);
+-              goto out;
+-      }
+-
+       err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
+                                   MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
+       if (err)
+@@ -3861,8 +3902,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
+               free_netdev(ndev);
+       }
+-      mana_destroy_eq(ac);
+-
+       if (ac->per_port_queue_reset_wq) {
+               destroy_workqueue(ac->per_port_queue_reset_wq);
+               ac->per_port_queue_reset_wq = NULL;
+diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+index 6a4b42fe09445..a9440c4b8825b 100644
+--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
++++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+@@ -454,6 +454,11 @@ static int mana_set_coalesce(struct net_device *ndev,
+       return err;
+ }
++/* mana_set_channels - change the number of queues on a port
++ *
++ * Returns -EBUSY if RDMA holds the vport with EQs sized to the
++ * current num_queues.
++ */
+ static int mana_set_channels(struct net_device *ndev,
+                            struct ethtool_channels *channels)
+ {
+@@ -462,10 +467,22 @@ static int mana_set_channels(struct net_device *ndev,
+       unsigned int old_count = apc->num_queues;
+       int err;
++      /* Set channel_changing to block RDMA from grabbing the vport
++       * during the detach/attach window. mana_cfg_vport() checks
++       * this flag under vport_mutex and returns -EBUSY if set.
++       */
++      mutex_lock(&apc->vport_mutex);
++      if (!apc->port_is_up && apc->vport_use_count) {
++              mutex_unlock(&apc->vport_mutex);
++              return -EBUSY;
++      }
++      apc->channel_changing = true;
++      mutex_unlock(&apc->vport_mutex);
++
+       err = mana_pre_alloc_rxbufs(apc, ndev->mtu, new_count);
+       if (err) {
+               netdev_err(ndev, "Insufficient memory for new allocations");
+-              return err;
++              goto clear_flag;
+       }
+       err = mana_detach(ndev, false);
+@@ -483,6 +500,10 @@ static int mana_set_channels(struct net_device *ndev,
+ out:
+       mana_pre_dealloc_rxbufs(apc);
++clear_flag:
++      mutex_lock(&apc->vport_mutex);
++      apc->channel_changing = false;
++      mutex_unlock(&apc->vport_mutex);
+       return err;
+ }
+diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
+index 4111b93169d2f..f8e05456888e4 100644
+--- a/include/net/mana/mana.h
++++ b/include/net/mana/mana.h
+@@ -488,8 +488,6 @@ struct mana_context {
+       u8 bm_hostmode;
+       struct mana_ethtool_hc_stats hc_stats;
+-      struct mana_eq *eqs;
+-      struct dentry *mana_eqs_debugfs;
+       struct workqueue_struct *per_port_queue_reset_wq;
+       /* Workqueue for querying hardware stats */
+       struct delayed_work gf_stats_work;
+@@ -509,6 +507,9 @@ struct mana_port_context {
+       u8 mac_addr[ETH_ALEN];
++      struct mana_eq *eqs;
++      struct dentry *mana_eqs_debugfs;
++
+       enum TRI_STATE rss_state;
+       mana_handle_t default_rxobj;
+@@ -555,6 +556,12 @@ struct mana_port_context {
+       struct mutex vport_mutex;
+       int vport_use_count;
++      /* Set by mana_set_channels() under vport_mutex to block RDMA
++       * from grabbing the vport during the detach/attach window.
++       * Checked by mana_cfg_vport() when called from the RDMA path.
++       */
++      bool channel_changing;
++
+       /* Net shaper handle*/
+       struct net_shaper_handle handle;
+@@ -1040,8 +1047,10 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
+                        mana_handle_t wq_obj);
+ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
+-                 u32 doorbell_pg_id);
++                 u32 doorbell_pg_id, bool check_channel_changing);
+ void mana_uncfg_vport(struct mana_port_context *apc);
++int mana_create_eq(struct mana_port_context *apc);
++void mana_destroy_eq(struct mana_port_context *apc);
+ struct net_device *mana_get_primary_netdev(struct mana_context *ac,
+                                          u32 port_index,
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch b/queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch
new file mode 100644 (file)
index 0000000..9e3e8d2
--- /dev/null
@@ -0,0 +1,66 @@
+From 62455be4b0fb6ff972e7b9be1ca107ac193f762a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 04:37:59 -0700
+Subject: net: mana: Return error code from mana_create_rxq()
+
+From: Aditya Garg <gargaditya@linux.microsoft.com>
+
+[ Upstream commit e67cc80b50f587cd1d8ffc8989dcec3291720bc3 ]
+
+mana_create_rxq() returns a struct mana_rxq pointer and returns NULL on
+any failure. The caller, mana_add_rx_queues(), cannot tell what went
+wrong and hardcodes the error as -ENOMEM. As a result the actual failure
+reported by the lower layers (for example -EPROTO from a failed HW
+request) is masked and every RX queue creation failure looks like an
+out-of-memory error.
+
+Return an ERR_PTR() encoded error code from mana_create_rxq() on failure
+instead of NULL. The caller now propagates the returned error code
+directly instead of substituting -ENOMEM.
+
+Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)")
+Signed-off-by: Aditya Garg <gargaditya@linux.microsoft.com>
+Reviewed-by: Joe Damato <joe@dama.to>
+Link: https://patch.msgid.link/20260727113759.2881500-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/microsoft/mana/mana_en.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
+index 9d8c91d36b3bc..7e6bc6e5ff232 100644
+--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
+@@ -2771,7 +2771,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
+       rxq = kzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size);
+       if (!rxq)
+-              return NULL;
++              return ERR_PTR(-ENOMEM);
+       rxq->ndev = ndev;
+       rxq->num_rx_buf = apc->rx_queue_size;
+@@ -2872,7 +2872,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
+       mana_destroy_rxq(apc, rxq, false);
+-      return NULL;
++      return ERR_PTR(err);
+ }
+ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
+@@ -2906,8 +2906,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
+       for (i = 0; i < apc->num_queues; i++) {
+               rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
+-              if (!rxq) {
+-                      err = -ENOMEM;
++              if (IS_ERR(rxq)) {
++                      err = PTR_ERR(rxq);
+                       netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
+                       goto out;
+               }
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch
new file mode 100644 (file)
index 0000000..5d5dcc3
--- /dev/null
@@ -0,0 +1,92 @@
+From 91bde6ea8b2feacb21ee2d7681a0ee5cb5091ed8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:08:05 +0200
+Subject: net: phylink: put link_gpio if phylink_create fails
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
+
+In phylink_create() if phylink_register_sfp() returns an error, link_gpio
+obtained by phylink_parse_fixedlink() is never released. While this is a
+very unlikely scenario, it's worth to fix/handle this.
+
+This was present from the very first implementation of phylink but got
+relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to
+bridge between network devices and sfp cages") where additional function
+were added after phylink_parse_fixedlink() making the release of link_gpio
+needed if such additional function errored out.
+
+While at it, restructure the exit condition of phylink_create() with the
+goto pattern to reduce code duplication on handling error conditions.
+
+Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phylink.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 087ac63f9193d..18d2ead97aa54 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1875,8 +1875,8 @@ struct phylink *phylink_create(struct phylink_config *config,
+       } else if (config->type == PHYLINK_DEV) {
+               pl->dev = config->dev;
+       } else {
+-              kfree(pl);
+-              return ERR_PTR(-EINVAL);
++              ret = -EINVAL;
++              goto free_pl;
+       }
+       pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops);
+@@ -1909,28 +1909,29 @@ struct phylink *phylink_create(struct phylink_config *config,
+       phylink_validate(pl, pl->supported, &pl->link_config);
+       ret = phylink_parse_mode(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto free_pl;
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+               ret = phylink_parse_fixedlink(pl, fwnode);
+-              if (ret < 0) {
+-                      kfree(pl);
+-                      return ERR_PTR(ret);
+-              }
++              if (ret < 0)
++                      goto release_link_gpio;
+       }
+       pl->req_link_an_mode = pl->cfg_link_an_mode;
+       ret = phylink_register_sfp(pl, fwnode);
+-      if (ret < 0) {
+-              kfree(pl);
+-              return ERR_PTR(ret);
+-      }
++      if (ret < 0)
++              goto release_link_gpio;
+       return pl;
++
++release_link_gpio:
++      if (pl->link_gpio)
++              gpiod_put(pl->link_gpio);
++free_pl:
++      kfree(pl);
++      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch b/queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch
new file mode 100644 (file)
index 0000000..1957e8e
--- /dev/null
@@ -0,0 +1,58 @@
+From 6dea08400f790d131efd1917d1edb4f49f469523 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 00:49:55 -0400
+Subject: net/sched: cls_u32: validate offshift to prevent shift-out-of-bounds
+
+From: Cen Zhang (Microsoft) <blbllhy@gmail.com>
+
+[ Upstream commit aef96eead2860cbfa371e4471d4f04412213b958 ]
+
+u32_change() copies the user-provided tc_u32_sel.offshift (unsigned char,
+0-255) into the kernel knode object without bounds validation. When a
+packet later hits u32_classify() with TC_U32_VAROFFSET set, it evaluates
+`ntohs(offmask & *data) >> offshift` where the left operand is a 16-bit
+value promoted to a 32-bit int. Any offshift >= 32 is undefined behavior
+per C11 6.5.7p3, triggerable by an unprivileged user via user/network
+namespaces.
+
+UBSAN: shift-out-of-bounds in net/sched/cls_u32.c:236:43
+shift exponent 32 is too large for 32-bit type int
+
+Fix this by rejecting offshift >= 16 during filter creation in
+u32_change().
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Link: https://lore.kernel.org/all/20260720034514.23053-1-blbllhy@gmail.com
+Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
+Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Tested-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Tested-by: Victor Nogueira <victor@mojatatu.com>
+Link: https://patch.msgid.link/20260723044955.89471-1-blbllhy@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/cls_u32.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
+index 8f30cc82181d9..ac98b1c2144a7 100644
+--- a/net/sched/cls_u32.c
++++ b/net/sched/cls_u32.c
+@@ -1107,6 +1107,13 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
+               goto erridr;
+       }
++      if (s->offshift >= 16) {
++              NL_SET_ERR_MSG_MOD(extack,
++                                 "offshift must be less than 16");
++              err = -EINVAL;
++              goto erridr;
++      }
++
+       n = kzalloc_flex(*n, sel.keys, s->nkeys);
+       if (n == NULL) {
+               err = -ENOBUFS;
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch b/queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch
new file mode 100644 (file)
index 0000000..38dc16f
--- /dev/null
@@ -0,0 +1,87 @@
+From f5df8a781071580119e416fb33ff93b5e0c9affb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 23:14:52 +0200
+Subject: net/sched: sch_cake: skip clearing unused tins during rate adjustment
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jonas Köppeler <j.koeppeler@tu-berlin.de>
+
+[ Upstream commit a3729e0df005a936ceb3c2b0d167f01a2b03f970 ]
+
+When cake_configure_rates() is called from the dequeue path with
+rate_adjust=true, it only needs to update the rate parameters. The
+loop that clears the unused tins is both unnecessary and harmful in
+this path:
+
+ - cake_clear_tin() overwrites q->cur_tin and q->cur_flow, which are
+   actively used by cake_dequeue(), corrupting the dequeue state.
+ - iterating over the unused tins and their internal queues to purge
+   packets adds needless overhead to the hot path.
+
+Skip the entire loop when rate_adjust is set, as neither
+cake_clear_tin() nor the mtu_time update are needed when only the
+rate changes.
+
+The clearing loop runs on every rate adjustment from the dequeue path,
+clearing (max_tins - cur_tins) tins each time, so the cost grows the
+fewer tins the configured mode actually uses. Testing cake_mq over veth
+(8 rx/tx queues, 2 Gbit limit) with flent's [1] rrul and tcp_nup tests and
+32 TCP upstreams shows a large drop in loaded latency and a throughput
+gain, restoring behaviour to pre-15c2715a5264 levels:
+
+  +------------+------+------+-------+-------+---------+
+  | kernel     | mode | test |  base |  load |    tput |
+  |            |      |      |  (ms) |  (ms) |  (Mbit) |
+  +------------+------+------+-------+-------+---------+
+  | net-next   | be   | rrul | 0.810 | 11.78 | 1469.67 |
+  | net-next   | be   | nup  | 0.637 | 85.71 | 1243.15 |
+  | net-next   | ds3  | rrul | 0.397 | 15.28 | 1770.06 |
+  | net-next   | ds3  | nup  | 0.351 | 15.98 | 1799.39 |
+  +------------+------+------+-------+-------+---------+
+  | patched    | be   | rrul | 0.092 |  0.56 | 1873.40 |
+  | patched    | be   | nup  | 0.109 |  1.82 | 1869.12 |
+  | patched    | ds3  | rrul | 0.097 |  0.98 | 1866.10 |
+  | patched    | ds3  | nup  | 0.101 |  0.51 | 1861.79 |
+  +------------+------+------+-------+-------+---------+
+
+The same trend holds on real hardware (IPQ8074A, 4 rx/tx queues,
+OpenWrt): in besteffort mode the tcp_nup loaded latency drops from
+~470 ms to ~4 ms.
+
+[1] https://flent.org
+
+Fixes: 15c2715a5264 ("net/sched: sch_cake: fixup cake_mq rate adjustment for diffserv config")
+Signed-off-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
+Tested-by: Mike Pham <mikepham4321@gmail.com>
+Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>
+Link: https://patch.msgid.link/20260720-sch_cake-skip-clearing-tins-v2-1-e6a8b0275c73@tu-berlin.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/sch_cake.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
+index b967b7153ad34..8965fe252471c 100644
+--- a/net/sched/sch_cake.c
++++ b/net/sched/sch_cake.c
+@@ -2609,9 +2609,11 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust)
+               break;
+       }
+-      for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) {
+-              cake_clear_tin(sch, c);
+-              qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time;
++      if (!rate_adjust) {
++              for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) {
++                      cake_clear_tin(sch, c);
++                      qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time;
++              }
+       }
+       qd->rate_ns   = qd->tins[ft].tin_rate_ns;
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch b/queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch
new file mode 100644 (file)
index 0000000..3ccdcb6
--- /dev/null
@@ -0,0 +1,53 @@
+From fff9e4cfacc128b213abe7fb410327440ed12961 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 23:09:04 -0700
+Subject: net: stmmac: Fix E2E delay mechanism
+
+From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
+
+[ Upstream commit b041ed62aa6e3b2d7d36127e0e5d7bf2701f8231 ]
+
+For E2E delay mechanism, "received DELAY_REQ without timestamp" error
+messages show up for dwmac v3.70+ and dwxgmac IPs.
+
+This issue affects socfpga platforms, Agilex7 (dwmac 3.70) and
+Agilex5 (dwxgmac). According to the databook, to enable timestamping
+for all events, the SNAPTYPSEL bits in the MAC_Timestamp_Control
+register must be set to 2'b01, and the TSEVNTENA bit must be cleared
+to 0'b0.
+
+Commit 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism") already
+addresses this problem for all dwmacs above version v4.10. However,
+same holds true for v3.70 and above, as well as for dwxgmac. Updates
+the check accordingly.
+
+Fixes: 14f347334bf2 ("net: stmmac: Correctly take timestamp for PTPv2")
+Fixes: f2fb6b6275eb ("net: stmmac: enable timestamp snapshot for required PTP packets in dwmac v5.10a")
+Fixes: 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism")
+Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
+Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
+Link: https://patch.msgid.link/20260728060904.31993-1-muhammad.nazim.amirul.nazle.asmade@altera.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+index 755f48a34314b..f28367fbcaf81 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -755,7 +755,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
+                       config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+                       ptp_v2 = PTP_TCR_TSVER2ENA;
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+-                      if (priv->synopsys_id < DWMAC_CORE_4_10)
++                      if (priv->synopsys_id < DWMAC_CORE_3_70 &&
++                          priv->plat->core_type != DWMAC_CORE_XGMAC)
+                               ts_event_en = PTP_TCR_TSEVNTENA;
+                       ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
+                       ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch
new file mode 100644 (file)
index 0000000..35e2c1f
--- /dev/null
@@ -0,0 +1,48 @@
+From 505286c76c6a8230226c8fbc1fc17b60d24b1726 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:20 +0800
+Subject: net: sxgbe: check descriptor ring allocation failures
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
+
+sxgbe_open() ignores the return value of init_dma_desc_rings() and
+continues to program DMA with invalid ring addresses when allocation
+fails. Check the return value and disconnect the PHY on failure.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 9b48a587d5c2e..70cf3619555f9 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -1078,7 +1078,9 @@ static int sxgbe_open(struct net_device *dev)
+       priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+       priv->tx_tc = TC_DEFAULT;
+       priv->rx_tc = TC_DEFAULT;
+-      init_dma_desc_rings(dev);
++      ret = init_dma_desc_rings(dev);
++      if (ret)
++              goto init_phy_error;
+       /* DMA initialization and SW reset */
+       ret = sxgbe_init_dma_engine(priv);
+@@ -1187,6 +1189,7 @@ static int sxgbe_open(struct net_device *dev)
+ init_error:
+       free_dma_desc_resources(priv);
++init_phy_error:
+       if (dev->phydev)
+               phy_disconnect(dev->phydev);
+ phy_error:
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
new file mode 100644 (file)
index 0000000..cb82f15
--- /dev/null
@@ -0,0 +1,50 @@
+From b765ab3c94d70a7f1ea49d77e9014906f96d5770 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:19 +0800
+Subject: net: sxgbe: free TX rings on RX allocation failure
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
+
+When RX descriptor ring allocation fails, init_dma_desc_rings() only
+frees the partially allocated RX rings and returns. The TX rings that
+were allocated earlier in the same function are leaked.
+
+Rearrange error labels to clean up TX rings upon RX failures.
+
+Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver")
+Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
+Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+index 5051ada43d2fa..9b48a587d5c2e 100644
+--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+@@ -597,14 +597,13 @@ static int init_dma_desc_rings(struct net_device *netd)
+       return 0;
+-txalloc_err:
+-      while (queue_num--)
+-              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+-      return ret;
+-
+ rxalloc_err:
+       while (queue_num--)
+               free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
++      queue_num = SXGBE_TX_QUEUES;
++txalloc_err:
++      while (queue_num--)
++              free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
new file mode 100644 (file)
index 0000000..4e68cb5
--- /dev/null
@@ -0,0 +1,150 @@
+From 730b3d9cff353b00522ca168e0878f2b9466dfd9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 09:11:37 +0000
+Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ]
+
+syzbot reported a memory leak [1] in the UDP tunnel NIC offload code.
+
+When device registration fails (e.g. in register_netdevice()), netdev core
+unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued
+during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister()
+returns early:
+
+       if (utn->work_pending)
+               return;
+
+Because failed registrations do not enter netdev_wait_allrefs_any(), no
+subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the
+struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked
+permanently.
+
+Fix this by removing the early return. Instead, synchronously cancel any
+pending work with cancel_delayed_work_sync() before freeing @utn.
+
+To be able to call cancel_delayed_work_sync() while holding RTNL (the work also
+needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL
+is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work())
+to prevent high CPU contention while waiting for RTNL lock.
+
+The utn->work_pending bookkeeping is no longer needed and is removed, as
+the workqueue core already tracks the pending/running state of the work.
+
+[1]
+BUG: memory leak
+unreferenced object 0xffff888127d5f840 (size 96):
+  comm "syz-executor", pid 5806, jiffies 4294942188
+  backtrace (crc 99fdb6c8):
+    __kmalloc_noprof+0x3bf/0x550
+    udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline]
+    udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline]
+    udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931
+    notifier_call_chain+0x59/0x160 kernel/notifier.c:85
+    call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250
+    register_netdevice+0xc10/0xeb0 net/core/dev.c:11478
+
+Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure")
+Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++---------------
+ 1 file changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
+index 3b32a0afa9798..53a1a9c1f8bff 100644
+--- a/net/ipv4/udp_tunnel_nic.c
++++ b/net/ipv4/udp_tunnel_nic.c
+@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry {
+  * @lock:     protects all fields
+  * @need_sync:        at least one port start changed
+  * @need_replay: space was freed, we need a replay of all ports
+- * @work_pending: @work is currently scheduled
+  * @n_tables: number of tables under @entries
+  * @missed:   bitmap of tables which overflown
+  * @entries:  table of tables of ports currently offloaded
+  */
+ struct udp_tunnel_nic {
+-      struct work_struct work;
++      struct delayed_work work;
+       struct net_device *dev;
+@@ -46,7 +45,6 @@ struct udp_tunnel_nic {
+       u8 need_sync:1;
+       u8 need_replay:1;
+-      u8 work_pending:1;
+       unsigned int n_tables;
+       unsigned long missed;
+@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void
+ udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
+ {
+-      if (!utn->need_sync || utn->work_pending)
++      if (!utn->need_sync)
+               return;
+-      queue_work(udp_tunnel_nic_workqueue, &utn->work);
+-      utn->work_pending = 1;
++      queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0);
+ }
+ static bool
+@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
+ static void udp_tunnel_nic_device_sync_work(struct work_struct *work)
+ {
+       struct udp_tunnel_nic *utn =
+-              container_of(work, struct udp_tunnel_nic, work);
++              container_of(work, struct udp_tunnel_nic, work.work);
+-      rtnl_lock();
++      /* We cannot block on RTNL here, otherwise we would deadlock with
++       * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync()
++       * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended.
++       */
++      if (!rtnl_trylock()) {
++              queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1);
++              return;
++      }
+       mutex_lock(&utn->lock);
+-
+-      utn->work_pending = 0;
+       __udp_tunnel_nic_device_sync(utn->dev, utn);
+       if (utn->need_replay)
+@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info,
+       if (!utn)
+               return NULL;
+       utn->n_tables = n_tables;
+-      INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
++      INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
+       mutex_init(&utn->lock);
+       for (i = 0; i < n_tables; i++) {
+@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
+       udp_tunnel_nic_flush(dev, utn);
+       udp_tunnel_nic_unlock(dev);
+-      /* Wait for the work to be done using the state, netdev core will
+-       * retry unregister until we give up our reference on this device.
++      /* Make sure no work is running or queued before freeing @utn.
++       * The work handler uses rtnl_trylock(), so it will not deadlock
++       * against the RTNL we are holding here.
+        */
+-      if (utn->work_pending)
+-              return;
++      cancel_delayed_work_sync(&utn->work);
+       udp_tunnel_nic_free(utn);
+ release_dev:
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch b/queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch
new file mode 100644 (file)
index 0000000..71c8eb4
--- /dev/null
@@ -0,0 +1,195 @@
+From 50dbe9ade28f59635d89f631781c623ff49e1406 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 00:26:04 +0200
+Subject: netfilter: nf_conntrack_expect: add and use
+ nf_ct_expect_related_pair()
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 4aa63842fc92de1bce59d4709a0d32e718890bb2 ]
+
+Add a new function to insert a pair of expectations, this is required by
+the SIP and H323 NAT helpers. The spinlock is held to check if there is
+a slot for both expectations, in such case, insert them.
+
+This removes the need for nf_ct_unexpect_related() inside the loop to
+find a pair of consecutive ports, otherwise inserting expectations whose
+dead flag is already set on can happen.
+
+Bump master_help->expecting for the expectation class after checking if
+the expectation fits in the master expectation list, which is needed for
+this new _pair() function variant to run the eviction routine including
+the preallocated slot for the first expectation in the pair.
+
+Fixes: b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC to reap expectations")
+Reported-by: Jaeyeong Lee <iostreampy@proton.me>
+Link: https://patch.msgid.link/178377968720.33756.12204817361601593230@proton.me/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_conntrack_expect.h |  3 ++
+ net/ipv4/netfilter/nf_nat_h323.c            | 22 +++++--------
+ net/netfilter/nf_conntrack_expect.c         | 35 ++++++++++++++++++++-
+ net/netfilter/nf_nat_sip.c                  | 20 ++++--------
+ 4 files changed, 50 insertions(+), 30 deletions(-)
+
+diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
+index c024345c9bd86..26d6babd92fcd 100644
+--- a/include/net/netfilter/nf_conntrack_expect.h
++++ b/include/net/netfilter/nf_conntrack_expect.h
+@@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,
+       return nf_ct_expect_related_report(expect, 0, 0, flags);
+ }
++int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
++                            unsigned int flag);
++
+ struct nf_conn_help;
+ void nf_ct_expectation_gc(struct nf_conn_help *master_help);
+diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
+index 183e8a3ff2bab..6bcd6734769b5 100644
+--- a/net/ipv4/netfilter/nf_nat_h323.c
++++ b/net/ipv4/netfilter/nf_nat_h323.c
+@@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
+                       struct nf_conntrack_expect *rtp_exp,
+                       struct nf_conntrack_expect *rtcp_exp)
+ {
++      struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
+       struct nf_ct_h323_master *info = nfct_help_data(ct);
+       int dir = CTINFO2DIR(ctinfo);
+       int i;
+@@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
+               int ret;
+               rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
+-              ret = nf_ct_expect_related(rtp_exp, 0);
++              rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1);
++              ret = nf_ct_expect_related_pair(rtp_pair, 0);
+               if (ret == 0) {
+-                      rtcp_exp->tuple.dst.u.udp.port =
+-                          htons(nated_port + 1);
+-                      ret = nf_ct_expect_related(rtcp_exp, 0);
+-                      if (ret == 0)
+-                              break;
+-                      else if (ret == -EBUSY) {
+-                              nf_ct_unexpect_related(rtp_exp);
+-                              continue;
+-                      } else if (ret < 0) {
+-                              nf_ct_unexpect_related(rtp_exp);
+-                              nated_port = 0;
+-                              break;
+-                      }
+-              } else if (ret != -EBUSY) {
++                      break;
++              } else if (ret == -EBUSY) {
++                      continue;
++              } else if (ret < 0) {
+                       nated_port = 0;
+                       break;
+               }
+diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
+index 38630c5e006f0..16520a41fe5f0 100644
+--- a/net/netfilter/nf_conntrack_expect.c
++++ b/net/netfilter/nf_conntrack_expect.c
+@@ -426,7 +426,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp,
+               exp->timeout += helper->expect_policy[exp->class].timeout * HZ;
+       hlist_add_head_rcu(&exp->lnode, &master_help->expectations);
+-      master_help->expecting[exp->class]++;
+       hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
+       cnet = nf_ct_pernet(net);
+@@ -533,6 +532,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
+       if (ret < 0)
+               goto out;
++      master_help->expecting[expect->class]++;
+       nf_ct_expect_insert(expect, master_help);
+       nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
+@@ -545,6 +545,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
+ }
+ EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
++int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
++                            unsigned int flags)
++{
++      struct nf_conn_help *master_help;
++      int i, ret;
++
++      spin_lock_bh(&nf_conntrack_expect_lock);
++      master_help = nfct_help(expect[0]->master);
++      if (!master_help || master_help != nfct_help(expect[1]->master)) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      for (i = 0; i < 2; i++) {
++              ret = __nf_ct_expect_check(expect[i], master_help, flags);
++              if (ret < 0) {
++                      if (i == 1)
++                              master_help->expecting[expect[0]->class]--;
++                      goto out;
++              }
++              master_help->expecting[expect[i]->class]++;
++      }
++
++      for (i = 0; i < 2; i++) {
++              nf_ct_expect_insert(expect[i], master_help);
++              nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0);
++      }
++out:
++      spin_unlock_bh(&nf_conntrack_expect_lock);
++      return ret;
++}
++EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair);
++
+ void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data),
+                                 void *data)
+ {
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index a93eaf0f7d305..133bd713fe0c2 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
+                                    unsigned int medialen,
+                                    union nf_inet_addr *rtp_addr)
+ {
++      struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+@@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
+               int ret;
+               rtp_exp->tuple.dst.u.udp.port = htons(port);
+-              ret = nf_ct_expect_related(rtp_exp,
+-                                         NF_CT_EXP_F_SKIP_MASTER);
+-              if (ret == -EBUSY)
+-                      continue;
+-              else if (ret < 0) {
+-                      port = 0;
+-                      break;
+-              }
+               rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
+-              ret = nf_ct_expect_related(rtcp_exp,
+-                                         NF_CT_EXP_F_SKIP_MASTER);
++
++              ret = nf_ct_expect_related_pair(rtp_pair,
++                                              NF_CT_EXP_F_SKIP_MASTER);
+               if (ret == 0)
+                       break;
+-              else if (ret == -EBUSY) {
+-                      nf_ct_unexpect_related(rtp_exp);
++              else if (ret == -EBUSY)
+                       continue;
+-              } else if (ret < 0) {
+-                      nf_ct_unexpect_related(rtp_exp);
++              else if (ret < 0) {
+                       port = 0;
+                       break;
+               }
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
new file mode 100644 (file)
index 0000000..db1246f
--- /dev/null
@@ -0,0 +1,96 @@
+From 9a7d0eb6ebdedcfcfd2c1509b5a305c5cb88ef59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jul 2026 16:42:01 -0700
+Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in
+ sip_help_tcp()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
+
+sip_help_tcp() stores the size change of each NAT-rewritten SIP message
+in s16 diff and accumulates it in s16 tdiff, but a single message can
+grow by more than S16_MAX while the packet stays under the 65535
+enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long
+Contact list expands the message by tens of kilobytes. diff then wraps,
+and "datalen = datalen + diff - msglen" yields a huge unsigned datalen,
+so the next iteration's ct_sip_get_header() reads past the linearized skb
+tail.
+
+Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the
+65535 byte packet limit, and the seqadj core is already s32
+(nf_ct_seqadj_set() takes s32), so no previously accepted input is
+rejected.
+
+  BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+  Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25
+   ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464)
+   sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694)
+   nf_confirm (net/netfilter/nf_conntrack_proto.c:183)
+   nf_hook_slow (net/netfilter/core.c:619)
+   ip6_output (net/ipv6/ip6_output.c:246)
+   ip6_forward (net/ipv6/ip6_output.c:690)
+   ipv6_rcv (net/ipv6/ip6_input.c:351)
+   __netif_receive_skb_one_core (net/core/dev.c:6212)
+   process_backlog (net/core/dev.c:6676)
+   __napi_poll (net/core/dev.c:7735)
+   net_rx_action (net/core/dev.c:7955)
+   handle_softirqs (kernel/softirq.c:622)
+   run_ksoftirqd (kernel/softirq.c:1076)
+   ...
+
+Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/netfilter/nf_conntrack_sip.h | 2 +-
+ net/netfilter/nf_conntrack_sip.c           | 2 +-
+ net/netfilter/nf_nat_sip.c                 | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
+index dbc614dfe0d56..aafa0c04f917e 100644
+--- a/include/linux/netfilter/nf_conntrack_sip.h
++++ b/include/linux/netfilter/nf_conntrack_sip.h
+@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
+                           unsigned int *datalen);
+       void (*seq_adjust)(struct sk_buff *skb,
+-                         unsigned int protoff, s16 off);
++                         unsigned int protoff, s32 off);
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
+index f3f90a8663389..e4a70d1d77b0b 100644
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -1663,7 +1663,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
+       unsigned int matchoff, matchlen;
+       unsigned int msglen, origlen;
+       const char *dptr, *end;
+-      s16 diff, tdiff = 0;
++      s32 diff, tdiff = 0;
+       int ret = NF_ACCEPT;
+       unsigned long clen;
+       bool term;
+diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
+index aea02f6aff092..a93eaf0f7d305 100644
+--- a/net/netfilter/nf_nat_sip.c
++++ b/net/netfilter/nf_nat_sip.c
+@@ -321,7 +321,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
+ }
+ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
+-                                s16 off)
++                                s32 off)
+ {
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
new file mode 100644 (file)
index 0000000..44e4829
--- /dev/null
@@ -0,0 +1,205 @@
+From d215d6ce24f26945944eafa143acbe96fee7905a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 10:13:37 +0200
+Subject: netfilter: nf_tables: make nft_object rhltable per table
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ]
+
+The nft_object rhltable is global, this allows for accessing objects
+that are being dismangled from lookup path by other existing netns.
+Given the nft_obj_destroy() releases the object inmediately, this might
+lead to use-after-free of these objects that are being released.
+Make the existing rhltable per table to address this issue to deal with
+with the nft_rcv_nl_event() path too.
+
+Update nft_obj_lookup() to take the table as non-const, otherwise,
+compiler complains when passing the objname_ht to rhltable_lookup().
+
+Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable")
+Suggested-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h |  4 +++-
+ net/netfilter/nf_tables_api.c     | 34 +++++++++++++++----------------
+ 2 files changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 9d844354c4d95..3be612145c130 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use)
+  *    @sets: sets in the table
+  *    @objects: stateful objects in the table
+  *    @flowtables: flow tables in the table
++ *    @objname_ht: hashtable for objects lookup by name
+  *    @hgenerator: handle generator state
+  *    @handle: table handle
+  *    @use: number of chain references to this table
+@@ -1313,6 +1314,7 @@ struct nft_table {
+       struct list_head                sets;
+       struct list_head                objects;
+       struct list_head                flowtables;
++      struct rhltable                 objname_ht;
+       u64                             hgenerator;
+       u64                             handle;
+       u32                             use;
+@@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)
+ #define nft_expr_obj(expr)    *((struct nft_object **)nft_expr_priv(expr))
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask);
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index e1db3e678656d..c89e55ffc77d1 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -45,8 +45,6 @@ enum {
+       NFT_VALIDATE_DO,
+ };
+-static struct rhltable nft_objname_ht;
+-
+ static u32 nft_chain_hash(const void *data, u32 len, u32 seed);
+ static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed);
+ static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *);
+@@ -1635,6 +1633,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err)
+               goto err_chain_ht;
++      err = rhltable_init(&table->objname_ht, &nft_objname_ht_params);
++      if (err < 0)
++              goto err_obj_ht;
++
+       INIT_LIST_HEAD(&table->chains);
+       INIT_LIST_HEAD(&table->sets);
+       INIT_LIST_HEAD(&table->objects);
+@@ -1653,6 +1655,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
+       list_add_tail_rcu(&table->list, &nft_net->tables);
+       return 0;
+ err_trans:
++      rhltable_destroy(&table->objname_ht);
++err_obj_ht:
+       rhltable_destroy(&table->chains_ht);
+ err_chain_ht:
+       kfree(table->udata);
+@@ -1819,6 +1823,7 @@ static void nf_tables_table_destroy(struct nft_table *table)
+               return;
+       rhltable_destroy(&table->chains_ht);
++      rhltable_destroy(&table->objname_ht);
+       kfree(table->name);
+       kfree(table->udata);
+       kfree(table);
+@@ -8082,7 +8087,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type)
+ EXPORT_SYMBOL_GPL(nft_unregister_obj);
+ struct nft_object *nft_obj_lookup(const struct net *net,
+-                                const struct nft_table *table,
++                                struct nft_table *table,
+                                 const struct nlattr *nla, u32 objtype,
+                                 u8 genmask)
+ {
+@@ -8098,7 +8103,7 @@ struct nft_object *nft_obj_lookup(const struct net *net,
+                    !lockdep_commit_lock_is_held(net));
+       rcu_read_lock();
+-      list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params);
++      list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params);
+       if (!list)
+               goto out;
+@@ -8376,7 +8381,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
+       if (err < 0)
+               goto err_trans;
+-      err = rhltable_insert(&nft_objname_ht, &obj->rhlhead,
++      err = rhltable_insert(&table->objname_ht, &obj->rhlhead,
+                             nft_objname_ht_params);
+       if (err < 0)
+               goto err_obj_ht;
+@@ -8561,8 +8566,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,
+       struct netlink_ext_ack *extack = info->extack;
+       u8 genmask = nft_genmask_cur(info->net);
+       u8 family = info->nfmsg->nfgen_family;
+-      const struct nft_table *table;
+       struct net *net = info->net;
++      struct nft_table *table;
+       struct nft_object *obj;
+       struct sk_buff *skb2;
+       u32 objtype;
+@@ -10423,9 +10428,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
+               nf_tables_commit_chain_free_rules_old(g0);
+ }
+-static void nft_obj_del(struct nft_object *obj)
++static void nft_obj_del(struct nft_table *table, struct nft_object *obj)
+ {
+-      rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params);
++      rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);
+       list_del_rcu(&obj->list);
+ }
+@@ -11110,7 +11115,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
+                       break;
+               case NFT_MSG_DELOBJ:
+               case NFT_MSG_DESTROYOBJ:
+-                      nft_obj_del(nft_trans_obj(trans));
++                      nft_obj_del(table, nft_trans_obj(trans));
+                       nf_tables_obj_notify(&ctx, nft_trans_obj(trans),
+                                            trans->msg_type);
+                       break;
+@@ -11402,7 +11407,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
+                               nft_trans_destroy(trans);
+                       } else {
+                               nft_use_dec_restore(&table->use);
+-                              nft_obj_del(nft_trans_obj(trans));
++                              nft_obj_del(table, nft_trans_obj(trans));
+                       }
+                       break;
+               case NFT_MSG_DELOBJ:
+@@ -12025,7 +12030,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
+               nft_set_destroy(&ctx, set);
+       }
+       list_for_each_entry_safe(obj, ne, &table->objects, list) {
+-              nft_obj_del(obj);
++              nft_obj_del(table, obj);
+               nft_use_dec(&table->use);
+               nft_obj_destroy(&ctx, obj);
+       }
+@@ -12207,10 +12212,6 @@ static int __init nf_tables_module_init(void)
+       if (err < 0)
+               goto err_netdev_notifier;
+-      err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params);
+-      if (err < 0)
+-              goto err_rht_objname;
+-
+       err = nft_offload_init();
+       if (err < 0)
+               goto err_offload;
+@@ -12233,8 +12234,6 @@ static int __init nf_tables_module_init(void)
+ err_netlink_notifier:
+       nft_offload_exit();
+ err_offload:
+-      rhltable_destroy(&nft_objname_ht);
+-err_rht_objname:
+       unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
+ err_netdev_notifier:
+       nf_tables_core_module_exit();
+@@ -12256,7 +12255,6 @@ static void __exit nf_tables_module_exit(void)
+       unregister_pernet_subsys(&nf_tables_net_ops);
+       cancel_work_sync(&trans_gc_work);
+       rcu_barrier();
+-      rhltable_destroy(&nft_objname_ht);
+       nf_tables_core_module_exit();
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
new file mode 100644 (file)
index 0000000..9f484a6
--- /dev/null
@@ -0,0 +1,69 @@
+From aa1c09461375c2b3b60e74721bf98aa0eea53f5b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:15:23 +0000
+Subject: netfilter: nft_payload: fix mask build for partial field offload
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
+
+nft_payload_offload_mask() builds the offload match mask for a payload
+expression that covers only part of a header field.  For a partial IPv6
+address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
+is undefined on the 32-bit int operand.  It also trims only one word, so
+the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
+the trim is skipped entirely), leaving the mask covering more bytes than
+the rule matches.
+
+  UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
+  shift exponent 120 is too large for 32-bit type 'int'
+  ...
+
+The match is byte-granular and struct nft_data is zero-initialised, so the
+correct mask is simply the first priv_len bytes set to 0xff. Set those
+bytes directly and drop the word/shift trimming; this removes the undefined
+shift and no longer over-masks the trailing bytes.
+
+Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_payload.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
+index 4438d05f8018c..8ffa864a2195a 100644
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb,
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+                                    u32 priv_len, u32 field_len)
+ {
+-      unsigned int remainder, delta, k;
+       struct nft_data mask = {};
+-      __be32 remainder_mask;
+       if (priv_len == field_len) {
+               memset(&reg->mask, 0xff, priv_len);
+@@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+               return false;
+       }
+-      memset(&mask, 0xff, field_len);
+-      remainder = priv_len % sizeof(u32);
+-      if (remainder) {
+-              k = priv_len / sizeof(u32);
+-              delta = field_len - priv_len;
+-              remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
+-              mask.data[k] = (__force u32)remainder_mask;
+-      }
+-
++      memset(&mask, 0xff, priv_len);
+       memcpy(&reg->mask, &mask, field_len);
+       return true;
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
new file mode 100644 (file)
index 0000000..f54594a
--- /dev/null
@@ -0,0 +1,83 @@
+From 3721c015d8cd0904795fdfe5a1bc0477bfc5144a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 22:02:46 +0200
+Subject: netfilter: xt_hashlimit: validate hashtable supports
+ XT_HASHLIMIT_RATE_MATCH
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
+
+The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the
+dsthash_ent structure which represents an entry in the hashtable.  There
+is a union area which uses a different layout to express the rate match
+mode.
+
+Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode
+flag is requested by two or more different rules that refer to the same
+hashtable. Otherwise, uninitialized access to the burst field in the
+union is possible.
+
+Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by
+revision less than 3 too.
+
+Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode")
+Reported-and-tested-by: Talha Berk Arslan <talha.anything.info@gmail.com>
+Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/xt_hashlimit.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index 2704b4b60d1e0..9af0fa895f730 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -117,6 +117,7 @@ struct xt_hashlimit_htable {
+       refcount_t use;
+       u_int8_t family;
+       bool rnd_initialized;
++      bool ratematch;
+       struct hashlimit_cfg3 cfg;      /* config */
+@@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
+               kvfree(hinfo);
+               return -ENOMEM;
+       }
++      hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
+       spin_lock_init(&hinfo->lock);
+       switch (revision) {
+@@ -872,7 +874,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+       }
+       /* Check for overflow. */
+-      if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++      if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
++              if (revision < 3)
++                      return -EINVAL;
++
+               if (cfg->avg == 0 || cfg->avg > U32_MAX) {
+                       pr_info_ratelimited("invalid rate\n");
+                       return -ERANGE;
+@@ -905,6 +910,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
+                       mutex_unlock(&hashlimit_mutex);
+                       return ret;
+               }
++      } else {
++              if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
++                   !(*hinfo)->ratematch) ||
++                  (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
++                    (*hinfo)->ratematch)) {
++                      mutex_unlock(&hashlimit_mutex);
++                      htable_put(*hinfo);
++                      return -EINVAL;
++              }
+       }
+       mutex_unlock(&hashlimit_mutex);
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch b/queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch
new file mode 100644 (file)
index 0000000..da86520
--- /dev/null
@@ -0,0 +1,44 @@
+From bc8fc6340236115402b5c926dc2deeab57608aba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:12 +0100
+Subject: netfs: clear PG_private_2 on copy-to-cache append failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 ]
+
+netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before
+netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling
+buffer.
+
+If the append fails, the folio is not queued for cache writeback, so
+the PG_private_2 state and its reference must be released immediately.
+
+Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/read_pgpriv2.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c
+index a1489aa29f782..7eacc58abadb7 100644
+--- a/fs/netfs/read_pgpriv2.c
++++ b/fs/netfs/read_pgpriv2.c
+@@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
+       /* Attach the folio to the rolling buffer. */
+       if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) {
++              folio_end_private_2(folio);
+               clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags);
+               return;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch b/queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch
new file mode 100644 (file)
index 0000000..b5422ba
--- /dev/null
@@ -0,0 +1,371 @@
+From 5ffea70555f4f3c9469436ad614c450f679ae5cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:15 +0100
+Subject: netfs: Fix folio_queue ENOMEM in writeback by adding a mempool
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 1d78d56c43ef3768183e8370e7367b162700e049 ]
+
+Fix the handling of folio_queue allocation failure in writeback by adding a
+mempool and passing in gfp_t flags to the rolling buffer functions that
+allocate memory, using the mempool if gfp != GFP_KERNEL.
+
+This is then extended upwards and the gfp to be used for a request is stored
+in the netfs_io_request struct and is then used for both requests and
+subrequests, eliminating the sleeping loops there.
+
+The failure caused:
+
+    folio != NULL
+    WARNING: fs/netfs/write_issue.c:603 at netfs_writepages+0x883/0xa10 fs/netfs/write_issue.c:603, CPU#3: syz.0.17/5919
+
+Fixes: cd0277ed0c18 ("netfs: Use new folio_queue data type and iterator instead of xarray iter")
+Reported-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=0da43efa72f88bd3a8af
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-5-dhowells@redhat.com
+Tested-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Yun Zhou <yun.zhou@windriver.com>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: Christoph Hellwig <hch@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/buffered_read.c       |  6 +++---
+ fs/netfs/internal.h            |  1 +
+ fs/netfs/main.c                |  7 +++++++
+ fs/netfs/objects.c             | 30 +++++++++++++++++-------------
+ fs/netfs/read_pgpriv2.c        |  2 +-
+ fs/netfs/rolling_buffer.c      | 22 +++++++++++++---------
+ fs/netfs/write_issue.c         | 10 +++++-----
+ include/linux/netfs.h          |  1 +
+ include/linux/rolling_buffer.h |  6 +++---
+ 9 files changed, 51 insertions(+), 34 deletions(-)
+
+diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
+index 68534cef37cd2..6c2192bb49cfc 100644
+--- a/fs/netfs/buffered_read.c
++++ b/fs/netfs/buffered_read.c
+@@ -361,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl)
+       netfs_rreq_expand(rreq, ractl);
+       rreq->submitted = rreq->start;
+-      if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
++      if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
+               goto cleanup_free;
+       netfs_read_to_pagecache(rreq, ractl);
+@@ -380,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo
+ {
+       ssize_t added;
+-      if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
++      if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
+               return -ENOMEM;
+-      added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags);
++      added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp);
+       if (added < 0)
+               return added;
+       rreq->submitted = rreq->start + added;
+diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
+index 645996ecfc803..acdd543349915 100644
+--- a/fs/netfs/internal.h
++++ b/fs/netfs/internal.h
+@@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests;
+ extern spinlock_t netfs_proc_lock;
+ extern mempool_t netfs_request_pool;
+ extern mempool_t netfs_subrequest_pool;
++extern mempool_t netfs_folioq_pool;
+ #ifdef CONFIG_PROC_FS
+ static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq)
+diff --git a/fs/netfs/main.c b/fs/netfs/main.c
+index 73da6c9f5777c..927badf3989db 100644
+--- a/fs/netfs/main.c
++++ b/fs/netfs/main.c
+@@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab;
+ static struct kmem_cache *netfs_subrequest_slab;
+ mempool_t netfs_request_pool;
+ mempool_t netfs_subrequest_pool;
++mempool_t netfs_folioq_pool;
+ #ifdef CONFIG_PROC_FS
+ LIST_HEAD(netfs_io_requests);
+@@ -108,6 +109,9 @@ static int __init netfs_init(void)
+ {
+       int ret = -ENOMEM;
++      if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0)
++              goto error_folioq_pool;
++
+       netfs_request_slab = kmem_cache_create("netfs_request",
+                                              sizeof(struct netfs_io_request), 0,
+                                              SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT,
+@@ -160,6 +164,8 @@ static int __init netfs_init(void)
+ error_reqpool:
+       kmem_cache_destroy(netfs_request_slab);
+ error_req:
++      mempool_exit(&netfs_folioq_pool);
++error_folioq_pool:
+       return ret;
+ }
+ fs_initcall(netfs_init);
+@@ -172,5 +178,6 @@ static void __exit netfs_exit(void)
+       kmem_cache_destroy(netfs_subrequest_slab);
+       mempool_exit(&netfs_request_pool);
+       kmem_cache_destroy(netfs_request_slab);
++      mempool_exit(&netfs_folioq_pool);
+ }
+ module_exit(netfs_exit);
+diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c
+index b8c4918d3dcda..01461a74642d6 100644
+--- a/fs/netfs/objects.c
++++ b/fs/netfs/objects.c
+@@ -7,7 +7,6 @@
+ #include <linux/slab.h>
+ #include <linux/mempool.h>
+-#include <linux/delay.h>
+ #include "internal.h"
+ static void netfs_free_request(struct work_struct *work);
+@@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
+       struct netfs_io_request *rreq;
+       mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool;
+       struct kmem_cache *cache = mempool->pool_data;
++      gfp_t gfp = GFP_KERNEL;
+       int ret;
+-      for (;;) {
+-              rreq = mempool_alloc(mempool, GFP_KERNEL);
+-              if (rreq)
+-                      break;
+-              msleep(10);
++      /* Writeback is part of memory reclaim and must not fail due to ENOMEM. */
++      if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) {
++              gfp = GFP_NOFS; /* Allows use of mempools. */
++
++              rreq = mempool_alloc(mempool, gfp);
++      } else {
++              rreq = mempool->alloc(gfp, mempool->pool_data);
++              if (!rreq)
++                      return ERR_PTR(-ENOMEM);
+       }
+       memset(rreq, 0, kmem_cache_size(cache));
+       INIT_WORK(&rreq->cleanup_work, netfs_free_request);
++      rreq->gfp       = gfp;
+       rreq->start     = start;
+       rreq->len       = len;
+       rreq->origin    = origin;
+@@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq
+       mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool;
+       struct kmem_cache *cache = mempool->pool_data;
+-      for (;;) {
+-              subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool,
+-                                     GFP_KERNEL);
+-              if (subreq)
+-                      break;
+-              msleep(10);
+-      }
++      if (rreq->gfp == GFP_KERNEL)
++              subreq = mempool->alloc(rreq->gfp, mempool->pool_data);
++      else
++              subreq = mempool_alloc(mempool, rreq->gfp);
++      if (!subreq)
++              return NULL;
+       memset(subreq, 0, kmem_cache_size(cache));
+       INIT_WORK(&subreq->work, NULL);
+diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c
+index 7eacc58abadb7..c31190993b762 100644
+--- a/fs/netfs/read_pgpriv2.c
++++ b/fs/netfs/read_pgpriv2.c
+@@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
+       trace_netfs_folio(folio, netfs_folio_trace_store_copy);
+       /* Attach the folio to the rolling buffer. */
+-      if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) {
++      if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) {
+               folio_end_private_2(folio);
+               clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags);
+               return;
+diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c
+index a17fbf9853a44..8c0026836f9c1 100644
+--- a/fs/netfs/rolling_buffer.c
++++ b/fs/netfs/rolling_buffer.c
+@@ -6,6 +6,7 @@
+  */
+ #include <linux/bitops.h>
++#include <linux/mempool.h>
+ #include <linux/pagemap.h>
+ #include <linux/rolling_buffer.h>
+ #include <linux/slab.h>
+@@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp,
+ {
+       struct folio_queue *fq;
+-      fq = kmalloc_obj(*fq, gfp);
++      if (gfp == GFP_KERNEL)
++              fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data);
++      else
++              fq = mempool_alloc(&netfs_folioq_pool, gfp);
+       if (fq) {
+               netfs_stat(&netfs_n_folioq);
+               folioq_init(fq, rreq_id);
+@@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq,
+ {
+       trace_netfs_folioq(folioq, trace);
+       netfs_stat_d(&netfs_n_folioq);
+-      kfree(folioq);
++      mempool_free(folioq, &netfs_folioq_pool);
+ }
+ EXPORT_SYMBOL(netfs_folioq_free);
+@@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free);
+  * consumer.
+  */
+ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
+-                      unsigned int direction)
++                      unsigned int direction, gfp_t gfp)
+ {
+       struct folio_queue *fq;
+-      fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init);
++      fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init);
+       if (!fq)
+               return -ENOMEM;
+@@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
+ /*
+  * Add another folio_queue to a rolling buffer if there's no space left.
+  */
+-int rolling_buffer_make_space(struct rolling_buffer *roll)
++int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
+ {
+       struct folio_queue *fq, *head = roll->head;
+       if (!folioq_full(head))
+               return 0;
+-      fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space);
++      fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space);
+       if (!fq)
+               return -ENOMEM;
+       fq->prev = head;
+@@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
+       int nr, ix, to;
+       ssize_t size = 0;
+-      if (rolling_buffer_make_space(roll) < 0)
++      if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0)
+               return -ENOMEM;
+       fq = roll->head;
+@@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
+  * Append a folio to the rolling buffer.
+  */
+ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
+-                            unsigned int flags)
++                            unsigned int flags, gfp_t gfp)
+ {
+       ssize_t size = folio_size(folio);
+       int slot;
+-      if (rolling_buffer_make_space(roll) < 0)
++      if (rolling_buffer_make_space(roll, gfp) < 0)
+               return -ENOMEM;
+       slot = folioq_append(roll->head, folio);
+diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
+index 8d3a6cad42d5e..0e5f0f259bd05 100644
+--- a/fs/netfs/write_issue.c
++++ b/fs/netfs/write_issue.c
+@@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
+       ictx = netfs_inode(wreq->inode);
+       if (is_cacheable)
+               fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx));
+-      if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0)
++      if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0)
+               goto nomem;
+       wreq->cleaned_to = wreq->start;
+@@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq,
+        */
+       if (iov_iter_is_folioq(wreq_iter) &&
+           wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq))
+-              rolling_buffer_make_space(&wreq->buffer);
++              rolling_buffer_make_space(&wreq->buffer, wreq->gfp);
+       subreq = netfs_alloc_subrequest(wreq);
+       subreq->source          = stream->source;
+@@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
+       _enter("");
+-      if (rolling_buffer_make_space(&wreq->buffer) < 0)
++      if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0)
+               return -ENOMEM;
+       /* netfs_perform_write() may shift i_size around the page or from out
+@@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
+       }
+       /* Attach the folio to the rolling buffer. */
+-      rolling_buffer_append(&wreq->buffer, folio, 0);
++      rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp);
+       /* Move the submission point forward to allow for write-streaming data
+        * not starting at the front of the page.  We don't do write-streaming
+@@ -760,7 +760,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
+       /* Attach the folio to the rolling buffer. */
+       folio_get(folio);
+-      ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
++      ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp);
+       if (ret < 0) {
+               folio_put(folio);
+               return ret;
+diff --git a/include/linux/netfs.h b/include/linux/netfs.h
+index 243c0f7379388..7b2daadffe198 100644
+--- a/include/linux/netfs.h
++++ b/include/linux/netfs.h
+@@ -253,6 +253,7 @@ struct netfs_io_request {
+       unsigned long long      cleaned_to;     /* Position we've cleaned folios to */
+       unsigned long long      abandon_to;     /* Position to abandon folios to */
+       const struct folio      *no_unlock_folio; /* Don't unlock this folio after read */
++      gfp_t                   gfp;            /* GFP flags to use */
+       unsigned int            direct_bv_count; /* Number of elements in direct_bv[] */
+       unsigned int            debug_id;
+       unsigned int            rsize;          /* Maximum read size (0 for none) */
+diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h
+index ac15b1ffdd831..9e5dad29669cf 100644
+--- a/include/linux/rolling_buffer.h
++++ b/include/linux/rolling_buffer.h
+@@ -43,13 +43,13 @@ struct rolling_buffer_snapshot {
+ #define ROLLBUF_MARK_2        BIT(1)
+ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
+-                      unsigned int direction);
+-int rolling_buffer_make_space(struct rolling_buffer *roll);
++                      unsigned int direction, gfp_t gfp);
++int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp);
+ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
+                                   struct readahead_control *ractl,
+                                   struct folio_batch *put_batch);
+ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
+-                            unsigned int flags);
++                            unsigned int flags, gfp_t gfp);
+ struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll);
+ void rolling_buffer_clear(struct rolling_buffer *roll);
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch b/queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch
new file mode 100644 (file)
index 0000000..88dea52
--- /dev/null
@@ -0,0 +1,57 @@
+From 8ede923dec1edaca1266043400e475d0fc565399 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:13 +0100
+Subject: netfs: handle single writeback rolling buffer allocation failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit 37a1c535c80c67d98668d190c7432f9ebda43310 ]
+
+netfs_write_folio_single() takes an extra folio reference before
+appending the folio to the rolling buffer.
+
+rolling_buffer_append() can fail if it cannot allocate another
+folio_queue. Check the return value and drop the extra folio reference
+before returning the error.
+
+Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/write_issue.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
+index d0d884731dc5d..8d3a6cad42d5e 100644
+--- a/fs/netfs/write_issue.c
++++ b/fs/netfs/write_issue.c
+@@ -731,6 +731,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
+       size_t iter_off = 0;
+       size_t fsize = folio_size(folio), flen;
+       loff_t fpos = folio_pos(folio);
++      ssize_t ret;
+       bool to_eof = false;
+       bool no_debug = false;
+@@ -759,7 +760,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
+       /* Attach the folio to the rolling buffer. */
+       folio_get(folio);
+-      rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
++      ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
++      if (ret < 0) {
++              folio_put(folio);
++              return ret;
++      }
+       /* Move the submission point forward to allow for write-streaming data
+        * not starting at the front of the page.  We don't do write-streaming
+-- 
+2.53.0
+
diff --git a/queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch b/queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch
new file mode 100644 (file)
index 0000000..ec80d8a
--- /dev/null
@@ -0,0 +1,49 @@
+From afab3556fd2ceb4069540f0245a09ed716235b01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:07:14 +0100
+Subject: netfs: release readahead folios on iterator preparation failure
+
+From: Yichong Chen <chenyichong@uniontech.com>
+
+[ Upstream commit 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 ]
+
+netfs_prepare_read_iterator() batches readahead folios in put_batch so that
+the folio references can be dropped after the I/O iterator has been
+prepared.
+
+If rolling_buffer_load_from_ra() fails after earlier folios have been
+batched, the function returns immediately and leaves those references held.
+Release the batch before returning the error.
+
+Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation")
+Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/netfs/buffered_read.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
+index 76d0f6a29abab..68534cef37cd2 100644
+--- a/fs/netfs/buffered_read.c
++++ b/fs/netfs/buffered_read.c
+@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq,
+                       added = rolling_buffer_load_from_ra(&rreq->buffer, ractl,
+                                                           &put_batch);
+-                      if (added < 0)
++                      if (added < 0) {
++                              folio_batch_release(&put_batch);
+                               return added;
++                      }
+                       rreq->submitted += added;
+               }
+               folio_batch_release(&put_batch);
+-- 
+2.53.0
+
diff --git a/queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch b/queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch
new file mode 100644 (file)
index 0000000..746e965
--- /dev/null
@@ -0,0 +1,71 @@
+From 69a403460e87b06cc66f12076d7e78189fdf36e8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 00:29:51 +0000
+Subject: nexthop: avoid unlocked f6i_list walk in nh_rt_cache_flush
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 4787a6d2629b4e8c0b6bacab1f75c1660eca44d9 ]
+
+nh_rt_cache_flush() walks nh->f6i_list during an RTNL-serialized nexthop
+replace without holding nh->lock, racing the unlocked IPv6 route
+add/delete that mutate the list under nh->lock and free fib6_info
+entries (nh_rt_cache_flush() is inlined into rtm_new_nexthop()):
+
+  BUG: KASAN: slab-use-after-free in nh_rt_cache_flush (net/ipv4/nexthop.c:2243)
+  Read of size 8 at addr ffff888012953e18 by task exploit/146
+   nh_rt_cache_flush (net/ipv4/nexthop.c:2243)
+   replace_nexthop (net/ipv4/nexthop.c:2610)
+   rtm_new_nexthop (net/ipv4/nexthop.c:3323)
+   rtnetlink_rcv_msg (net/core/rtnetlink.c:7076)
+
+Unlike the other f6i_list walks, this one bumps each route's sernum via
+fib6_update_sernum_upto_root(), which needs tb6_lock; taking nh->lock
+around it would invert the established tb6_lock -> nh->lock order and
+deadlock. As the only purpose is to invalidate cached dsts, bump the
+IPv6 sernum for the whole netns with rt_genid_bump_ipv6() instead,
+mirroring the rt_cache_flush() already done for IPv4 just above.
+
+Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260722002951.2614721-2-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/nexthop.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
+index 8b1f3382a998a..1bfc429fc9607 100644
+--- a/net/ipv4/nexthop.c
++++ b/net/ipv4/nexthop.c
+@@ -2223,18 +2223,18 @@ static void remove_nexthop(struct net *net, struct nexthop *nh,
+ static void nh_rt_cache_flush(struct net *net, struct nexthop *nh,
+                             struct nexthop *replaced_nh)
+ {
+-      struct fib6_info *f6i;
+       struct nh_group *nhg;
++      bool have_f6i;
+       int i;
+       if (!list_empty(&nh->fi_list))
+               rt_cache_flush(net);
+-      list_for_each_entry(f6i, &nh->f6i_list, nh_list) {
+-              spin_lock_bh(&f6i->fib6_table->tb6_lock);
+-              fib6_update_sernum_upto_root(net, f6i);
+-              spin_unlock_bh(&f6i->fib6_table->tb6_lock);
+-      }
++      spin_lock_bh(&nh->lock);
++      have_f6i = !list_empty(&nh->f6i_list);
++      spin_unlock_bh(&nh->lock);
++      if (have_f6i)
++              rt_genid_bump_ipv6(net);
+       /* if an IPv6 group was replaced, we have to release all old
+        * dsts to make sure all refcounts are released
+-- 
+2.53.0
+
diff --git a/queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch b/queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch
new file mode 100644 (file)
index 0000000..2f0bc0a
--- /dev/null
@@ -0,0 +1,86 @@
+From 9b0956956136a72831574cb8a105b7e580ab8cfb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 00:29:50 +0000
+Subject: nexthop: take nh->lock for f6i_list walks in replace check and notify
+
+From: Xiang Mei (Microsoft) <xmei5@asu.edu>
+
+[ Upstream commit 072cd1f21819dedd2252e704d255de3b0cfc61a7 ]
+
+fib6_check_nh_list() and __nexthop_replace_notify() walk nh->f6i_list
+during an RTNL-serialized nexthop replace without holding nh->lock. IPv6
+RTM_NEWROUTE/RTM_DELROUTE run without RTNL and mutate that list under
+nh->lock (fib6_add_rt2node_nh(), fib6_purge_rt()), so both walks race a
+concurrent route delete that unlinks and frees a fib6_info:
+
+  BUG: KASAN: slab-use-after-free in rt6_fill_node.isra.0 (net/ipv6/route.c:5799)
+  Read of size 4 at addr ffff888014607e64 by task exploit/143
+   rt6_fill_node.isra.0 (net/ipv6/route.c:5799)
+   fib6_rt_update (net/ipv6/route.c:6412)
+   __nexthop_replace_notify (net/ipv4/nexthop.c:2542)
+   rtm_new_nexthop (net/ipv4/nexthop.c:2554)
+   rtnetlink_rcv_msg (net/core/rtnetlink.c:7076)
+
+  BUG: KASAN: slab-use-after-free in fib6_check_nh_list (net/ipv4/nexthop.c:1605)
+  Read of size 8 at addr ffff888014a7d068 by task exploit/142
+   fib6_check_nh_list (net/ipv4/nexthop.c:1605)
+   rtm_new_nexthop (net/ipv4/nexthop.c:2575)
+   rtnetlink_rcv_msg (net/core/rtnetlink.c:7076)
+
+Both walks only read the entries and take no tb6_lock, so protect them
+with nh->lock; fib6_rt_update() uses gfp_any(), which returns GFP_ATOMIC
+under the lock.
+
+Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.")
+Reported-by: AutonomousCodeSecurity@microsoft.com
+Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260722002951.2614721-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/nexthop.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
+index dc95a3d6e899e..8b1f3382a998a 100644
+--- a/net/ipv4/nexthop.c
++++ b/net/ipv4/nexthop.c
+@@ -1597,14 +1597,21 @@ static int fib6_check_nh_list(struct nexthop *old, struct nexthop *new,
+                             struct netlink_ext_ack *extack)
+ {
+       struct fib6_info *f6i;
++      int err = 0;
+       if (list_empty(&old->f6i_list))
+               return 0;
++      spin_lock_bh(&old->lock);
+       list_for_each_entry(f6i, &old->f6i_list, nh_list) {
+-              if (check_src_addr(&f6i->fib6_src.addr, extack) < 0)
+-                      return -EINVAL;
++              err = check_src_addr(&f6i->fib6_src.addr, extack);
++              if (err)
++                      break;
+       }
++      spin_unlock_bh(&old->lock);
++
++      if (err)
++              return err;
+       return fib6_check_nexthop(new, NULL, extack);
+ }
+@@ -2521,8 +2528,10 @@ static void __nexthop_replace_notify(struct net *net, struct nexthop *nh,
+                       fi->nh_updated = false;
+       }
++      spin_lock_bh(&nh->lock);
+       list_for_each_entry(f6i, &nh->f6i_list, nh_list)
+               fib6_rt_update(net, f6i, info);
++      spin_unlock_bh(&nh->lock);
+ }
+ /* send RTM_NEWROUTE with REPLACE flag set for all FIB entries
+-- 
+2.53.0
+
diff --git a/queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch b/queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch
new file mode 100644 (file)
index 0000000..af7964f
--- /dev/null
@@ -0,0 +1,48 @@
+From 24a4dcae24dd9a5b3ec3433ae81d60eeed4f3587 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 16:49:57 +0900
+Subject: ntfs: drop stale page-cache when shrinking a non-resident attr
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 4e646ecd44759e552b0b9ccd995f3f608daab414 ]
+
+ntfs_non_resident_attr_shrink() shrinks attribute sizes but fails to
+trim the page cache. This leaves orphaned dirty folios beyond the new
+end of the attribute, leading to writeback failures (-ENOENT), data
+loss, and $EA chain corruption.
+
+Fix this by truncating the page cache to the new size immediately after
+updating the sizes, preventing writeback from flushing out-of-range folios.
+
+Fixes: 495e90fa3348 ("ntfs: update attrib operations")
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs/attrib.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
+index 0f1d0b54cfb52..22338ad541279 100644
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -4292,6 +4292,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
+               ni->initialized_size = newsize;
+               ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize);
+       }
++
++      /*
++       * Drop any page-cache folios that now lie beyond the shrunk
++       * attribute. The clusters backing them have just been freed and the
++       * runlist truncated, so leaving stale dirty folios around makes a
++       * later writeback map a vcn past the new allocation, which fails with
++       * -ENOENT and loses the write.
++       */
++      truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
++
+       /* Update data size in the index. */
+       if (ni->type == AT_DATA && ni->name == AT_UNNAMED)
+               NInoSetFileNameDirty(ni);
+-- 
+2.53.0
+
diff --git a/queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch b/queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch
new file mode 100644 (file)
index 0000000..5c7e051
--- /dev/null
@@ -0,0 +1,109 @@
+From 61ec872dfc62a72c2694c7797a4758ba15252d39 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 14:22:57 +0900
+Subject: ntfs: harden runlist realloc size calculations
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 8bed376124ab4505b70083a2b91f2c7ef6d51e24 ]
+
+Add a shared helper to safely convert runlist element counts to byte sizes
+using overflow checks, and use it in both ntfs_rl_realloc() and
+ntfs_rl_realloc_nofail().
+
+Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator")
+Co-developed-by: Alper Mudar <kommandant_alper@proton.me>
+Signed-off-by: Alper Mudar <kommandant_alper@proton.me>
+Tested-by: Alper Mudar <kommandant_alper@proton.me>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs/runlist.c | 50 +++++++++++++++++++++++++----------------------
+ 1 file changed, 27 insertions(+), 23 deletions(-)
+
+diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
+index 15f1ae530ae13..8d2740cfddd73 100644
+--- a/fs/ntfs/runlist.c
++++ b/fs/ntfs/runlist.c
+@@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst,
+  * On success, return a pointer to the newly allocated, or recycled, memory.
+  * On error, return -errno.
+  */
+-struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
+-              int old_size, int new_size)
++static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl,
++              int old_size, int new_size, gfp_t gfp)
+ {
+       struct runlist_element *new_rl;
++      size_t new_bytes;
++
++      if (old_size < 0 || new_size < 0)
++              return ERR_PTR(-EINVAL);
+-      old_size = old_size * sizeof(*rl);
+-      new_size = new_size * sizeof(*rl);
+       if (old_size == new_size)
+               return rl;
+-      new_rl = kvzalloc(new_size, GFP_NOFS);
++      if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes))
++              return ERR_PTR(-EINVAL);
++
++      new_rl = kvzalloc(new_bytes, gfp);
+       if (unlikely(!new_rl))
+               return ERR_PTR(-ENOMEM);
+       if (likely(rl != NULL)) {
+-              if (unlikely(old_size > new_size))
+-                      old_size = new_size;
+-              memcpy(new_rl, rl, old_size);
++              size_t old_bytes;
++
++              if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) {
++                      kvfree(new_rl);
++                      return ERR_PTR(-EINVAL);
++              }
++              if (unlikely(old_bytes > new_bytes))
++                      old_bytes = new_bytes;
++              memcpy(new_rl, rl, old_bytes);
+               kvfree(rl);
+       }
+       return new_rl;
+ }
++struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
++              int old_size, int new_size)
++{
++      return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS);
++}
++
+ /*
+  * ntfs_rl_realloc_nofail - Reallocate memory for runlists
+  * @rl:               original runlist
+@@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
+ static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl,
+               int old_size, int new_size)
+ {
+-      struct runlist_element *new_rl;
+-
+-      old_size = old_size * sizeof(*rl);
+-      new_size = new_size * sizeof(*rl);
+-      if (old_size == new_size)
+-              return rl;
+-
+-      new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL);
+-      if (likely(rl != NULL)) {
+-              if (unlikely(old_size > new_size))
+-                      old_size = new_size;
+-              memcpy(new_rl, rl, old_size);
+-              kvfree(rl);
+-      }
+-      return new_rl;
++      return ntfs_rl_realloc_gfp(rl, old_size, new_size,
++                      GFP_NOFS | __GFP_NOFAIL);
+ }
+ /*
+-- 
+2.53.0
+
diff --git a/queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch b/queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch
new file mode 100644 (file)
index 0000000..363ba92
--- /dev/null
@@ -0,0 +1,48 @@
+From ff8886849d9036c0d3bedc1591f11d811ed5a2bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 11 Jul 2026 16:36:55 +0900
+Subject: ntfs: preserve RECALL_ON_OPEN on WSL special-file reparse points
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 523307b8516fc740895238af8473aa0630b3e088 ]
+
+When creating a WSL special file (socket, fifo, character or block
+device), __ntfs_create() sets FILE_ATTRIBUTE_RECALL_ON_OPEN in ni->flags
+as valid_reparse_data() requires for these tags. This flag is
+intentionally absent from $FILE_NAME, so the subsequent reload
+
+        ni->flags = fn->file_attributes;
+
+drops it from ni->flags, the authoritative copy written back to
+$STANDARD_INFORMATION. The on-disk file_attributes becomes 0x00000404
+instead of 0x00040404, and after a remount valid_reparse_data() rejects
+the reparse point while fsck reports "$REPARSE_POINT data is corrupted".
+
+Preserve the RECALL_ON_OPEN bit across the reload. Symlinks do not set
+that bit, so they are unaffected.
+
+Fixes: af0db57d4293 ("ntfs: update inode operations")
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs/namei.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
+index a20ef06087368..712c9ccc8b254 100644
+--- a/fs/ntfs/namei.c
++++ b/fs/ntfs/namei.c
+@@ -683,7 +683,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
+       mutex_unlock(&dir_ni->mrec_lock);
+       mutex_unlock(&ni->mrec_lock);
+-      ni->flags = fn->file_attributes;
++      ni->flags = fn->file_attributes |
++                  (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN);
+       /* Set the sequence number. */
+       vi->i_generation = ni->seq_no;
+       set_nlink(vi, 1);
+-- 
+2.53.0
+
diff --git a/queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch b/queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch
new file mode 100644 (file)
index 0000000..d2d3626
--- /dev/null
@@ -0,0 +1,288 @@
+From b29087c17956e53822413e11b3815137416755c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:42:29 +0530
+Subject: octeontx2-af: Block VFs from clobbering special CGX PKIND state
+
+From: Hariprasad Kelam <hkelam@marvell.com>
+
+[ Upstream commit 3bd438a58e910db5dc369aa25dfed1fc95f1b596 ]
+
+PF and VF NIX LFs that share a CGX LMAC reuse the same hardware PKIND
+programming. When HiGig2 or EDSA parsing is enabled, a VF NIX LF alloc must
+not reset the LMAC RX PKIND or default TX parse config over the PF setup.
+
+Add cgx_get_pkind() and rvu_cgx_is_pkind_config_permitted() so VFs skip
+cgx_set_pkind(), rvu_npc_set_pkind(), and NIX_AF_LFX_TX_PARSE_CFG updates
+when the LMAC is using NPC_RX_HIGIG_PKIND or NPC_RX_EDSA_PKIND.
+
+Fixes: 94d942c5fb97 ("octeontx2-af: Config pkind for CGX mapped PFs")
+Cc: Geetha sowjanya <gakula@marvell.com>
+Signed-off-by: Hariprasad Kelam <hkelam@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260722081229.1653619-1-rkannoth@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../net/ethernet/marvell/octeontx2/af/cgx.c   | 12 +++
+ .../net/ethernet/marvell/octeontx2/af/cgx.h   |  1 +
+ .../net/ethernet/marvell/octeontx2/af/rvu.h   |  2 +
+ .../ethernet/marvell/octeontx2/af/rvu_cgx.c   | 79 +++++++++++++++++++
+ .../ethernet/marvell/octeontx2/af/rvu_nix.c   | 22 +++++-
+ .../ethernet/marvell/octeontx2/af/rvu_npc.c   | 29 ++++---
+ 6 files changed, 131 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
+index 2e94d5105016b..f5fd6138c352f 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
+@@ -518,6 +518,18 @@ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind)
+       return 0;
+ }
++int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind)
++{
++      struct cgx *cgx = cgxd;
++
++      if (!is_lmac_valid(cgx, lmac_id))
++              return -ENODEV;
++
++      *pkind = cgx_read(cgx, lmac_id, cgx->mac_ops->rxid_map_offset);
++      *pkind = *pkind & 0x3F;
++      return 0;
++}
++
+ static u8 cgx_get_lmac_type(void *cgxd, int lmac_id)
+ {
+       struct cgx *cgx = cgxd;
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
+index 92ccf343dfe04..8411a75dd723f 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
+@@ -141,6 +141,7 @@ int cgx_get_cgxid(void *cgxd);
+ int cgx_get_lmac_cnt(void *cgxd);
+ void *cgx_get_pdata(int cgx_id);
+ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind);
++int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind);
+ int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id);
+ int cgx_lmac_evh_unregister(void *cgxd, int lmac_id);
+ int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat);
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+index 65397daae4c2f..635a7630238e6 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+@@ -1113,6 +1113,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
+                        u8 *intf, u8 *ena);
+ int npc_config_cntr_default_entries(struct rvu *rvu, bool enable);
+ bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc);
++bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind);
++bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc);
+ bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature);
+ u32  rvu_cgx_get_fifolen(struct rvu *rvu);
+ void *rvu_first_cgx_pdata(struct rvu *rvu);
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
+index 4ff3935ed3fe8..87d21889dc49e 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
+@@ -1355,3 +1355,82 @@ void rvu_mac_reset(struct rvu *rvu, u16 pcifunc)
+       if (mac_ops->mac_reset(cgxd, lmac, !is_vf(pcifunc)))
+               dev_err(rvu->dev, "Failed to reset MAC\n");
+ }
++
++/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds
++ * (HiGig, EDSA, etc.) are in use on the shared LMAC. VFs must not program
++ * NPC_TX_DEF_PKIND on NIX_AF_LFX_TX_PARSE_CFG in that case: the PF owns
++ * parse mode and no separate NPC_TX_HIGIG_PKIND is installed on the VF LF.
++ * TX-parse callers skip the write when denied; rvu_lf_reset() clears each LF
++ * before alloc so the next permitted owner programs NPC_TX_DEF_PKIND.
++ */
++bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc)
++{
++      int pf, err, rxpkind;
++      u8 cgx_id, lmac_id;
++      void *cgxd;
++
++      pf = rvu_get_pf(rvu->pdev, pcifunc);
++
++      if (!(pcifunc & RVU_PFVF_FUNC_MASK))
++              return true;
++
++      if (!is_pf_cgxmapped(rvu, pf))
++              return true;
++
++      rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
++      cgxd = rvu_cgx_pdata(cgx_id, rvu);
++      err = cgx_get_pkind(cgxd, lmac_id, &rxpkind);
++      if (err)
++              return false;
++
++      switch (rxpkind) {
++      case NPC_RX_HIGIG_PKIND:
++      case NPC_RX_EDSA_PKIND:
++              return false;
++      default:
++              return true;
++      }
++}
++
++/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds
++ * (HiGig, EDSA, etc.) are in use on the shared LMAC.
++ */
++bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind)
++{
++      int pf, err, rxpkind;
++      u8 cgx_id, lmac_id;
++      struct cgx *cgxd;
++
++      pf = rvu_get_pf(rvu->pdev, pcifunc);
++
++      if (!is_pf_cgxmapped(rvu, pf))
++              return false;
++
++      rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
++      cgxd = rvu_cgx_pdata(cgx_id, rvu);
++
++      mutex_lock(&cgxd->lock);
++      if (!is_vf(pcifunc))
++              goto set;
++
++      err = cgx_get_pkind(cgxd, lmac_id, &rxpkind);
++      if (err)
++              goto err;
++
++      switch (rxpkind) {
++      case NPC_RX_HIGIG_PKIND:
++      case NPC_RX_EDSA_PKIND:
++              goto err;
++      default:
++              break;
++      }
++
++set:
++      cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
++      mutex_unlock(&cgxd->lock);
++      return true;
++
++err:
++      mutex_unlock(&cgxd->lock);
++      return false;
++}
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+index 6a0ce2665031d..964bcaae098e2 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+@@ -363,8 +363,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
+               pfvf->tx_chan_cnt = 1;
+               rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id;
+-              cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
+-              rvu_npc_set_pkind(rvu, pkind, pfvf);
++              if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind))
++                      rvu_npc_set_pkind(rvu, pkind, pfvf);
+               break;
+       case NIX_INTF_TYPE_LBK:
+               vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
+@@ -1505,13 +1505,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+                                 struct nix_lf_alloc_req *req,
+                                 struct nix_lf_alloc_rsp *rsp)
+ {
+-      int nixlf, qints, hwctx_size, intf, rc = 0;
++      int nixlf, qints, hwctx_size, intf, rc = 0, pf;
+       u16 bcast, mcast, promisc, ucast;
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
++      u8 cgx_id = 0, lmac_id = 0;
+       bool rules_created = false;
+       struct rvu_block *block;
+       struct rvu_pfvf *pfvf;
++      struct cgx *cgxd;
+       u64 cfg, ctx_cfg;
+       int blkaddr;
+@@ -1685,8 +1687,20 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg);
+       /* Configure pkind for TX parse config */
++
++      pf = rvu_get_pf(rvu->pdev, pcifunc);
+       cfg = NPC_TX_DEF_PKIND;
+-      rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
++
++      if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) {
++              rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
++              cgxd = rvu_cgx_pdata(cgx_id, rvu);
++              mutex_lock(&cgxd->lock);
++              if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc))
++                      rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
++              mutex_unlock(&cgxd->lock);
++      } else {
++              rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
++      }
+       if (is_rep_dev(rvu, pcifunc)) {
+               pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN;
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+index 41d92eb652af9..1e090508f958d 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+@@ -19,6 +19,7 @@
+ #include "cn20k/npc.h"
+ #include "rvu_npc.h"
+ #include "cn20k/reg.h"
++#include "lmac_common.h"
+ #define RSVD_MCAM_ENTRIES_PER_PF      3 /* Broadcast, Promisc and AllMulticast */
+ #define RSVD_MCAM_ENTRIES_PER_NIXLF   1 /* Ucast for LFs */
+@@ -3912,10 +3913,11 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
+ {
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+-      int blkaddr, nixlf, rc, intf_mode;
+       int pf = rvu_get_pf(rvu->pdev, pcifunc);
++      int blkaddr, nixlf, rc, intf_mode;
++      u8 cgx_id = 0, lmac_id = 0;
+       u64 rxpkind, txpkind;
+-      u8 cgx_id, lmac_id;
++      struct cgx *cgxd;
+       /* use default pkind to disable edsa/higig */
+       rxpkind = rvu_npc_get_pkind(rvu, pf);
+@@ -3939,12 +3941,8 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
+               /* rx pkind set req valid only for cgx mapped PFs */
+               if (!is_cgx_config_permitted(rvu, pcifunc))
+                       return 0;
+-              rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
+-
+-              rc = cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id,
+-                                 rxpkind);
+-              if (rc)
+-                      return rc;
++              if (!rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, rxpkind))
++                      return -EINVAL;
+       }
+       if (dir & PKIND_TX) {
+@@ -3953,8 +3951,19 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
+               if (rc)
+                       return rc;
+-              rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
+-                          txpkind);
++              if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) {
++                      rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id,
++                                          &lmac_id);
++                      cgxd = rvu_cgx_pdata(cgx_id, rvu);
++                      mutex_lock(&cgxd->lock);
++                      if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc))
++                              rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
++                                          txpkind);
++                      mutex_unlock(&cgxd->lock);
++              } else {
++                      rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
++                                  txpkind);
++              }
+       }
+       pfvf->intf_mode = intf_mode;
+-- 
+2.53.0
+
diff --git a/queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch b/queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch
new file mode 100644 (file)
index 0000000..eaf0ca4
--- /dev/null
@@ -0,0 +1,319 @@
+From f6606f107ec6dc5f7ee22ed259bacc3879ca87e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 9 Jun 2026 09:34:50 +0530
+Subject: octeontx2: cn20k: Coordinate default rules with NIX LF lifecycle
+
+From: Ratheesh Kannoth <rkannoth@marvell.com>
+
+[ Upstream commit aac055dbc0fadf64c9d6fbcfc066b8ba33216dc4 ]
+
+Add NIX_LF_DONT_FREE_DFT_IDXS so the PF can send NIX LF free during hw
+reinit or teardown without the AF freeing CN20K default NPC rule indexes
+while the driver still owns that state (otx2_init_hw_resources and
+otx2_free_hw_resources).
+
+On CN20K, allocate default NPC rules from NIX LF alloc before
+nix_interface_init, roll back with npc_cn20k_dft_rules_free on failure,
+and free from NIX LF free when the new flag is not set. Tighten
+rvu_mbox_handler_nix_lf_alloc error handling: use a single rc, propagate
+qmem_alloc and other errors, and set -ENOMEM only when kcalloc fails
+(remove the blanket -ENOMEM at the free_mem path).
+
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260609040453.711932-7-rkannoth@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 3bd438a58e91 ("octeontx2-af: Block VFs from clobbering special CGX PKIND state")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../net/ethernet/marvell/octeontx2/af/mbox.h  |  1 +
+ .../ethernet/marvell/octeontx2/af/rvu_nix.c   | 77 +++++++++++++------
+ .../ethernet/marvell/octeontx2/af/rvu_npc.c   | 20 +++--
+ .../ethernet/marvell/octeontx2/nic/otx2_pf.c  |  6 +-
+ 4 files changed, 69 insertions(+), 35 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+index 44fdd6ba7307c..714e47f68d932 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
++++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+@@ -1008,6 +1008,7 @@ struct nix_lf_free_req {
+       struct mbox_msghdr hdr;
+ #define NIX_LF_DISABLE_FLOWS          BIT_ULL(0)
+ #define NIX_LF_DONT_FREE_TX_VTAG      BIT_ULL(1)
++#define NIX_LF_DONT_FREE_DFT_IDXS     BIT_ULL(2)
+       u64 flags;
+ };
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+index 219fc44ab68d4..6a0ce2665031d 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+@@ -16,6 +16,7 @@
+ #include "cgx.h"
+ #include "lmac_common.h"
+ #include "rvu_npc_hash.h"
++#include "cn20k/npc.h"
+ static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc);
+ static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req,
+@@ -1504,9 +1505,11 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+                                 struct nix_lf_alloc_req *req,
+                                 struct nix_lf_alloc_rsp *rsp)
+ {
+-      int nixlf, qints, hwctx_size, intf, err, rc = 0;
++      int nixlf, qints, hwctx_size, intf, rc = 0;
++      u16 bcast, mcast, promisc, ucast;
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
++      bool rules_created = false;
+       struct rvu_block *block;
+       struct rvu_pfvf *pfvf;
+       u64 cfg, ctx_cfg;
+@@ -1560,8 +1563,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+               return NIX_AF_ERR_RSS_GRPS_INVALID;
+       /* Reset this NIX LF */
+-      err = rvu_lf_reset(rvu, block, nixlf);
+-      if (err) {
++      rc = rvu_lf_reset(rvu, block, nixlf);
++      if (rc) {
+               dev_err(rvu->dev, "Failed to reset NIX%d LF%d\n",
+                       block->addr - BLKADDR_NIX0, nixlf);
+               return NIX_AF_ERR_LF_RESET;
+@@ -1571,13 +1574,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       /* Alloc NIX RQ HW context memory and config the base */
+       hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
+-      err = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
+-      if (err)
++      rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
++      if (rc)
+               goto free_mem;
+       pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
+-      if (!pfvf->rq_bmap)
++      if (!pfvf->rq_bmap) {
++              rc = -ENOMEM;
+               goto free_mem;
++      }
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf),
+                   (u64)pfvf->rq_ctx->iova);
+@@ -1588,13 +1593,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       /* Alloc NIX SQ HW context memory and config the base */
+       hwctx_size = 1UL << (ctx_cfg & 0xF);
+-      err = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
+-      if (err)
++      rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
++      if (rc)
+               goto free_mem;
+       pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
+-      if (!pfvf->sq_bmap)
++      if (!pfvf->sq_bmap) {
++              rc = -ENOMEM;
+               goto free_mem;
++      }
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf),
+                   (u64)pfvf->sq_ctx->iova);
+@@ -1604,13 +1611,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       /* Alloc NIX CQ HW context memory and config the base */
+       hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF);
+-      err = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
+-      if (err)
++      rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
++      if (rc)
+               goto free_mem;
+       pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
+-      if (!pfvf->cq_bmap)
++      if (!pfvf->cq_bmap) {
++              rc = -ENOMEM;
+               goto free_mem;
++      }
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf),
+                   (u64)pfvf->cq_ctx->iova);
+@@ -1620,18 +1629,18 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       /* Initialize receive side scaling (RSS) */
+       hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF);
+-      err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
+-                               req->rss_grps, hwctx_size, req->way_mask,
+-                               !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER));
+-      if (err)
++      rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
++                              req->rss_grps, hwctx_size, req->way_mask,
++                              !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER));
++      if (rc)
+               goto free_mem;
+       /* Alloc memory for CQINT's HW contexts */
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
+       qints = (cfg >> 24) & 0xFFF;
+       hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF);
+-      err = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
+-      if (err)
++      rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
++      if (rc)
+               goto free_mem;
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf),
+@@ -1644,8 +1653,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
+       qints = (cfg >> 12) & 0xFFF;
+       hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF);
+-      err = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
+-      if (err)
++      rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
++      if (rc)
+               goto free_mem;
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf),
+@@ -1689,10 +1698,22 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       if (is_sdp_pfvf(rvu, pcifunc))
+               intf = NIX_INTF_TYPE_SDP;
+-      err = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp,
+-                               !!(req->flags & NIX_LF_LBK_BLK_SEL));
+-      if (err)
+-              goto free_mem;
++      if (is_cn20k(rvu->pdev)) {
++              rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &bcast, &mcast,
++                                               &promisc, &ucast);
++              if (rc) {
++                      rc = npc_cn20k_dft_rules_alloc(rvu, pcifunc);
++                      if (rc)
++                              goto free_mem;
++
++                      rules_created = true;
++              }
++      }
++
++      rc = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp,
++                              !!(req->flags & NIX_LF_LBK_BLK_SEL));
++      if (rc)
++              goto free_dft;
+       /* Disable NPC entries as NIXLF's contexts are not initialized yet */
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
+@@ -1704,9 +1725,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
+       goto exit;
++free_dft:
++      if (is_cn20k(rvu->pdev) && rules_created)
++              npc_cn20k_dft_rules_free(rvu, pcifunc);
++
+ free_mem:
+       nix_ctx_free(rvu, pfvf);
+-      rc = -ENOMEM;
+ exit:
+       /* Set macaddr of this PF/VF */
+@@ -1780,6 +1804,9 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req,
+       nix_ctx_free(rvu, pfvf);
++      if (is_cn20k(rvu->pdev) && !(req->flags & NIX_LF_DONT_FREE_DFT_IDXS))
++              npc_cn20k_dft_rules_free(rvu, pcifunc);
++
+       return 0;
+ }
+diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+index 4994385a822b7..41d92eb652af9 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+@@ -1285,11 +1285,18 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc,
+       struct nix_mce_list *mce_list;
+       int index, blkaddr, mce_idx;
+       struct rvu_pfvf *pfvf;
++      u16 ptr[4];
+       /* multicast pkt replication is not enabled for AF's VFs & SDP links */
+       if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc))
+               return;
++      /* In cn20k, only CGX mapped devices have default MCAST entry */
++      if (is_cn20k(rvu->pdev) &&
++          npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1],
++                                      &ptr[2], &ptr[3]))
++              return;
++
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return;
+@@ -1329,9 +1336,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int index, blkaddr;
++      u16 ptr[4];
+       /* only CGX or LBK interfaces have default entries */
+-      if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc))
++      if (is_cn20k(rvu->pdev) &&
++          npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1],
++                                      &ptr[2], &ptr[3]))
+               return;
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+@@ -4075,12 +4085,10 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
+       ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                            nixlf, NIXLF_UCAST_ENTRY);
+-      if (ucast_idx < 0) {
+-              dev_err(rvu->dev,
+-                      "%s: Error to get ucast entry for pcifunc=%#x\n",
+-                      __func__, pcifunc);
++
++      /* In cn20k, default rules are freed before detach rsrc */
++      if (ucast_idx < 0)
+               return;
+-      }
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false);
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index 617a0db97e804..2e33b33ec9934 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -1053,7 +1053,6 @@ irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq)
+       /* Clear the IRQ */
+       otx2_write64(pf, RVU_PF_INT, BIT_ULL(0));
+-
+       mbox_data = otx2_read64(pf, RVU_PF_PFAF_MBOX0);
+       if (mbox_data & MBOX_UP_MSG) {
+@@ -1727,7 +1726,7 @@ int otx2_init_hw_resources(struct otx2_nic *pf)
+       mutex_lock(&mbox->lock);
+       free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox);
+       if (free_req) {
+-              free_req->flags = NIX_LF_DISABLE_FLOWS;
++              free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS;
+               if (otx2_sync_mbox_msg(mbox))
+                       dev_err(pf->dev, "%s failed to free nixlf\n", __func__);
+       }
+@@ -1801,7 +1800,7 @@ void otx2_free_hw_resources(struct otx2_nic *pf)
+       /* Reset NIX LF */
+       free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox);
+       if (free_req) {
+-              free_req->flags = NIX_LF_DISABLE_FLOWS;
++              free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS;
+               if (!(pf->flags & OTX2_FLAG_PF_SHUTDOWN))
+                       free_req->flags |= NIX_LF_DONT_FREE_TX_VTAG;
+               if (otx2_sync_mbox_msg(mbox))
+@@ -1924,7 +1923,6 @@ int otx2_alloc_queue_mem(struct otx2_nic *pf)
+       struct otx2_qset *qset = &pf->qset;
+       struct otx2_cq_poll *cq_poll;
+-
+       /* RQ and SQs are mapped to different CQs,
+        * so find out max CQ IRQs (i.e CINTs) needed.
+        */
+-- 
+2.53.0
+
diff --git a/queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
new file mode 100644 (file)
index 0000000..4ced065
--- /dev/null
@@ -0,0 +1,42 @@
+From 07eddcd95fb9d7a6456662c41c6d90343ddf9913 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 12:58:31 +0530
+Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop
+
+From: Suman Ghosh <sumang@marvell.com>
+
+[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
+
+During link down event, we were doing netif_tx_stop_all_queues() first
+and then netif_carrier_off(). This can cause a potential race since
+carrier is still on during down event. This patch reverse the calling
+order to fix the issue.
+
+Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications")
+Signed-off-by: Suman Ghosh <sumang@marvell.com>
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+index 2e33b33ec9934..c995f29008590 100644
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
+               netif_carrier_on(netdev);
+               netif_tx_start_all_queues(netdev);
+       } else {
+-              netif_tx_stop_all_queues(netdev);
+               netif_carrier_off(netdev);
++              netif_tx_stop_all_queues(netdev);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
new file mode 100644 (file)
index 0000000..18bdfe2
--- /dev/null
@@ -0,0 +1,66 @@
+From 8c3cd3851fa580efeb109dbd65c770991befc956 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 14 Jun 2026 22:38:06 +0900
+Subject: of: reserved_mem: prevent OOB when too many dynamic regions are
+ defined
+
+From: Sang-Heon Jeon <ekffu200098@gmail.com>
+
+[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ]
+
+On boot, fdt_scan_reserved_mem() saves each dynamically-placed
+/reserved-memory subnode into a local array of size
+MAX_RESERVED_REGIONS.
+
+If the device tree defines more than MAX_RESERVED_REGIONS
+dynamically-placed regions, fdt_scan_reserved_mem() writes past the
+end of the local array.
+
+Add a bounds check that logs an error and skips the excess regions,
+restoring the original behavior.
+
+Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed")
+Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
+Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com
+Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/of/of_reserved_mem.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
+index deaea58c74f2a..47041d195dff8 100644
+--- a/drivers/of/of_reserved_mem.c
++++ b/drivers/of/of_reserved_mem.c
+@@ -351,6 +351,7 @@ int __init fdt_scan_reserved_mem(void)
+               err = __reserved_mem_reserve_reg(child, uname);
+               if (!err)
+                       count++;
++
+               /*
+                * Save the nodes for the dynamically-placed regions
+                * into an array which will be used for allocation right
+@@ -358,10 +359,17 @@ int __init fdt_scan_reserved_mem(void)
+                * or marked as no-map. This is done to avoid dynamically
+                * allocating from one of the statically-placed regions.
+                */
+-              if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
+-                      dynamic_nodes[dynamic_nodes_cnt] = child;
+-                      dynamic_nodes_cnt++;
++              if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL))
++                      continue;
++
++              if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) {
++                      pr_err("too many defined dynamic regions, skip '%s'\n",
++                             uname);
++                      continue;
+               }
++
++              dynamic_nodes[dynamic_nodes_cnt] = child;
++              dynamic_nodes_cnt++;
+       }
+       for (int i = 0; i < dynamic_nodes_cnt; i++) {
+               const char *uname;
+-- 
+2.53.0
+
diff --git a/queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch b/queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch
new file mode 100644 (file)
index 0000000..dd3d437
--- /dev/null
@@ -0,0 +1,38 @@
+From 1f21100d61aa0a9c0badf56ca531da3621d834ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 15:01:31 +0530
+Subject: phy: qcom: m31-eusb2: Fix return value of init call
+
+From: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
+
+[ Upstream commit 361f533a2dce2c2841fe4dc0c9d85a67117edf95 ]
+
+The init call currently returns success irrespective of any failures
+during repeater init or clock enablement. Return appropriate error value
+in the init call failure path.
+
+Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver")
+Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+index 68f1ba8fec4ad..9434bd22ef32d 100644
+--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
++++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+@@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy)
+ disable_vreg:
+       regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs);
+-      return 0;
++      return ret;
+ }
+ static int m31eusb2_phy_exit(struct phy *uphy)
+-- 
+2.53.0
+
diff --git a/queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
new file mode 100644 (file)
index 0000000..769e8d3
--- /dev/null
@@ -0,0 +1,76 @@
+From 1fc66ed79f32976ce0c360d6584272b9e5621452 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:30 +0530
+Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
+
+Propagate clk_prepare_enable() failures to the caller instead of
+returning success, and disable the reference clock on initialization
+error paths to avoid leaking clock references when phy_exit() is not
+called.
+
+Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index fe6b4925d1662..c8230f2bda629 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy)
+ {
+       struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+-      int ret = 0;
++      int ret;
+       mutex_lock(&gtr_dev->gtr_mutex);
+       /* Configure and enable the clock when peripheral phy_init call */
+-      if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
++      ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
++      if (ret)
+               goto out;
+       /* Skip initialization if not required. */
+@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+       if (gtr_dev->tx_term_fix) {
+               ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+               if (ret < 0)
+-                      goto out;
++                      goto out_disable_clk;
+               gtr_dev->tx_term_fix = false;
+       }
+@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy)
+        */
+       ret = xpsgtr_configure_pll(gtr_phy);
+       if (ret)
+-              goto out;
++              goto out_disable_clk;
+       xpsgtr_lane_set_protocol(gtr_phy);
+@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy)
+               break;
+       }
++      goto out;
++
++out_disable_clk:
++      clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
+ out:
+       mutex_unlock(&gtr_dev->gtr_mutex);
+       return ret;
+-- 
+2.53.0
+
diff --git a/queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
new file mode 100644 (file)
index 0000000..43f7559
--- /dev/null
@@ -0,0 +1,56 @@
+From 192e9e138d4eaf6ffbe3f6fcd591f6320429e4dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 21:08:31 +0530
+Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure
+
+From: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+
+[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
+
+Allocate saved_regs before pm_runtime_resume_and_get() so a
+devm_kmalloc() failure does not leave an unreleased runtime PM usage
+counter.
+
+Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume")
+Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
+index c8230f2bda629..2138f5399821a 100644
+--- a/drivers/phy/xilinx/phy-zynqmp.c
++++ b/drivers/phy/xilinx/phy-zynqmp.c
+@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return PTR_ERR(provider);
+       }
++      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
++                                         sizeof(save_reg_address),
++                                         GFP_KERNEL);
++      if (!gtr_dev->saved_regs)
++              return -ENOMEM;
++
+       pm_runtime_set_active(gtr_dev->dev);
+       pm_runtime_enable(gtr_dev->dev);
+@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
+               return ret;
+       }
+-      gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+-                                         sizeof(save_reg_address),
+-                                         GFP_KERNEL);
+-      if (!gtr_dev->saved_regs)
+-              return -ENOMEM;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
new file mode 100644 (file)
index 0000000..e02e736
--- /dev/null
@@ -0,0 +1,58 @@
+From 713235a971bc50bfd57e6cce4e1ccd54ad9f19a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:28:44 -0500
+Subject: pinctrl-amd: Don't clear S4 wake bits at probe
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
+
+commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+introduced a regression where Wake-on-LAN no longer works after suspend
+or shutdown on some AMD platforms.
+
+Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI
+PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake
+sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME
+does not use GPIO IRQ infrastructure and relies on firmware configuration.
+
+The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake
+bits on probe again") was to clear spurious wake bits left by firmware
+to prevent unwanted wakeups. However, S4 wake bits are used for
+hardware-level wake sources like WoL that bypass the kernel's IRQ wake
+API.
+
+Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
+- Firmware-configured S4 wake sources (WoL) continue working
+- Kernel maintains control of S3/S0i3 wake policy via set_wake()
+- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
+  amd: Take suspend type into consideration which pins are non-wake")
+
+The trade-off is that firmware-programmed spurious S4 wake bits remain
+set, but this is less problematic than breaking WoL.
+
+Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/pinctrl-amd.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
+index e3128b0045d22..15a398bb3be23 100644
+--- a/drivers/pinctrl/pinctrl-amd.c
++++ b/drivers/pinctrl/pinctrl-amd.c
+@@ -884,8 +884,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)
+       u32 pin_reg, mask;
+       int i;
+-      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) |
+-              BIT(WAKE_CNTRL_OFF_S4);
++      mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
+       for (i = 0; i < desc->npins; i++) {
+               int pin = desc->pins[i].number;
+-- 
+2.53.0
+
diff --git a/queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
new file mode 100644 (file)
index 0000000..4935a66
--- /dev/null
@@ -0,0 +1,58 @@
+From 780b8c510367bc77bd0c14a8f76f8766c0014178 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 15:08:05 +0200
+Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
+
+From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+
+[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
+
+Pins 143 and 151 were not included in the PDC wakeup map. They are
+normally used for PCIe2A and PCIe3a PERST# respectively, so they're
+unlikely to be excercised in practice, but still add them for the sake
+of completeness.
+
+Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver")
+Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+index 4056b9fa32f8c..e018bd11626ca 100644
+--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c
+@@ -1881,16 +1881,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = {
+       { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 },
+       { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 },
+       { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 },
+-      { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 },
+-      { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 },
+-      { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 },
+-      { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 },
+-      { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 },
+-      { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 },
+-      { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 },
+-      { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 },
+-      { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 },
+-      { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 },
++      { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 },
++      { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 },
++      { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 },
++      { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 },
++      { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 },
++      { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 },
++      { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 },
++      { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 },
++      { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 },
++      { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 },
++      { 225, 196 }, { 227, 197 },
+ };
+ static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
+-- 
+2.53.0
+
diff --git a/queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
new file mode 100644 (file)
index 0000000..c3fd090
--- /dev/null
@@ -0,0 +1,70 @@
+From bfb457d9a75fea41516001316227400ed9fa87e5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 17:24:53 +0530
+Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable
+
+From: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+
+[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ]
+
+GPIO interrupts that are wakeup capable need to be forwarded to wakeup
+capable parent irqchip. This is done via writing to it's wakeup_enable bit.
+
+Currently the bit is set only for PDC irqchip by checking skip_wake_irqs.
+skip_wake_irqs is set to differentiate between parent irqchips MPM and
+PDC. It is set when the parent irqchip is PDC to inform pinctrl about
+skipping the IRQ setting up at TLMM.
+
+However, the functionality to forward GPIO interrupts during SoC low
+power mode is needed regardless of which parent irqchip it is.
+Without the functionality it is impossible for MPM irqchip to detect the
+GPIO interrupt during SoC low power mode since for MPM irqchip the
+skip_wake_irqs is always false.
+
+Remove skip_wake_irqs condition when setting wakeup enable bit to allow
+forwarding GPIO interrupts for SoCs using MPM irqchip too.
+
+Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits")
+Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
+Reviewed-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
+index 45b3a2763eb85..6a24f9b5e4a97 100644
+--- a/drivers/pinctrl/qcom/pinctrl-msm.c
++++ b/drivers/pinctrl/qcom/pinctrl-msm.c
+@@ -1242,12 +1242,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d)
+       /*
+        * If the wakeup_enable bit is present and marked as available for the
+        * requested GPIO, it should be enabled when the GPIO is marked as
+-       * wake irq in order to allow the interrupt event to be transfered to
+-       * the PDC HW.
++       * wake irq in order to allow the interrupt event to be transferred to
++       * the PDC/MPM HW.
+        * While the name implies only the wakeup event, it's also required for
+        * the interrupt event.
+        */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+@@ -1275,7 +1275,7 @@ static void msm_gpio_irq_relres(struct irq_data *d)
+       unsigned long flags;
+       /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */
+-      if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) {
++      if (g->intr_wakeup_present_bit) {
+               u32 intr_cfg;
+               raw_spin_lock_irqsave(&pctrl->lock, flags);
+-- 
+2.53.0
+
diff --git a/queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
new file mode 100644 (file)
index 0000000..550992e
--- /dev/null
@@ -0,0 +1,38 @@
+From eb20d069aeef686a828030cae92152366da6d6bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:55 +0200
+Subject: powerpc/boot: Fix simpleboot CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/simpleboot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
+index c80691d83880b..27591df41e9e8 100644
+--- a/arch/powerpc/boot/simpleboot.c
++++ b/arch/powerpc/boot/simpleboot.c
+@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+       /* finally, setup the timebase */
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
new file mode 100644 (file)
index 0000000..e58ee86
--- /dev/null
@@ -0,0 +1,38 @@
+From 919eff34cfecf66bfbb55123308e0dc08ad50e77 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:57 +0200
+Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-akebono.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c
+index e3cc2599869cc..1b529037480fb 100644
+--- a/arch/powerpc/boot/treeboot-akebono.c
++++ b/arch/powerpc/boot/treeboot-akebono.c
+@@ -146,7 +146,7 @@ void platform_init(char *userdata)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
new file mode 100644 (file)
index 0000000..f905ab8
--- /dev/null
@@ -0,0 +1,38 @@
+From 2ce0e670c80cf4b19e979c1f5832b15f300cd9ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jul 2026 23:15:56 +0200
+Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
+
+fdt_node_offset_by_prop_value() returns a negative error code on
+failure - fix the check accordingly.
+
+Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform")
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/boot/treeboot-currituck.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c
+index d53e8a592f816..5b5363b74f9f3 100644
+--- a/arch/powerpc/boot/treeboot-currituck.c
++++ b/arch/powerpc/boot/treeboot-currituck.c
+@@ -102,7 +102,7 @@ void platform_init(void)
+       node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type",
+                                            "cpu", sizeof("cpu"));
+-      if (!node)
++      if (node < 0)
+               fatal("Cannot find cpu node\n");
+       timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size);
+       if (timebase && (size == 4))
+-- 
+2.53.0
+
diff --git a/queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch b/queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch
new file mode 100644 (file)
index 0000000..da83bb6
--- /dev/null
@@ -0,0 +1,121 @@
+From 56e27b4f5b22e1faa973747c169326205eb0d0d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 14:03:48 +0800
+Subject: ptp: netc: fix potential interrupt storm caused by incorrect unbind
+ order
+
+From: Wei Fang <wei.fang@nxp.com>
+
+[ Upstream commit 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea ]
+
+In netc_timer_remove(), hardware interrupts are disabled by clearing
+TMR_TEMASK before ptp_clock_unregister() is called. This may cause a
+race condition during driver unbind that could leave hardware interrupts
+active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable
+TMR_TEMASK after it has been cleared, leaving a pending hardware
+interrupt when the driver unbinds.
+
+Since the NETC Timer does not support PCIe FLR, hardware state is not
+reset during probe. When the driver is rebound and the IRQ is registered,
+the pending interrupt fires immediately. At that point priv->tmr_emask
+is still zero, so netc_timer_isr() does not clear the interrupt status
+and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible
+infinite interrupt storm.
+
+Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so
+it is not enabled when request_irq() runs, and clear TMR_TEMASK in
+netc_timer_init() before enabling it. The IRQ is only enabled at the end
+of probe once the timer has been reprogrammed and the PTP clock has been
+registered. This ensures a stale pending interrupt from a previous unbind
+or an unclean shutdown cannot be delivered before the driver is fully
+initialized.
+
+Second, in netc_timer_remove() call disable_irq() before
+ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after
+it. disable_irq() masks the line and waits for any in-flight
+netc_timer_isr() to finish, so no ISR can dereference priv->clock after
+ptp_clock_unregister() has freed it. Unregistering the PTP clock before
+clearing the mask also guarantees that no in-flight or concurrent ioctl
+can re-enable hardware interrupts.
+
+Finally, return IRQ_NONE from netc_timer_isr() when the masked event
+status is zero, so the kernel's spurious interrupt detection can disable
+a stuck line instead of looping forever.
+
+Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com
+Signed-off-by: Wei Fang <wei.fang@nxp.com>
+Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ptp/ptp_netc.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
+index 5e381c354d746..1c20d7efab929 100644
+--- a/drivers/ptp/ptp_netc.c
++++ b/drivers/ptp/ptp_netc.c
+@@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv)
+                  TMR_CTRL_TE | TMR_CTRL_FS;
+       netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+       netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
++      netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
+       /* Disable FIPER by default */
+       fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+@@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
+       /* Clear interrupts status */
+       netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
++      if (!tmr_event) {
++              spin_unlock(&priv->lock);
++              return IRQ_NONE;
++      }
++
+       if (tmr_event & TMR_TEVENT_ALMEN(0))
+               netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+@@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv)
+       }
+       priv->irq = pci_irq_vector(pdev, 0);
+-      err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv);
++      err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN,
++                        priv->irq_name, priv);
+       if (err) {
+               dev_err(&pdev->dev, "request_irq() failed\n");
+               pci_free_irq_vectors(pdev);
+@@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv)
+ {
+       struct pci_dev *pdev = priv->pdev;
+-      disable_irq(priv->irq);
+       free_irq(priv->irq, priv);
+       pci_free_irq_vectors(pdev);
+ }
+@@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev,
+               goto free_msix_irq;
+       }
++      enable_irq(priv->irq);
++
+       return 0;
+ free_msix_irq:
+@@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev)
+ {
+       struct netc_timer *priv = pci_get_drvdata(pdev);
++      disable_irq(priv->irq);
++      ptp_clock_unregister(priv->clock);
+       netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
+       netc_timer_wr(priv, NETC_TMR_CTRL, 0);
+-      ptp_clock_unregister(priv->clock);
+       netc_timer_free_msix_irq(priv);
+       netc_timer_pci_remove(pdev);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
new file mode 100644 (file)
index 0000000..8d8ecb6
--- /dev/null
@@ -0,0 +1,169 @@
+From a807f3e3345e7f1d055b7ae31e48f7bd5d1c6a99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 12:43:11 +0200
+Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path
+
+From: Denis V. Lunev <den@openvz.org>
+
+[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
+
+A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
+configured wedges the rtnetlink control plane of the whole machine:
+
+  NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
+  [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
+  [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
+
+The recovery path deadlocks on the driver's own mutex:
+
+  qede_sp_task
+   rtnl_lock()
+   mutex_lock(&edev->qede_lock)        <- taken
+   qede_recovery_handler
+    qede_load
+    udp_tunnel_nic_reset_ntf
+     __udp_tunnel_nic_device_sync
+      info->sync_table == qede_udp_tunnel_sync
+       mutex_lock(&edev->qede_lock)    <- same task: deadlock
+
+The mutex is not recursive, so the kworker blocks on itself with
+rtnl_lock held, and neither lock is ever released. Every task that
+calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
+addrconf, sshd) blocks forever while the node still answers ping.
+In a vmcore from an affected production node rtnl_mutex.owner
+decodes to the very kworker blocked at the innermost mutex_lock()
+above.
+
+Re-sync the tunnel ports from qede_sp_task() after the internal lock
+is dropped, still under rtnl_lock as the udp_tunnel API requires.
+This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
+under rtnl without the internal lock.
+
+qede_recovery_handler() now returns whether it has successfully
+reloaded an open device, and the caller re-syncs the ports only in
+that case. This keeps the old gating exactly: a device that was down
+or a failed recovery returns false, as those paths never reached the
+udp_tunnel_nic_reset_ntf() call before either.
+
+This was the only user of the qede_lock()/qede_unlock() helpers, so
+remove them.
+
+Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra")
+Signed-off-by: Denis V. Lunev <den@openvz.org>
+CC: Andrew Lunn <andrew+netdev@lunn.ch>
+CC: "David S. Miller" <davem@davemloft.net>
+CC: Eric Dumazet <edumazet@google.com>
+CC: Jakub Kicinski <kuba@kernel.org>
+CC: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++----------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
+index 39842eb73bc33..da92c580a3dcf 100644
+--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
+@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev);
+ static void qede_shutdown(struct pci_dev *pdev);
+ static void qede_link_update(void *dev, struct qed_link_output *link);
+ static void qede_schedule_recovery_handler(void *dev);
+-static void qede_recovery_handler(struct qede_dev *edev);
++static bool qede_recovery_handler(struct qede_dev *edev);
+ static void qede_schedule_hw_err_handler(void *dev,
+                                        enum qed_hw_err_type err_type);
+ static void qede_get_eth_tlv_data(void *edev, void *data);
+@@ -1043,21 +1043,6 @@ void __qede_unlock(struct qede_dev *edev)
+       mutex_unlock(&edev->qede_lock);
+ }
+-/* This version of the lock should be used when acquiring the RTNL lock is also
+- * needed in addition to the internal qede lock.
+- */
+-static void qede_lock(struct qede_dev *edev)
+-{
+-      rtnl_lock();
+-      __qede_lock(edev);
+-}
+-
+-static void qede_unlock(struct qede_dev *edev)
+-{
+-      __qede_unlock(edev);
+-      rtnl_unlock();
+-}
+-
+ static void qede_periodic_task(struct work_struct *work)
+ {
+       struct qede_dev *edev = container_of(work, struct qede_dev,
+@@ -1094,6 +1079,8 @@ static void qede_sp_task(struct work_struct *work)
+        */
+       if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
++              bool reloaded;
++
+               cancel_delayed_work_sync(&edev->periodic_task);
+ #ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+@@ -1102,9 +1089,17 @@ static void qede_sp_task(struct work_struct *work)
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+ #endif
+-              qede_lock(edev);
+-              qede_recovery_handler(edev);
+-              qede_unlock(edev);
++              rtnl_lock();
++              __qede_lock(edev);
++              reloaded = qede_recovery_handler(edev);
++              __qede_unlock(edev);
++
++              /* The udp_tunnel core synchronously calls back into
++               * qede_udp_tunnel_sync(), which takes the qede lock.
++               */
++              if (reloaded)
++                      udp_tunnel_nic_reset_ntf(edev->ndev);
++              rtnl_unlock();
+       }
+       __qede_lock(edev);
+@@ -2645,9 +2640,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
+               edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
+ }
+-static void qede_recovery_handler(struct qede_dev *edev)
++/* Returns true if an open device was successfully reloaded and its
++ * udp_tunnel ports need to be re-synced by the caller.
++ */
++static bool qede_recovery_handler(struct qede_dev *edev)
+ {
+       u32 curr_state = edev->state;
++      bool reloaded = false;
+       int rc;
+       DP_NOTICE(edev, "Starting a recovery process\n");
+@@ -2677,17 +2676,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
+                       goto err;
+               qede_config_rx_mode(edev->ndev);
+-              udp_tunnel_nic_reset_ntf(edev->ndev);
++              reloaded = true;
+       }
+       edev->state = curr_state;
+       DP_NOTICE(edev, "Recovery handling is done\n");
+-      return;
++      return reloaded;
+ err:
+       qede_recovery_failed(edev);
++      return false;
+ }
+ static void qede_atomic_hw_err_handler(struct qede_dev *edev)
+-- 
+2.53.0
+
diff --git a/queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
new file mode 100644 (file)
index 0000000..7aabea0
--- /dev/null
@@ -0,0 +1,85 @@
+From 718ae7acacf5cb69516e50da118ca88ba7b98796 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:02:03 -0700
+Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in
+ rds_tcp_laddr_check()
+
+From: Xiang Mei <xmei5@asu.edu>
+
+[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
+
+rds_tcp_laddr_check() looks up a scoped IPv6 interface with
+dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
+passes the bare struct net_device * into ipv6_chk_addr().
+
+dev_get_by_index_rcu() only keeps the device alive within the same RCU
+read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
+free the net_device; ipv6_chk_addr() then dereferences the stale pointer
+in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
+freed memory.
+
+Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
+of dropping it right after the lookup, so the device cannot be freed
+while it is in use.
+
+  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+  Read of size 8 at addr ffff8880106ec000 by task exploit/153
+  Call Trace:
+   ...
+   kasan_report (mm/kasan/report.c:595)
+   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
+   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
+   rds_tcp_laddr_check (net/rds/tcp.c:370)
+   rds_bind (net/rds/bind.c:248)
+   __sys_bind (net/socket.c:1920)
+   __x64_sys_bind (net/socket.c:1956)
+   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
+   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
+
+Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
+Reported-by: Weiming Shi <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Allison Henderson <achender@kernel.org>
+Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rds/tcp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/rds/tcp.c b/net/rds/tcp.c
+index 76f441cbf50e6..c20dc6d5ca65d 100644
+--- a/net/rds/tcp.c
++++ b/net/rds/tcp.c
+@@ -336,23 +336,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+       /* If the scope_id is specified, check only those addresses
+        * hosted on the specified interface.
+        */
++      rcu_read_lock();
+       if (scope_id != 0) {
+-              rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, scope_id);
+               /* scope_id is not valid... */
+               if (!dev) {
+                       rcu_read_unlock();
+                       return -EADDRNOTAVAIL;
+               }
+-              rcu_read_unlock();
+       }
+ #if IS_ENABLED(CONFIG_IPV6)
+       if (ipv6_mod_enabled()) {
+               ret = ipv6_chk_addr(net, addr, dev, 0);
+-              if (ret)
++              if (ret) {
++                      rcu_read_unlock();
+                       return 0;
++              }
+       }
+ #endif
++      rcu_read_unlock();
+       return -EADDRNOTAVAIL;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch b/queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch
new file mode 100644 (file)
index 0000000..014db15
--- /dev/null
@@ -0,0 +1,39 @@
+From 69cea598c41edc75956f0032e3012026ce95fccc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 14:36:09 +0100
+Subject: ring-buffer: Fix reader page read offset for remote buffers
+
+From: Vincent Donnefort <vdonnefort@google.com>
+
+[ Upstream commit 78cd56c2a9d2e8da763cea3b06b636266ca66911 ]
+
+A page swapped in by __rb_get_reader_page_from_remote() retains its
+stale read offset, causing subsequent reads to skip events or read
+past valid data. Fix it.
+
+Link: https://patch.msgid.link/20260729133609.4022734-1-vdonnefort@google.com
+Fixes: fbd1743ecba1 ("ring-buffer: Add non-consuming read for ring-buffer remotes")
+Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
+Reviewed-by: Keir Fraser <keirf@google.com>
+Tested-by: Keir Fraser <keirf@google.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/ring_buffer.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index da9339466bddd..d896fb25de702 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -5624,6 +5624,7 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
+       cpu_buffer->head_page = new_head;
+       cpu_buffer->reader_page = new_reader;
++      cpu_buffer->reader_page->read = 0;
+       cpu_buffer->pages = &new_head->list;
+       cpu_buffer->read_stamp = new_reader->page->time_stamp;
+       cpu_buffer->lost_events = cpu_buffer->meta_page->reader.lost_events;
+-- 
+2.53.0
+
diff --git a/queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch b/queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch
new file mode 100644 (file)
index 0000000..5d66dce
--- /dev/null
@@ -0,0 +1,48 @@
+From 4de2b9b360f19037d347b95263da544234291391 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Jul 2026 23:16:46 +0900
+Subject: ring-buffer: Fix subbuf_ids memory leak in rb_allocate_cpu_buffer()
+ error path
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 260b20d9b78bf002f89088fb62d60e8dee98f6f8 ]
+
+In rb_allocate_cpu_buffer(), cpu_buffer->subbuf_ids is allocated using
+kcalloc() when buffer->remote is non-NULL. If a subsequent page allocation
+fails (e.g., ring_buffer_desc_page() returns NULL or rb_allocate_pages()
+fails), execution jumps to fail_free_reader.
+
+While __free(kfree) automatically frees the outer cpu_buffer structure
+at scope exit, kfree(cpu_buffer) does not recursively free nested heap
+pointers such as cpu_buffer->subbuf_ids, resulting in a memory leak.
+
+Fix this by explicitly freeing cpu_buffer->subbuf_ids in the
+fail_free_reader error unwinding path when cpu_buffer->remote is set.
+
+Link: https://patch.msgid.link/178550740672.380917.6067449683620196150.stgit@devnote2
+Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/ring_buffer.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
+index d896fb25de702..d2e2cf18bfdbe 100644
+--- a/kernel/trace/ring_buffer.c
++++ b/kernel/trace/ring_buffer.c
+@@ -2489,6 +2489,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
+       return_ptr(cpu_buffer);
+  fail_free_reader:
++      kfree(cpu_buffer->subbuf_ids);
+       free_buffer_page(cpu_buffer->reader_page);
+       return NULL;
+-- 
+2.53.0
+
diff --git a/queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch b/queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch
new file mode 100644 (file)
index 0000000..eba848a
--- /dev/null
@@ -0,0 +1,80 @@
+From e6e24bcae268e85f8a367ded25a9d731d7f2a493 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Jun 2026 11:24:43 -0500
+Subject: riscv: drop __init from vec_check_unaligned_access_speed_all_cpus
+
+From: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
+
+[ Upstream commit f51fed61eea0daba2f95f1a6074085e4cd513c7b ]
+
+This function runs within a kthread and need not necessarily finish
+before system finishes boot and free_initmem() unmaps the .init.text
+section. This function makes calls to SBI for probing unaligned access
+speed, and if this is slow for some reason (say some debug prints were
+added to SBI), the kthread can still be running at this point and result
+in an instruction page fault when trying to fetch from the freed region.
+
+[   25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8
+[   25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000
+[   25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3
+[   25.661244] Oops [#1]
+[   25.662997] Modules linked in:
+[   25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY
+[   25.674669] Hardware name: Tenstorrent Blackhole (DT)
+[   25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.683458]  ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20
+[   25.693874]  gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000
+[   25.699401]  t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30
+[   25.704912]  s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002
+[   25.710407]  a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000
+[   25.715917]  a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30
+[   25.721419]  s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0
+[   25.726937]  s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000
+[   25.732450]  s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000
+[   25.737944]  s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402
+[   25.743481]  t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000
+[   25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c
+[   25.755060] [<ffffffff80a04ef8>] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c
+[   25.760964] [<ffffffff80047a10>] kthread+0xd8/0xfc
+[   25.764660] [<ffffffff80010c48>] ret_from_fork_kernel+0x18/0x1c4
+[   25.769220] [<ffffffff80895fe6>] ret_from_fork_kernel_asm+0x16/0x18
+[   25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc
+
+Drop __init from its signature so that this doesn't happen.
+
+Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions")
+Signed-off-by: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
+Assisted-by: Claude:claude-opus-4-6
+Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/kernel/unaligned_access_speed.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c
+index 11c781a4de733..557d661364ac5 100644
+--- a/arch/riscv/kernel/unaligned_access_speed.c
++++ b/arch/riscv/kernel/unaligned_access_speed.c
+@@ -314,7 +314,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus
+ }
+ /* Measure unaligned access speed on all CPUs present at boot in parallel. */
+-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+ {
+       schedule_on_each_cpu(check_vector_unaligned_access);
+       riscv_hwprobe_complete_async_probe();
+@@ -322,7 +322,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway
+       return 0;
+ }
+ #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */
+-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+ {
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
new file mode 100644 (file)
index 0000000..e04a292
--- /dev/null
@@ -0,0 +1,63 @@
+From 4a30c54a465f8eafe4609ad9e8484a4a35ef0bcf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 03:21:32 +0200
+Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove
+
+From: Karl Mehltretter <kmehltretter@gmail.com>
+
+[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ]
+
+remove_pud_mapping() and remove_p4d_mapping() obtain a child table base
+with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for
+addr.
+
+RISC-V folds page-table levels at runtime. When a level is folded, its
+offset helper returns the parent entry itself, but the index can still be
+nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39
+folds both P4D and PUD, so memory hot-remove can descend into unrelated
+memory and pass an invalid page to __free_pages(). This can trigger:
+
+  kernel BUG at include/linux/mm.h:1810!
+  VM_BUG_ON_PAGE(page_ref_count(page) == 0)
+  arch_remove_memory+0x1e/0x5c
+  try_remove_memory+0x15e/0x200
+  remove_memory+0x24/0x3c
+
+Only add the index when the corresponding page-table level is enabled,
+matching p4d_offset() and pud_offset().
+
+Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
+Assisted-by: Claude:claude-fable-5
+Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
+Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/mm/init.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index 885f1db4e9bfd..6aae507d8e818 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -1629,7 +1629,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = pud_addr_end(addr, end);
+-              pudp = pud_base + pud_index(addr);
++              pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base;
+               pud = pudp_get(pudp);
+               if (!pud_present(pud))
+                       continue;
+@@ -1660,7 +1660,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un
+       for (; addr < end; addr = next) {
+               next = p4d_addr_end(addr, end);
+-              p4dp = p4d_base + p4d_index(addr);
++              p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base;
+               p4d = p4dp_get(p4dp);
+               if (!p4d_present(p4d))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch b/queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch
new file mode 100644 (file)
index 0000000..5fd245a
--- /dev/null
@@ -0,0 +1,42 @@
+From b7bcca815bdd22b9259bc1f0a0756f6f41b66d80 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Jul 2026 08:49:00 +0200
+Subject: riscv: vdso: Only try to install vDSO when present
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
+
+[ Upstream commit c052927905710de1ab7364bf1925efbd12517ea3 ]
+
+vdso.so.dbg is only built with CONFIG_MMU.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202607090258.iSAUYlO1-lkp@intel.com/
+Fixes: f157d411a9eb ("riscv: add missing vdso_install target")
+Fixes: 3edf39916977 ("vDSO, kbuild: Provide vDSO debug variants at runtime")
+Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
+Link: https://patch.msgid.link/20260709-riscv-install-vdso-v1-1-0ba4345419ca@linutronix.de
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index ce0cc737f8709..1363e5bef35cd 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -168,7 +168,7 @@ vdso_prepare: prepare0
+ endif
+ endif
+-vdso-install-y                        += arch/riscv/kernel/vdso/vdso.so.dbg
++vdso-install-$(CONFIG_MMU)            += arch/riscv/kernel/vdso/vdso.so.dbg
+ vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg
+ vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg
+-- 
+2.53.0
+
diff --git a/queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
new file mode 100644 (file)
index 0000000..d60e92d
--- /dev/null
@@ -0,0 +1,47 @@
+From eddaf4a1dc2ec8491987c0a83d31c1a07bcfb172 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 10:38:36 +0800
+Subject: rtase: fix double free of multi-frag skb on DMA map failure
+
+From: Yun Lu <luyun@kylinos.cn>
+
+[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ]
+
+In rtase_start_xmit(), when the head buffer DMA mapping fails after
+rtase_xmit_frags() has mapped all fragments, the error path clears
+the fragment descriptors with rtase_tx_clear_range(), which frees
+the skb through the last-frag slot and accounts tx_dropped. Control
+then falls through to the common error label, which frees the same
+skb a second time and counts it again.
+
+Return right after clearing the fragments when the skb owns frags;
+the no-frag case still drops through and frees the head skb once.
+
+Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function")
+Signed-off-by: Yun Lu <luyun@kylinos.cn>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Reviewed-by: Justin Lai <justinlai0215@realtek.com>
+Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+index a57a525327a3b..bc9b14614f7a7 100644
+--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
+@@ -1620,6 +1620,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
+ err_dma_1:
+       ring->skbuff[entry] = NULL;
+       rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
++      if (frags)
++              /* the frags were cleared above, along with the skb */
++              return NETDEV_TX_OK;
+ err_dma_0:
+       tp->stats.tx_dropped++;
+-- 
+2.53.0
+
diff --git a/queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch b/queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch
new file mode 100644 (file)
index 0000000..71ba2df
--- /dev/null
@@ -0,0 +1,62 @@
+From 77ced2c70a3982e202d59b9171f3bc55b41d7057 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 16:10:47 +0200
+Subject: rtla/timerlat_top: Fix on-threshold actions firing on signal
+
+From: Tomas Glozar <tglozar@redhat.com>
+
+[ Upstream commit fafb66e5903c2bcfc7b7e259042a8282f18a6faa ]
+
+A bug was reported when rtla-timerlat-top tool performs on-threshold
+actions, even though no threshold was hit. This is reproduced even if no
+threshold is set at all:
+
+$ rtla timerlat top -q -c 0 --on-threshold shell,command='echo BAD'
+BAD
+                                     Timer Latency
+...
+
+The bug is due to incorrect logic in timerlat_top_bpf_main_loop().
+The loop uses timerlat_bpf_wait(), the return values of which are:
+
+- > 0 (number of ringbuffer entries): at least 1 CPU hit threshold
+- = 0: time out
+- < 0: wait was interrupted by a signal
+
+Commit 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero
+wait_retval") changed the condition for "threshold hit" from
+"wait_reval == 1" (exactly 1 CPU hit threshold) to "wait_retval != 0",
+to fix a race where multiple CPUs hit the threshold at the same time.
+
+That also made it incorrectly include a signal (< 0), coming from either
+duration expired (SIGALRM) or user interrupt (SIGINT).
+
+Check for wait_retval greater than zero in the if condition to cover all
+return values correctly.
+
+Fixes: 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval")
+Reported-by: Attila Fazekas <afazekas@redhat.com>
+Reviewed-by: Wander Lairson Costa <wander@redhat.com>
+Link: https://lore.kernel.org/r/20260713141047.687877-1-tglozar@redhat.com
+Signed-off-by: Tomas Glozar <tglozar@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/tracing/rtla/src/timerlat_top.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
+index 035abf01dbe63..d625b76a31679 100644
+--- a/tools/tracing/rtla/src/timerlat_top.c
++++ b/tools/tracing/rtla/src/timerlat_top.c
+@@ -821,7 +821,7 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool)
+               if (!params->quiet)
+                       timerlat_print_stats(tool);
+-              if (wait_retval != 0) {
++              if (wait_retval > 0) {
+                       /* Stopping requested by tracer */
+                       retval = common_threshold_handler(tool);
+                       if (retval)
+-- 
+2.53.0
+
diff --git a/queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
new file mode 100644 (file)
index 0000000..43cd699
--- /dev/null
@@ -0,0 +1,46 @@
+From eced13eaad18f35ea16531c0e8e4de10dcd05a99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 14:58:33 +0200
+Subject: sched/deadline: Use revised wakeup rule only for running dl_server
+
+From: Gabriele Monaco <gmonaco@redhat.com>
+
+[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ]
+
+Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for
+dl_server") applies the revised wakeup rule to any server, as a result
+servers that are not running (dl_defer_running == 0) and start with a
+deadline overflow get enqueued and can boost tasks as if they were
+running, invalidating the defer rule and the documented state model.
+
+Apply the revised wakeup rule only for deferrable servers that are
+marked as running.
+
+Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server")
+Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Acked-by: Juri Lelli <juri.lelli@redhat.com>
+Tested-by: Andrea Righi <arighi@nvidia.com>
+Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/sched/deadline.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
+index 7db4c87df83b0..e5a7701a8af71 100644
+--- a/kernel/sched/deadline.c
++++ b/kernel/sched/deadline.c
+@@ -1017,7 +1017,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se)
+       if (dl_time_before(dl_se->deadline, rq_clock(rq)) ||
+           dl_entity_overflow(dl_se, rq_clock(rq))) {
+-              if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) &&
++              if (unlikely((!dl_is_implicit(dl_se) ||
++                            (dl_se->dl_defer && dl_se->dl_defer_running)) &&
+                            !dl_time_before(dl_se->deadline, rq_clock(rq)) &&
+                            !is_dl_boosted(dl_se))) {
+                       update_dl_revised_wakeup(dl_se, rq);
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
new file mode 100644 (file)
index 0000000..2d299d4
--- /dev/null
@@ -0,0 +1,56 @@
+From 70103f0fe2173bcc5792f189a2b91b2a9821d445 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 19:49:34 +0900
+Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
+
+iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the
+target-supplied data segment.  The segment carries a 2-byte sense length
+followed by the sense bytes, so it must hold 2 + senselen bytes, but the
+bounds check only requires datalen >= senselen:
+
+       senselen = get_unaligned_be16(data);
+       if (datalen < senselen)
+               goto invalid_datalen;
+       memcpy(sc->sense_buffer, data + 2,
+              min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+
+A target that returns a SCSI Response whose datalen equals senselen
+(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data +
+2 read up to two bytes past the received data.  Those bytes are stale
+conn->data contents and end up in the command's sense buffer, which is
+returned to userspace.
+
+Account for the 2-byte sense length prefix in the check.
+
+Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi")
+Suggested-by: Sashiko AI <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 25857d6ed6e84..1b4e6af37c927 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+               }
+               senselen = get_unaligned_be16(data);
+-              if (datalen < senselen)
++              if (datalen < senselen + 2)
+                       goto invalid_datalen;
+               memcpy(sc->sense_buffer, data + 2,
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
new file mode 100644 (file)
index 0000000..968a091
--- /dev/null
@@ -0,0 +1,71 @@
+From 8251e1f36c006a6972806878cd0389edc1ad3e23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:58:48 +0900
+Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the
+ connection buffer
+
+From: HyeongJun An <sammiee5311@gmail.com>
+
+[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
+
+iscsi_tcp_hdr_dissect() receives the data segment of several PDU types
+into the fixed-size conn->data buffer, which is allocated for
+ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes.  For the LOGIN_RSP, TEXT_RSP,
+REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU
+whose DataSegmentLength exceeds that buffer.
+
+The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its
+data segment (sense/response data) into conn->data via
+iscsi_tcp_data_recv_prep(), but it does so without the same check.  The
+only upstream bound on in.datalen is conn->max_recv_dlength, the
+initiator's advertised MaxRecvDataSegmentLength, which is commonly
+negotiated well above 8192 (open-iscsi defaults to 262144).  A target
+that returns a SCSI Response with a DataSegmentLength between 8193 and
+max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
+
+Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly
+like those responses: bound the data segment, receive it into conn->data
+when present, and otherwise complete the PDU with no data.  Fold the
+opcode into that case group rather than duplicating the check.
+
+Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld")
+Suggested-by: Chris Leech <cleech@redhat.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
+Acked-by: Chris Leech <cleech@redhat.com>
+Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/libiscsi_tcp.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
+index e90805ba868fb..7223bb18b0480 100644
+--- a/drivers/scsi/libiscsi_tcp.c
++++ b/drivers/scsi/libiscsi_tcp.c
+@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+               spin_unlock(&conn->session->back_lock);
+               break;
+-      case ISCSI_OP_SCSI_CMD_RSP:
+-              if (tcp_conn->in.datalen) {
+-                      iscsi_tcp_data_recv_prep(tcp_conn);
+-                      return 0;
+-              }
+-              rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+-              break;
+       case ISCSI_OP_R2T:
+               if (ahslen) {
+                       rc = ISCSI_ERR_AHSLEN;
+@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+               }
+               rc = iscsi_tcp_r2t_rsp(conn, hdr);
+               break;
++      case ISCSI_OP_SCSI_CMD_RSP:
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_REJECT:
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
new file mode 100644 (file)
index 0000000..1b656b8
--- /dev/null
@@ -0,0 +1,165 @@
+From ecedd21d2cd35a5d3ea5e8f447c54d75613e10bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 16:11:45 +0800
+Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
+
+From: Xingui Yang <yangxingui@huawei.com>
+
+[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
+
+Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue
+for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
+the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue,
+calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI
+devices and waits for the host to become runtime-active. But the host
+cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the
+drain is blocked on that very handler.
+
+However skipping the drain reintroduces a race: hisi_sas returns from
+resume before all PHY UP work and libsas discovery work finish. The
+controller may then autosuspend while disks are still waking up. The
+disks issue IO to a suspended controller, the IO fails, and the disks
+get disabled.
+
+Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT
+notification to after sas_drain_work(). By then the host resume is about
+to complete, so device removal through device_link no longer blocks on
+the resume and the cycle is broken.
+
+With the deadlock gone, restore sas_resume_ha() (the draining variant)
+in hisi_sas and remove sas_resume_ha_no_sync().
+
+The reorder is safe for the other libsas consumers (isci, pm8001,
+aic94xx, mvsas). During suspend, sas_suspend_devices() calls
+sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to
+NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a
+timed-out phy's disk is immediately rejected by the LLDD before reaching
+hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET),
+and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both
+complete directly via scsi_done() without entering SCSI EH. This is
+identical in both the old and new ordering since lldd_dev_gone runs
+during suspend, before resume. The reorder only affects when the
+PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work()
+vs. asynchronous after resume returns), not whether I/O can reach the
+device. aic94xx and mvsas do not register any PM ops and never reach
+this code path.
+
+Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume")
+Signed-off-by: Xingui Yang <yangxingui@huawei.com>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------
+ drivers/scsi/libsas/sas_init.c         | 37 +++++++++++++-------------
+ include/scsi/libsas.h                  |  1 -
+ 3 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+index 213d5b5dea94f..8a2500993e19d 100644
+--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device)
+               return rc;
+       }
+       phys_init_v3_hw(hisi_hba);
+-
+-      /*
+-       * If a directly-attached disk is removed during suspend, a deadlock
+-       * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+-       * hisi_hba->device to be active, which can only happen when resume
+-       * completes. So don't wait for the HA event workqueue to drain upon
+-       * resume.
+-       */
+-      sas_resume_ha_no_sync(sha);
++      sas_resume_ha(sha);
+       clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+       dev_warn(dev, "end of resuming controller\n");
+diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
+index 0bec236f0fb59..c3f3d05b46dea 100644
+--- a/drivers/scsi/libsas/sas_init.c
++++ b/drivers/scsi/libsas/sas_init.c
+@@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+       }
+ }
+-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
++void sas_resume_ha(struct sas_ha_struct *ha)
+ {
+       const unsigned long tmo = msecs_to_jiffies(25000);
+       int i;
+@@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+                        i, i > 1 ? "s" : "");
+       wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
++
++      /*
++       * All phys are back up or timed out. Turn on I/O and drain
++       * pending work.
++       */
++      scsi_unblock_requests(ha->shost);
++      sas_drain_work(ha);
++
++      /*
++       * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
++       * calls sas_deform_port() -> sas_destruct_devices(), which removes
++       * SCSI devices and, for LLDDs using device_link() PM sync, waits
++       * for the host to be runtime-active. Sending it before the drain
++       * would deadlock: the drain waits for the handler, the handler
++       * waits for host resume, and host resume waits for the drain to
++       * finish.
++       */
+       for (i = 0; i < ha->num_phys; i++) {
+               struct asd_sas_phy *phy = ha->sas_phy[i];
+@@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+               }
+       }
+-      /* all phys are back up or timed out, turn on i/o so we can
+-       * flush out disks that did not return
+-       */
+-      scsi_unblock_requests(ha->shost);
+-      if (drain)
+-              sas_drain_work(ha);
+       clear_bit(SAS_HA_RESUMING, &ha->state);
+       sas_queue_deferred_work(ha);
+@@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+        */
+       sas_resume_insert_broadcast_ha(ha);
+ }
+-
+-void sas_resume_ha(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, true);
+-}
+ EXPORT_SYMBOL(sas_resume_ha);
+-/* A no-sync variant, which does not call sas_drain_ha(). */
+-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+-{
+-      _sas_resume_ha(ha, false);
+-}
+-EXPORT_SYMBOL(sas_resume_ha_no_sync);
+-
+ void sas_suspend_ha(struct sas_ha_struct *ha)
+ {
+       int i;
+diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
+index 163f23c92b411..36d4cb567837c 100644
+--- a/include/scsi/libsas.h
++++ b/include/scsi/libsas.h
+@@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
+ extern int sas_unregister_ha(struct sas_ha_struct *);
+ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
+ extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
+-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
+ extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
+ int sas_phy_reset(struct sas_phy *phy, int hard_reset);
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch b/queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch
new file mode 100644 (file)
index 0000000..067f7cf
--- /dev/null
@@ -0,0 +1,69 @@
+From 38a239845c788d6851838a0ff1939f8f281ee1c7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 23:22:31 +0530
+Subject: scsi: mpi3mr: Fix potential deadlock in mpi3mr_fault_uevent_emit
+
+From: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
+
+[ Upstream commit ccff8c92571500fcfed21281e33daaf645bf692f ]
+
+mpi3mr_fault_uevent_emit() runs from the fault watchdog and reset paths
+where host I/O may already be blocked. GFP_KERNEL allocations here, both
+the local kzalloc_obj() and the ones inside kobject_uevent_env() itself,
+can trigger reclaim that waits on that blocked I/O and deadlock.
+
+Use memalloc_noio_save()/restore() to cover the whole call instead of
+just the local allocation.
+
+Fixes: ec54b348f274 ("scsi: mpi3mr: Record and report controller firmware faults")
+Reported-by: sashiko-bot <sashiko-bot@kernel.org>
+Closes: https://sashiko.dev/#/patchset/20260724164630.924288-1-chandrakanth.patil%40broadcom.com
+Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
+Link: https://patch.msgid.link/20260724175231.935192-1-chandrakanth.patil@broadcom.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/mpi3mr/mpi3mr_fw.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
+index 31b19ed1528e5..681868716ebdb 100644
+--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
++++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
+@@ -9,6 +9,7 @@
+ #include "mpi3mr.h"
+ #include <linux/io-64-nonatomic-lo-hi.h>
++#include <linux/sched/mm.h>
+ static int
+ mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason);
+@@ -1287,11 +1288,14 @@ static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc)
+ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
+ {
+       struct kobj_uevent_env *env;
++      unsigned int noio_flag;
+       int ret;
++      noio_flag = memalloc_noio_save();
++
+       env = kzalloc_obj(*env);
+       if (!env)
+-              return;
++              goto out_restore;
+       ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name);
+       if (ret)
+@@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
+ out_free:
+       kfree(env);
+-
++out_restore:
++      memalloc_noio_restore(noio_flag);
+ }
+ /**
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
new file mode 100644 (file)
index 0000000..b13ab24
--- /dev/null
@@ -0,0 +1,80 @@
+From 2e7efe7903b25d2c5e09b1bfd1c961b424c8d5d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:30:10 +0300
+Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
+
+When target_get_sess_cmd() fails during session shutdown because
+percpu_ref_tryget_live() returns false, the command keeps the
+se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier
+without owning a reference. Final release through
+target_release_cmd_kref() then issues an unmatched percpu_ref_put().
+
+Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during
+cmd setup") moved the cmd_cnt assignment ahead of the reference
+acquisition.  Clear se_cmd->cmd_cnt whenever the initial
+target_get_sess_cmd() fails in target_init_cmd() and
+target_submit_tmr(), so release performs exactly one matching put per
+acquired reference.
+
+Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup")
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_transport.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
+index fad03a15c969e..dcfe945949167 100644
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+                   u32 data_length, int task_attr, int data_dir, int flags)
+ {
+       struct se_portal_group *se_tpg;
++      int ret;
+       se_tpg = se_sess->se_tpg;
+       BUG_ON(!se_tpg);
+@@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
+        * kref_put() to happen during fabric packet acknowledgement.
+        */
+-      return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
++      if (ret)
++              se_cmd->cmd_cnt = NULL;
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(target_init_cmd);
+@@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+        * allocation failure.
+        */
+       ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+-      if (ret < 0)
++      if (ret < 0) {
++              se_cmd->cmd_cnt = NULL;
+               return -ENOMEM;
++      }
+       if (tm_type == TMR_ABORT_TASK)
+               se_cmd->se_tmr_req->ref_task_tag = tag;
+@@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+       /* See target_submit_cmd for commentary */
+       ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+       if (ret) {
++              se_cmd->cmd_cnt = NULL;
+               core_tmr_release_req(se_cmd->se_tmr_req);
+               return ret;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
new file mode 100644 (file)
index 0000000..40d1eae
--- /dev/null
@@ -0,0 +1,54 @@
+From bf930fa9efd8fb40efdfb366b4470ad46a671fab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2026 15:58:50 +0800
+Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE
+
+From: TanZheng <tanzheng@kylinos.cn>
+
+[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ]
+
+In the iblock_execute_pr_out() function, PRO_PREEMPT,
+PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability
+checks through ops->pr_clear. The error check allows unimplemented hooks
+to pass through the gate, resulting dereferencing a NULL function
+pointer.
+
+Check whether the hooks that need to be called are supported.
+
+Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock")
+Signed-off-by: TanZheng <tanzheng@kylinos.cn>
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/target/target_core_iblock.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
+index 1087d1d17c36d..ea65d39fef884 100644
+--- a/drivers/target/target_core_iblock.c
++++ b/drivers/target/target_core_iblock.c
+@@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+               break;
+       case PRO_PREEMPT:
+       case PRO_PREEMPT_AND_ABORT:
+-              if (!ops->pr_clear) {
++              if (!ops->pr_preempt) {
+                       pr_err("block_device does not support pr_preempt.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+@@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
+                                     sa == PRO_PREEMPT_AND_ABORT);
+               break;
+       case PRO_RELEASE:
+-              if (!ops->pr_clear) {
+-                      pr_err("block_device does not support pr_pclear.\n");
++              if (!ops->pr_release) {
++                      pr_err("block_device does not support pr_release.\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
new file mode 100644 (file)
index 0000000..fb85aef
--- /dev/null
@@ -0,0 +1,70 @@
+From eb9bd94a48c6a718b60abe97abe8127f42d7d519 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 01:27:26 +0800
+Subject: scsi: ufs: core: Cancel RTC work in active-active suspend
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ]
+
+UFS RTC support schedules ufs_rtc_update_work to periodically update the
+device RTC. The work can issue query commands and access the UFS host
+controller.
+
+A previous change moved the RTC work cancellation before the PRE_CHANGE
+vendor suspend callback to close a race in the common suspend path.
+However, the active-active path jumps directly to vops_suspend after
+flushing exception handling work and therefore bypasses the
+cancellation.
+
+If the RTC work runs while the vendor suspend callback is gating or
+otherwise changing hardware state, it can access the controller during
+suspend and trigger an SError.
+
+Cancel the RTC work before entering the vendor suspend callback in the
+active-active path. Since this path now cancels the work, move the RTC
+work scheduling outside the device and link state restoration block in
+the resume path. This restarts RTC updates after an active-active
+suspend and resume cycle.
+
+Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend")
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 84e32957c332d..d6361e42ba553 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -10238,6 +10238,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+                       req_link_state == UIC_LINK_ACTIVE_STATE) {
+               ufshcd_disable_auto_bkops(hba);
+               flush_work(&hba->eeh_work);
++              cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
+               goto vops_suspend;
+       }
+@@ -10447,10 +10448,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+               if (ret)
+                       goto set_old_link_state;
+               ufshcd_set_timestamp_attr(hba);
+-              schedule_delayed_work(&hba->ufs_rtc_update_work,
+-                                    msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+       }
++      schedule_delayed_work(&hba->ufs_rtc_update_work,
++                            msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
++
+       if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+               ufshcd_enable_auto_bkops(hba);
+       else
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch b/queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch
new file mode 100644 (file)
index 0000000..f54127e
--- /dev/null
@@ -0,0 +1,54 @@
+From 5c73e51b26824dd3b389f8b2037e226e2248c8c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 03:45:30 +0000
+Subject: scsi: ufs: core: Initialize hba->rpmbs list in ufshcd
+
+From: Ao Sun <ao.sun@transsion.com>
+
+[ Upstream commit 0279fd451a9971c0d5b959fc59f3e11b55e1694e ]
+
+Initialize the hba->rpmbs list in ufshcd_alloc_host() to prevent NULL
+pointer dereference in the device teardown path if ufs_rpmb_probe()
+fails.
+
+Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices")
+Co-developed-by: Jiazi Li <jiazi.li@transsion.com>
+Signed-off-by: Jiazi Li <jiazi.li@transsion.com>
+Signed-off-by: Ao Sun <ao.sun@transsion.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Link: https://patch.msgid.link/20260723034440.217-1-ao.sun@transsion.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufs-rpmb.c | 2 --
+ drivers/ufs/core/ufshcd.c   | 1 +
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
+index ffad049872b9a..62120dc2e9da7 100644
+--- a/drivers/ufs/core/ufs-rpmb.c
++++ b/drivers/ufs/core/ufs-rpmb.c
+@@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba)
+               return -EINVAL;
+       }
+-      INIT_LIST_HEAD(&hba->rpmbs);
+-
+       struct rpmb_descr descr = {
+               .type = RPMB_TYPE_UFS,
+               .route_frames = ufs_rpmb_route_frames,
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index 082eac1e13463..d6444b3c2ef16 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -10924,6 +10924,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
+       hba->nop_out_timeout = NOP_OUT_TIMEOUT;
+       ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry));
+       INIT_LIST_HEAD(&hba->clk_list_head);
++      INIT_LIST_HEAD(&hba->rpmbs);
+       spin_lock_init(&hba->outstanding_lock);
+       *hba_handle = hba;
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch b/queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch
new file mode 100644 (file)
index 0000000..d9a3de8
--- /dev/null
@@ -0,0 +1,123 @@
+From 2cd1cb8848d17a5b3fd8bb21df378703ae179456 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 10:12:28 -0700
+Subject: scsi: ufs: core: Revert "Delegate the interrupt service routine to a
+ threaded IRQ handler"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ Upstream commit 8a309036f557d3ff4efb2beea5132ba91172d934 ]
+
+There have been multiple reports of performance regressions caused by
+commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service
+routine to a threaded IRQ handler"). Hence this revert.
+
+This patch reverts most of the following commits:
+
+ * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service
+   routine to a threaded IRQ handler")
+
+ * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active
+   UIC command")
+
+This patch preserves the following commits:
+
+ * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode")
+
+ * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI
+   host lock")
+
+Cc: Neil Armstrong <neil.armstrong@linaro.org>
+Cc: 孙魁 (Kui Sun) <kui.sun@unisoc.com>
+Cc: André Draszik <andre.draszik@linaro.org>
+Cc: Gregory CLEMENT <gregory.clement@bootlin.com>
+Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler")
+Signed-off-by: Bart Van Assche <bvanassche@acm.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Tested-by: André Draszik <andre.draszik@linaro.org> # on Pixel 6
+Reviewed-by: André Draszik <andre.draszik@linaro.org>
+Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/core/ufshcd.c | 39 +++------------------------------------
+ 1 file changed, 3 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
+index d6361e42ba553..082eac1e13463 100644
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -7309,7 +7309,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+ }
+ /**
+- * ufshcd_threaded_intr - Threaded interrupt service routine
++ * ufshcd_intr - Main interrupt service routine
+  * @irq: irq number
+  * @__hba: pointer to adapter instance
+  *
+@@ -7317,7 +7317,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+  *  IRQ_HANDLED - If interrupt is valid
+  *  IRQ_NONE    - If invalid interrupt
+  */
+-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
++static irqreturn_t ufshcd_intr(int irq, void *__hba)
+ {
+       u32 last_intr_status, intr_status, enabled_intr_status = 0;
+       irqreturn_t retval = IRQ_NONE;
+@@ -7356,38 +7356,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
+       return retval;
+ }
+-/**
+- * ufshcd_intr - Main interrupt service routine
+- * @irq: irq number
+- * @__hba: pointer to adapter instance
+- *
+- * Return:
+- *  IRQ_HANDLED     - If interrupt is valid
+- *  IRQ_WAKE_THREAD - If handling is moved to threaded handled
+- *  IRQ_NONE        - If invalid interrupt
+- */
+-static irqreturn_t ufshcd_intr(int irq, void *__hba)
+-{
+-      struct ufs_hba *hba = __hba;
+-      u32 intr_status, enabled_intr_status;
+-
+-      /*
+-       * Handle interrupt in thread if MCQ or ESI is disabled,
+-       * and no active UIC command.
+-       */
+-      if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) &&
+-          !hba->active_uic_cmd)
+-              return IRQ_WAKE_THREAD;
+-
+-      intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
+-      enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+-
+-      ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
+-
+-      /* Directly handle interrupts since MCQ ESI handlers does the hard job */
+-      return ufshcd_sl_intr(hba, enabled_intr_status);
+-}
+-
+ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
+ {
+       int err = 0;
+@@ -11204,8 +11172,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
+       ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
+       /* IRQ registration */
+-      err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr,
+-                                      IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba);
++      err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+       if (err) {
+               dev_err(hba->dev, "request irq failed\n");
+               goto out_disable;
+-- 
+2.53.0
+
diff --git a/queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
new file mode 100644 (file)
index 0000000..cd3c7db
--- /dev/null
@@ -0,0 +1,66 @@
+From 90a0aebcef3f45bcde4a95cf79a6f749acfb9674 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:27:36 +0200
+Subject: scsi: zfcp: Fix memory leak during adapter release by destroying
+ gid_pn_req
+
+From: Benjamin Block <bblock@linux.ibm.com>
+
+[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
+
+When releasing an adapter we don't free the mempool 'gid_pn_req' that is
+allocated during the enqueue. This leaks memory:
+
+  unreferenced object 0xd8d29297de700 (size 256):
+    comm "(udev-worker)", pid 2105, jiffies 4294945794
+    hex dump (first 32 bytes):
+      00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00  ......N.........
+      ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0  ..........._g...
+    backtrace (crc 4a5b5da2):
+      [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
+      [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
+      [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
+      [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
+      [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
+      [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
+      [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
+      [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
+      [<000dc45f643d8238>] online_store+0x298/0x5b0
+      [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
+      [<000dc45f62c81150>] new_sync_write+0x370/0x4b0
+      [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
+      [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
+      [<000dc45f621c4a16>] do_syscall+0x2f6/0x430
+      [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
+      [<000dc45f64dc2224>] system_call+0x74/0xa0
+
+Fix this by destroying the mempool during the adapter's release.
+
+Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp")
+Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
+Tested-by: M Nikhil <nikh1092@linux.ibm.com>
+Acked-by: M Nikhil <nikh1092@linux.ibm.com>
+Reviewed-by: Chinmaya Kajagar <chinmayk@linux.ibm.com>
+Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
+Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/scsi/zfcp_aux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
+index 8ff7db7921b5a..fea573e00f9df 100644
+--- a/drivers/s390/scsi/zfcp_aux.c
++++ b/drivers/s390/scsi/zfcp_aux.c
+@@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+ {
+       mempool_destroy(adapter->pool.erp_req);
++      mempool_destroy(adapter->pool.gid_pn_req);
+       mempool_destroy(adapter->pool.scsi_req);
+       mempool_destroy(adapter->pool.scsi_abort);
+       mempool_destroy(adapter->pool.qtcb_pool);
+-- 
+2.53.0
+
diff --git a/queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch b/queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch
new file mode 100644 (file)
index 0000000..5390573
--- /dev/null
@@ -0,0 +1,42 @@
+From 8309659d709933d5d6098ac9b150fafa9fcd6519 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 16:02:10 +0800
+Subject: selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE
+
+From: Haofeng Li <lihaofeng@kylinos.cn>
+
+[ Upstream commit b3a7aa9c0020ae549a0d4964867ff66d2bd61709 ]
+
+Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE")
+renamed the LKDTM crash type and selftest configuration but missed the
+entry in tests.txt.
+
+As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh
+skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename
+the test entry so the generated runner uses the registered crash type.
+
+Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE")
+Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
+Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/lkdtm/tests.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt
+index 3245032db34d3..444040ca51689 100644
+--- a/tools/testing/selftests/lkdtm/tests.txt
++++ b/tools/testing/selftests/lkdtm/tests.txt
+@@ -78,7 +78,7 @@ USERCOPY_STACK_FRAME_TO
+ USERCOPY_STACK_FRAME_FROM
+ USERCOPY_STACK_BEYOND
+ USERCOPY_KERNEL
+-STACKLEAK_ERASING OK: the rest of the thread stack is properly erased
++KSTACK_ERASE OK: the rest of the thread stack is properly erased
+ CFI_FORWARD_PROTO
+ CFI_BACKWARD call trace:|ok: control flow unchanged
+ FORTIFY_STRSCPY detected buffer overflow
+-- 
+2.53.0
+
diff --git a/queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch b/queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch
new file mode 100644 (file)
index 0000000..a9fe525
--- /dev/null
@@ -0,0 +1,253 @@
+From f736457503efdb1390931ecfc2061d2690e47a07 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 14:29:02 -0400
+Subject: selftests/net/af_unix: test listen() rejects wrong socket states
+
+From: John Ericson <mail@johnericson.me>
+
+[ Upstream commit 7f57c650d08b8793bb551bdb33ad876535ef9fe8 ]
+
+Add a regression test for the unix_listen() state check. The key case is
+listen() on a bound socket that has already been connected: it is no
+longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A
+prepare_peercred() call slipped in ahead of that check once left err at 0
+and made listen() silently succeed there instead; this guards against a
+repeat.
+
+The neighbouring outcomes are covered too so they cannot regress the same
+way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a
+socket already in TCP_LISTEN is allowed, and an unbound socket fails with
+EINVAL.
+
+Each case runs for both listenable socket types (SOCK_STREAM and
+SOCK_SEQPACKET) and both pathname and abstract addresses.
+
+Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
+Signed-off-by: John Ericson <mail@johnericson.me>
+Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../testing/selftests/net/af_unix/.gitignore  |   1 +
+ tools/testing/selftests/net/af_unix/Makefile  |   1 +
+ .../selftests/net/af_unix/unix_listen.c       | 187 ++++++++++++++++++
+ 3 files changed, 189 insertions(+)
+ create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c
+
+diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
+index 240b26740c9e0..9731766441036 100644
+--- a/tools/testing/selftests/net/af_unix/.gitignore
++++ b/tools/testing/selftests/net/af_unix/.gitignore
+@@ -6,3 +6,4 @@ scm_rights
+ so_peek_off
+ unix_connect
+ unix_connreset
++unix_listen
+diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
+index 4c0375e28bbee..57d159803a3ab 100644
+--- a/tools/testing/selftests/net/af_unix/Makefile
++++ b/tools/testing/selftests/net/af_unix/Makefile
+@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \
+       so_peek_off \
+       unix_connect \
+       unix_connreset \
++      unix_listen \
+ # end of TEST_GEN_PROGS
+ include ../../lib.mk
+diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c
+new file mode 100644
+index 0000000000000..416fa3e5bfe9b
+--- /dev/null
++++ b/tools/testing/selftests/net/af_unix/unix_listen.c
+@@ -0,0 +1,187 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Tests for the state checks in AF_UNIX listen().
++ *
++ * The central case is a regression test: listen() on a bound socket that
++ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must
++ * fail with EINVAL.  A prior change accidentally let it return success
++ * without doing anything, because a helper called in between reset the
++ * error code to 0.  The neighbouring checks (unbound, already listening)
++ * are tested too so they cannot silently regress the same way.
++ *
++ * Every case runs for both listenable socket types (SOCK_STREAM and
++ * SOCK_SEQPACKET) and both pathname and abstract addresses.
++ */
++#define _GNU_SOURCE
++
++#include <errno.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <sys/socket.h>
++#include <sys/un.h>
++
++#include "kselftest_harness.h"
++
++#define SK_NAME               "unix_listen_sk"
++#define SRV_NAME      "unix_listen_srv"
++
++FIXTURE(unix_listen)
++{
++      int sk;                 /* socket under test */
++      int server;             /* a listening peer, when a test needs one */
++      struct sockaddr_un addr, srv_addr;
++      socklen_t addrlen, srv_addrlen;
++};
++
++FIXTURE_VARIANT(unix_listen)
++{
++      int type;
++      int abstract;
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, stream_pathname)
++{
++      .type = SOCK_STREAM,
++      .abstract = 0,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, stream_abstract)
++{
++      .type = SOCK_STREAM,
++      .abstract = 1,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname)
++{
++      .type = SOCK_SEQPACKET,
++      .abstract = 0,
++};
++
++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract)
++{
++      .type = SOCK_SEQPACKET,
++      .abstract = 1,
++};
++
++/* Fill @addr with a pathname or abstract address named @name. */
++static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name,
++                             int abstract)
++{
++      size_t len = strlen(name);
++
++      memset(addr, 0, sizeof(*addr));
++      addr->sun_family = AF_UNIX;
++      /* An abstract address leads with a NUL and has no filesystem entry. */
++      memcpy(addr->sun_path + (abstract ? 1 : 0), name, len);
++
++      return offsetof(struct sockaddr_un, sun_path) + len + 1;
++}
++
++FIXTURE_SETUP(unix_listen)
++{
++      self->sk = -1;
++      self->server = -1;
++      self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract);
++      self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME,
++                                        variant->abstract);
++}
++
++FIXTURE_TEARDOWN(unix_listen)
++{
++      if (self->sk >= 0)
++              close(self->sk);
++      if (self->server >= 0)
++              close(self->server);
++
++      /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */
++      if (!variant->abstract) {
++              remove(SK_NAME);
++              remove(SRV_NAME);
++      }
++}
++
++/* A bound socket in TCP_CLOSE is the normal, allowed case. */
++TEST_F(unix_listen, bound_is_ok)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(0, err);
++}
++
++/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */
++TEST_F(unix_listen, relisten_is_ok)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 16);
++      EXPECT_EQ(0, err);
++}
++
++/* listen() on an unbound socket fails: there is nothing to listen on. */
++TEST_F(unix_listen, unbound_is_einval)
++{
++      int err;
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(-1, err);
++      EXPECT_EQ(EINVAL, errno);
++}
++
++/*
++ * The regression: a bound socket that has already been connected is not in
++ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather
++ * than quietly succeeding.
++ */
++TEST_F(unix_listen, connected_is_einval)
++{
++      int err;
++
++      self->server = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->server);
++
++      err = bind(self->server, (struct sockaddr *)&self->srv_addr,
++                 self->srv_addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->server, 8);
++      ASSERT_EQ(0, err);
++
++      self->sk = socket(AF_UNIX, variant->type, 0);
++      ASSERT_LE(0, self->sk);
++
++      /* Bind first so the unbound check does not mask the state check. */
++      err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
++      ASSERT_EQ(0, err);
++
++      err = connect(self->sk, (struct sockaddr *)&self->srv_addr,
++                    self->srv_addrlen);
++      ASSERT_EQ(0, err);
++
++      err = listen(self->sk, 8);
++      EXPECT_EQ(-1, err);
++      EXPECT_EQ(EINVAL, errno);
++}
++
++TEST_HARNESS_MAIN
+-- 
+2.53.0
+
diff --git a/queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch b/queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch
new file mode 100644 (file)
index 0000000..86cb1df
--- /dev/null
@@ -0,0 +1,101 @@
+From 83cf4b8bed05611a4f28b947ca79c51342972d3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 14:53:22 +0200
+Subject: selftests: netfilter: nft_flowtable.sh: fix offload counter
+ verification for tunnel tests
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+[ Upstream commit 1d6123f87eebb5148844cd43045c6e598799720b ]
+
+The IPIP and IP6IP6 tunnel tests call check_counters() to verify
+flowtable offloading occurred, but the flow-add rule only matches
+meta oif "veth1". When traffic is routed through a tunnel device,
+oif is the tunnel interface (tun0, tun6, etc.), not veth1, so
+the flow-add rule never fires, no flowtable entry is created,
+and counters stay at zero — producing a silent false pass.
+Fix by adding tunnel-specific flow-add rules for each tunnel
+interface. These match TCP dport 12345 traffic before the bare
+accept rule, set ct mark, add the flow to the flowtable, and
+increment routed_orig. The existing routed_repl rule on veth0
+already handles the reply direction since decapsulated reply
+packets exit through the physical interface.
+Also add check_counters() for the IP6IP6 non-VLAN and
+IP6IP6-over-VLAN tests which previously used a bare PASS message.
+
+Fixes: fe8313316eaf ("selftests: netfilter: nft_flowtable.sh: Add IPIP flowtable selftest")
+Fixes: 5e5180352193 ("selftests: netfilter: nft_flowtable.sh: Add IP6IP6 flowtable selftest")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/net/netfilter/nft_flowtable.sh       | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+index 08ad07500e8a7..d9a21ca8ed2cb 100755
+--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
++++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+@@ -617,7 +617,11 @@ ip -6 -net "$nsr2" route add default via fee1:3::1
+ ip -net "$ns2" route add default via 10.0.2.1
+ ip -6 -net "$ns2" route add default via dead:2::1
++ip netns exec "$nsr1" nft -a insert rule inet filter forward \
++      'meta oif tun0 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept'
++ip netns exec "$nsr1" nft -a insert rule inet filter forward \
++      'meta oif tun6 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+       'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
+@@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then
+ fi
+ if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+-      echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel"
++      check_counters "flow offload for ns1/ns2 IP6IP6 tunnel"
+ else
+       echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2
+       ip netns exec "$nsr1" nft list ruleset
+@@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up
+ ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10
+ ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad
+ ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null
++ip netns exec "$nsr1" nft -a insert rule inet filter forward \
++      'meta oif veth1.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif veth1.10 accept'
+ ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2
+@@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up
+ ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10
+ ip -net "$nsr1" route change default via 192.168.200.2
+ ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null
++ip netns exec "$nsr1" nft -a insert rule inet filter forward \
++      'meta oif tun0.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept'
+ ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none
+@@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up
+ ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad
+ ip -6 -net "$nsr1" route delete default
+ ip -6 -net "$nsr1" route add default via fee1:5::2
++ip netns exec "$nsr1" nft -a insert rule inet filter forward \
++      'meta oif tun6.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept'
+ ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10
+@@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then
+ fi
+ if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+-      echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
++      check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
+ else
+       echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2
+       ip netns exec "$nsr1" nft list ruleset
+-- 
+2.53.0
+
diff --git a/queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch b/queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch
new file mode 100644 (file)
index 0000000..b796059
--- /dev/null
@@ -0,0 +1,57 @@
+From 88812d591559a652371feb0a592ac479e985cfac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:35:52 +0800
+Subject: selftests/seccomp: Fix pointer type mismatch build error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Kuan-Ying Lee <kuan-ying.lee@canonical.com>
+
+[ Upstream commit 3421b9b056a6576d0ebac1030eafb48ad0544092 ]
+
+We hit the following build error while running the seccomp selftests in
+our testing.
+
+CC seccomp_bpf
+seccomp_bpf.c: In function ‘UPROBE_setup’:
+seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types]
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^
+seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^~~~~~~~~~~~~~~~
+seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’
+5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
+| ^~~~~~~~~~~~~
+
+get_uprobe_offset() takes a 'const void *' argument, so cast both
+operands to 'void *'.
+
+Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp")
+Signed-off-by: Kuan-Ying Lee <kuan-ying.lee@canonical.com>
+Acked-by: Jiri Olsa <jolsa@kernel.org>
+Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
+index 358b6c65e120e..0622bc2acad40 100644
+--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
++++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
+@@ -5178,7 +5178,8 @@ FIXTURE_SETUP(UPROBE)
+               ASSERT_GE(bit, 0);
+       }
+-      offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe);
++      offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe
++                                                    : (void *)probed_uprobe);
+       ASSERT_GE(offset, 0);
+       if (variant->uretprobe)
+-- 
+2.53.0
+
index c1b4db88670e16a7c378255f810094fb0ab3c6e3..2c17f6cba6c8bbae3408eae72dd94a5fce535d85 100644 (file)
@@ -4,3 +4,192 @@ lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch
 mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch
 alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch
 thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
+dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch
+pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch
+pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch
+dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch
+dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch
+dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch
+iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch
+gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch
+selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch
+selftests-seccomp-fix-pointer-type-mismatch-build-er.patch
+ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch
+ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch
+ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch
+phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch
+asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch
+asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch
+of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch
+btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch
+btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch
+btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch
+btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch
+btrfs-skip-global-block-reserve-accounting-for-rescu.patch
+btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch
+btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch
+ntfs-harden-runlist-realloc-size-calculations.patch
+ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch
+rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch
+phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch
+phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch
+netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch
+selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch
+netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch
+drm-mediatek-check-crtc-state-before-freeing.patch
+mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch
+drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch
+mshv-fix-duplicate-gsi-detection-for-gsi-0.patch
+mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch
+kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch
+kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch
+kvm-arm64-fix-hyp_trace-clock-disabling.patch
+kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch
+kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch
+kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch
+kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch
+keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch
+keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch
+keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch
+assoc_array-trim-the-final-shortcut-word-using-the-c.patch
+ipvs-adjust-double-hashing-when-fwd-method-changes.patch
+netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch
+netfilter-xt_hashlimit-validate-hashtable-supports-x.patch
+ipvs-fix-the-checksum-validations.patch
+ipvs-fix-places-with-wrong-packet-offsets.patch
+ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch
+ipvs-clear-the-nfct-flag-under-lock.patch
+netfilter-nft_payload-fix-mask-build-for-partial-fie.patch
+asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch
+asoc-sdca-always-free-firmware-in-fdl-path.patch
+asoc-sdca-make-ump-message-size-check-more-robust.patch
+asoc-sdca-ensure-that-control-range-is-large-enough-.patch
+rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch
+nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch
+nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch
+af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch
+selftests-net-af_unix-test-listen-rejects-wrong-sock.patch
+xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch
+xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch
+xsk-provide-sufficient-space-in-pool-tx_descs.patch
+xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch
+net-sched-sch_cake-skip-clearing-unused-tins-during-.patch
+pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch
+scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch
+scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch
+scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch
+smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
+erofs-clean-up-erofs_ishare_fill_inode.patch
+erofs-remove-fscache-backend-entirely.patch
+erofs-ensure-valid-f_path-for-page-cache-sharing.patch
+gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch
+acpi-cppc-skip-writes-to-unsupported-performance-con.patch
+wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch
+asoc-tas2781-use-correct-calibration-data-for-sinega.patch
+spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
+hwmon-nct6775-core-fix-number-of-temperature-registe.patch
+hwmon-ina2xx-fix-various-overflow-issues.patch
+hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch
+hwmon-sht3x-fix-unaligned-accesses.patch
+hwmon-lm90-only-report-alarms-if-driver-is-ready.patch
+hwmon-nzxt-smart2-dma-align-output-buffer.patch
+net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch
+hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch
+net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch
+asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch
+forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch
+hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch
+hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch
+hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch
+hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch
+hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch
+hwmon-adt7470-use-cached-pwm-frequency-value.patch
+hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch
+hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch
+rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch
+ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch
+powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch
+powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch
+powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch
+net-sched-cls_u32-validate-offshift-to-prevent-shift.patch
+net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch
+wifi-mac80211-validate-individual-twt-params-before-.patch
+netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch
+netfs-handle-single-writeback-rolling-buffer-allocat.patch
+netfs-release-readahead-folios-on-iterator-preparati.patch
+netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch
+net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch
+hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch
+idpf-bound-interrupt-vector-register-fill-to-the-all.patch
+idpf-adjust-txq-ring-count-minimum.patch
+idpf-fix-mailbox-irq-name-leak-on-request-failure.patch
+ice-suppress-dpll-errors-during-reset-recovery.patch
+bluetooth-iso-clear-iso_data-always-when-detaching-c.patch
+bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch
+bluetooth-iso-fix-connected-closed-transition-on-shu.patch
+bluetooth-iso-lock-sk-in-iso_sock_getname.patch
+bluetooth-iso-lock-sk-in-iso_connect_ind.patch
+bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch
+bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch
+bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch
+bluetooth-iso-fix-leaking-sk-after-socket-release.patch
+bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch
+bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch
+bluetooth-iso-fix-refcounting-of-iso_conn.patch
+bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch
+bluetooth-btintel-validate-length-before-parsing-dia.patch
+bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch
+bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch
+bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch
+bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch
+bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch
+bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch
+bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch
+x86-boot-add-volatile-clobbers-and-zero-length-test-.patch
+net-phylink-put-link_gpio-if-phylink_create-fails.patch
+scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch
+scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch
+scsi-ufs-core-revert-delegate-the-interrupt-service-.patch
+scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch
+scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch
+octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch
+octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch
+scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch
+scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch
+net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch
+net-sxgbe-check-descriptor-ring-allocation-failures.patch
+can-isotp-check-register_netdevice_notifier-error-in.patch
+drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch
+drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch
+fprobe-fix-module-reference-count-leak-on-error-in-r.patch
+tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
+tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
+riscv-drop-__init-from-vec_check_unaligned_access_sp.patch
+accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch
+riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch
+ring-buffer-fix-reader-page-read-offset-for-remote-b.patch
+ipv6-release-fib6_null_entry-on-subtree-failure.patch
+riscv-vdso-only-try-to-install-vdso-when-present.patch
+net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch
+net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch
+net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch
+net-stmmac-fix-e2e-delay-mechanism.patch
+net-mana-create-separate-eqs-for-each-vport.patch
+net-mana-return-error-code-from-mana_create_rxq.patch
+ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch
+net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch
+octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch
+sched-deadline-use-revised-wakeup-rule-only-for-runn.patch
+spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch
+qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch
+drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch
+ksmbd-return-success-for-deferred-final-close.patch
+ksmbd-fix-use-after-free-in-__close_file_table_ids.patch
+ksmbd-use-memcmp-to-compare-clientguids.patch
+iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch
+mshv-fix-race-in-mshv_irqfd_deassign.patch
+mshv-fix-level-triggered-check-on-uninitialized-data.patch
+mshv-fix-missing-error-code-on-vp-allocation-failure.patch
+mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch
+mshv-publish-vp-to-pt_vp_array-before-installing-the.patch
+ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch
diff --git a/queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch
new file mode 100644 (file)
index 0000000..c9506d5
--- /dev/null
@@ -0,0 +1,85 @@
+From b6d9477020fdc02547966c10ef80cd1e7c5ed39f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jun 2026 14:59:09 +0800
+Subject: smb: client: fix buffer leaks in SMB1 read and write
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
+
+CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request
+buffer before checking whether tcon->ses->server is NULL. If that
+defensive check ever fails, the helper returns -ECONNABORTED without
+releasing the request buffer.
+
+Fix these leaks by releasing the allocated request buffer before
+returning from these error paths. Use cifs_small_buf_release() for the
+buffers allocated by small_smb_init() and cifs_buf_release() for the
+buffer allocated by smb_init().
+
+The bug was first flagged by an experimental analysis tool we are
+developing for kernel memory-management bugs while analyzing
+v6.13-rc1. The tool is still under development and is not yet publicly
+available. Manual inspection confirms that the bug is still
+present in v7.1.1.
+
+An x86_64 allyesconfig build showed no new warnings.
+
+Runtime validation used a temporary fault-injection hook to force
+tcon->ses->server to NULL after request-buffer initialization. On the
+unfixed kernel, the harness observed two leaked small request buffers and
+one leaked large request buffer, with directed kmemleak dumps confirming
+the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer
+deltas remained.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 9e27bfa7376b1..024c5d462424d 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -1681,8 +1681,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -1796,8 +1798,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+@@ -2077,8 +2081,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+       pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+       /* tcon and ses pointer are checked in smb_init */
+-      if (tcon->ses->server == NULL)
++      if (!tcon->ses->server) {
++              cifs_small_buf_release(pSMB);
+               return -ECONNABORTED;
++      }
+       pSMB->AndXCommand = 0xFF;       /* none */
+       pSMB->Fid = netfid;
+-- 
+2.53.0
+
diff --git a/queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch
new file mode 100644 (file)
index 0000000..363034d
--- /dev/null
@@ -0,0 +1,111 @@
+From ead9658f107b27afd8cd1d29cfd265fcd4a78c20 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 18:25:10 +0530
+Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Srikanth Boyapally <srikanth.boyapally@amd.com>
+
+[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ]
+
+SPI host transfers could intermittently stall with spi_transfer timeouts.
+The TXFULL condition was checked only once in cdns_transfer_one() before
+cdns_spi_process_fifo(), so if the FIFO became full again during refill,
+writes could be dropped and the transfer would never complete.
+
+Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so
+the 10µs back-off is applied per FIFO entry during filling, ensuring
+forward progress and eliminating spurious timeouts.
+
+Restrict the delay to host mode using spi_controller_is_target(), the
+controller is passed into cdns_spi_process_fifo() so the check is made at
+the point of use. In target mode this delay must not run as it causes the
+target to miss its transfer window and corrupt data.
+
+Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()")
+Signed-off-by: Srikanth Boyapally <srikanth.boyapally@amd.com>
+Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-cadence.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
+index 891e2ba369584..2f549e668ae6a 100644
+--- a/drivers/spi/spi-cadence.c
++++ b/drivers/spi/spi-cadence.c
+@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi)
+ /**
+  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
++ * @ctlr:     Pointer to the spi_controller structure
+  * @xspi:     Pointer to the cdns_spi structure
+  * @ntx:      Number of bytes to pack into the TX FIFO
+  * @nrx:      Number of bytes to drain from the RX FIFO
+  */
+-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
++static void cdns_spi_process_fifo(struct spi_controller *ctlr,
++                                struct cdns_spi *xspi, int ntx, int nrx)
+ {
+       ntx = clamp(ntx, 0, xspi->tx_bytes);
+       nrx = clamp(nrx, 0, xspi->rx_bytes);
+@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+               }
+               if (ntx) {
++                      /* When xspi in busy condition, bytes may send failed,
++                       * then spi control didn't work thoroughly, add one byte
++                       * delay. Only in host mode; in target mode this delay
++                       * causes data corruption as the target fails to prepare
++                       * data in time.
++                       */
++                      if (!spi_controller_is_target(ctlr) &&
++                          (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL))
++                              udelay(10);
++
+                       cdns_spi_writer(xspi);
+                       ntx--;
+               }
+@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, 1);
+               if (xspi->tx_bytes) {
+-                      cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt);
+               } else {
+                       /* Fixed delay due to controller limitation with
+                        * RX_NEMPTY incorrect status
+                        * Xilinx AR:65885 contains more details
+                        */
+                       udelay(10);
+-                      cdns_spi_process_fifo(xspi, 0, trans_cnt);
++                      cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt);
+                       cdns_spi_write(xspi, CDNS_SPI_IDR,
+                                      CDNS_SPI_IXR_DEFAULT);
+                       spi_finalize_current_transfer(ctlr);
+@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
+                       cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
+       }
+-      /* When xspi in busy condition, bytes may send failed,
+-       * then spi control didn't work thoroughly, add one byte delay
+-       */
+-      if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+-              udelay(10);
+-
+       xspi->n_bytes = cdns_spi_n_bytes(transfer);
+       xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+       xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+-      cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
++      cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0);
+       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
+       return transfer->len;
+-- 
+2.53.0
+
diff --git a/queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch b/queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch
new file mode 100644 (file)
index 0000000..7403165
--- /dev/null
@@ -0,0 +1,197 @@
+From 2e71772b73995db2899c5ba23c858afda15d5950 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 18:18:08 +0800
+Subject: spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all
+ supported SoCs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Haibo Chen <haibo.chen@nxp.com>
+
+[ Upstream commit 9c19d60fea9f46ed0c3394653ef4941f1a459968 ]
+
+The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for
+different sample clock source selection") introduced a global 166MHz
+cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing
+specification (Section 3.9.9, page 65).
+
+After reviewing the FlexSPI timing parameters in the datasheets for all
+supported SoCs, the following corrections and additions are needed:
+
+1. SDR mode (RXCLKSRC=0) limits vary per SoC:
+   - i.MX8MN/MM/MP/95: 66MHz  (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10,
+                                IMX8MPCEC, IMX95CEC Rev.8 §4.11.7)
+   - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC,
+                                   IMX8ULPCEC §7.3.1 ND mode)
+   - LX2160A: 100MHz            (LX2160ACEC FlexSPI timing parameters)
+
+2. DTR mode (RXCLKSRC=3) limits vary per SoC:
+   - i.MX8MN/MM/MP/ULP: 166MHz
+   - i.MX8QXP/QM/DXL: 200MHz   (same FlexSPI IP across this family)
+   - i.MX95: 200MHz             (IMX95CEC §4.11.7.3.2.3 Table 106)
+   - LX2160A: DTR disabled      (FSPI_QUIRK_DISABLE_DTR)
+
+Update related platform data with correct speed limation according
+to datasheet.
+
+Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection")
+Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
+Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 80 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
+index 1e36ae084dd86..39c1eaaf9e0ae 100644
+--- a/drivers/spi/spi-nxp-fspi.c
++++ b/drivers/spi/spi-nxp-fspi.c
+@@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data {
+       unsigned int quirks;
+       unsigned int lut_num;
+       bool little_endian;
++      /*
++       * The max clock rate (Hz) that FlexSPI can output to the device
++       * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero.
++       * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode.
++       */
++      unsigned long max_sdr_rate;
++      /*
++       * The max clock rate (Hz) that FlexSPI can output to the device
++       * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero.
++       * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz.
++       */
++      unsigned long max_dtr_rate;
+ };
+ static struct nxp_fspi_devtype_data lx2160a_data = {
+@@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
+       .quirks = FSPI_QUIRK_DISABLE_DTR,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk.
++       */
++      .max_sdr_rate = 100000000,
+ };
+ static struct nxp_fspi_devtype_data imx8mm_data = {
+@@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
+       .quirks = 0,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 166000000,
++};
++
++static struct nxp_fspi_devtype_data imx8mp_data = {
++      .rxfifo = SZ_512,       /* (64  * 64 bits)  */
++      .txfifo = SZ_1K,        /* (128 * 64 bits)  */
++      .ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
++      .quirks = 0,
++      .lut_num = 32,
++      .little_endian = true,  /* little-endian    */
++      /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 166000000,
+ };
+ static struct nxp_fspi_devtype_data imx8qxp_data = {
+@@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = {
+       .quirks = 0,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz.
++       * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 200000000,
+ };
+ static struct nxp_fspi_devtype_data imx8dxl_data = {
+@@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = {
+       .quirks = FSPI_QUIRK_USE_IP_ONLY,
+       .lut_num = 32,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz,
++       * DDR RXCLKSRC=3 max 200MHz.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 200000000,
+ };
+ static struct nxp_fspi_devtype_data imx8ulp_data = {
+@@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = {
+       .quirks = 0,
+       .lut_num = 16,
+       .little_endian = true,  /* little-endian    */
++      /*
++       * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode:
++       * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz.
++       * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR
++       * but is not the default use case.
++       */
++      .max_sdr_rate = 60000000,
++      .max_dtr_rate = 166000000,
++};
++
++static struct nxp_fspi_devtype_data imx95_data = {
++      .rxfifo = SZ_512,       /* (64  * 64 bits)  */
++      .txfifo = SZ_1K,        /* (128 * 64 bits)  */
++      .ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
++      .quirks = 0,
++      .lut_num = 32,
++      .little_endian = true,  /* little-endian    */
++      /*
++       * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz,
++       * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode).
++       */
++      .max_sdr_rate = 66000000,
++      .max_dtr_rate = 200000000,
+ };
+ struct nxp_fspi {
+@@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f,
+       reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+       if (op_is_dtr) {
+               reg |= FSPI_MCR0_RXCLKSRC(3);
+-              f->max_rate = 166000000;
++              /*
++               * Use the SoC-specific DTR max rate if provided, otherwise
++               * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9).
++               */
++              f->max_rate = f->devtype_data->max_dtr_rate ?
++                            f->devtype_data->max_dtr_rate : 166000000;
+       } else {        /*select mode 0 */
+               reg &= ~FSPI_MCR0_RXCLKSRC(3);
+-              f->max_rate = 66000000;
++              /*
++               * Use the SoC-specific SDR max rate if provided, otherwise
++               * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9).
++               */
++              f->max_rate = f->devtype_data->max_sdr_rate ?
++                            f->devtype_data->max_sdr_rate : 66000000;
+       }
+       fspi_writel(f, reg, f->iobase + FSPI_MCR0);
+ }
+@@ -1444,10 +1520,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = {
+ static const struct of_device_id nxp_fspi_dt_ids[] = {
+       { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
+       { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
+-      { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
++      { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, },
+       { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
+       { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
+       { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, },
++      { .compatible = "nxp,imx95-fspi",   .data = (void *)&imx95_data, },
+       { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
+-- 
+2.53.0
+
diff --git a/queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch
new file mode 100644 (file)
index 0000000..17a34cd
--- /dev/null
@@ -0,0 +1,70 @@
+From e119c0928d8d829ee951e14a236c4859c9ad5259 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:50:00 +0900
+Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging
+ functions
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ]
+
+mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into
+tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map().
+If these functions are invoked while mmio_trace_array is NULL (e.g. before
+initialization or after disabled), accessing tr->array_buffer.buffer will
+result in a NULL pointer dereference crash.
+
+Fix this by adding an explicit NULL check for tr at the beginning of
+__trace_mmiotrace_rw() and __trace_mmiotrace_map().
+
+Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2
+Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index e064ba3f28cb9..df8692c2dea8a 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -294,11 +294,15 @@ device_initcall(init_mmio_trace);
+ static void __trace_mmiotrace_rw(struct trace_array *tr,
+                               struct mmiotrace_rw *rw)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_rw *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
+                                         sizeof(*entry), trace_ctx);
+@@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw)
+ static void __trace_mmiotrace_map(struct trace_array *tr,
+                               struct mmiotrace_map *map)
+ {
+-      struct trace_buffer *buffer = tr->array_buffer.buffer;
++      struct trace_buffer *buffer;
+       struct ring_buffer_event *event;
+       struct trace_mmiotrace_map *entry;
+       unsigned int trace_ctx;
++      if (!tr)
++              return;
++
++      buffer = tr->array_buffer.buffer;
+       trace_ctx = tracing_gen_ctx_flags(0);
+       event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
+                                         sizeof(*entry), trace_ctx);
+-- 
+2.53.0
+
diff --git a/queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch
new file mode 100644 (file)
index 0000000..71f23cb
--- /dev/null
@@ -0,0 +1,43 @@
+From e586c4e22d55a294830d49859e782dfe319eb514 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 21:49:51 +0900
+Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
+
+mmio_reset_data() is called during tracer initialization, reset, and
+start. While it resets overrun_detected and prev_overruns, it neglects
+to reset dropped_count. Consequently, dropped event counts from prior
+tracing sessions persist in dropped_count and corrupt overrun reports
+in subsequent runs.
+
+Fix this by explicitly calling atomic_set(&dropped_count, 0) in
+mmio_reset_data().
+
+Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2
+Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording")
+Assisted-by: Antigravity:gemini-3.6-flash
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_mmiotrace.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
+index b88b8d9923adb..e064ba3f28cb9 100644
+--- a/kernel/trace/trace_mmiotrace.c
++++ b/kernel/trace/trace_mmiotrace.c
+@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
+ {
+       overrun_detected = false;
+       prev_overruns = 0;
++      atomic_set(&dropped_count, 0);
+       tracing_reset_online_cpus(&tr->array_buffer);
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch b/queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch
new file mode 100644 (file)
index 0000000..948f208
--- /dev/null
@@ -0,0 +1,57 @@
+From d135f59e005549ade412c44e0c7501de83892473 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 14:43:22 +0800
+Subject: wifi: ath12k: fix out-of-bounds clear_bit in
+ ath12k_mac_dp_peer_cleanup()
+
+From: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+
+[ Upstream commit 47abd2ca281531deee38a3b3770d885e270e9fc9 ]
+
+ath12k_mac_dp_peer_cleanup() clears the ML peer ID slot on the
+free_ml_peer_id_map bitmap by indexing it with dp_peer->peer_id. That is
+wrong: dp_peer->peer_id for an MLO peer always carries the
+ATH12K_PEER_ML_ID_VALID bit (BIT(13)), so clear_bit() is invoked with
+index >= 0x2000, which is far outside the bitmap of ATH12K_MAX_MLO_PEERS
+(256) bits and corrupts memory adjacent to ah->free_ml_peer_id_map. The
+intended bitmap entry also never gets cleared, so subsequent
+ath12k_peer_ml_alloc() calls eventually run out of IDs.
+
+The ID without the VALID bit is what ath12k_peer_ml_alloc() returned and
+is stored in ahsta->ml_peer_id. Use that instead.
+
+While there, also reset ahsta->ml_peer_id to ATH12K_MLO_PEER_ID_INVALID so
+the bitmap and ahsta->ml_peer_id stay in sync.
+
+Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
+
+Fixes: ee16dcf573d5 ("wifi: ath12k: Define ath12k_dp_peer structure & APIs for create & delete")
+Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-1-630632758a80@oss.qualcomm.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/ath/ath12k/mac.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
+index 7408c2577dc2e..d13fb31d13014 100644
+--- a/drivers/net/wireless/ath/ath12k/mac.c
++++ b/drivers/net/wireless/ath/ath12k/mac.c
+@@ -1283,8 +1283,11 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
+       spin_lock_bh(&dp_hw->peer_lock);
+       list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
+               if (dp_peer->is_mlo) {
++                      struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta);
++
+                       rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
+-                      clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map);
++                      clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
++                      ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
+               }
+               list_move(&dp_peer->list, &peers);
+-- 
+2.53.0
+
diff --git a/queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch
new file mode 100644 (file)
index 0000000..7f68a35
--- /dev/null
@@ -0,0 +1,52 @@
+From 6e3d95caf6d624fa77a4d4cfd1fc6a96c2787a48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 09:09:28 +0800
+Subject: wifi: mac80211: validate individual TWT params before driver setup
+
+From: Zhao Li <enderaoelyther@gmail.com>
+
+[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
+
+ieee80211_process_rx_twt_action() only partially validates a received
+S1G TWT setup frame before queueing it.
+
+An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup()
+with twt->length too short for the full struct ieee80211_twt_params.
+
+The individual path passes twt to drv_add_twt_setup(). Both the tracepoint
+and the driver callback consume the complete parameters block, not merely
+req_type. Do not pass a short individual agreement to the driver.
+Broadcast agreements remain unchanged because they are rejected locally
+after accessing only req_type.
+
+Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
+Assisted-by: Codex:gpt-5
+Assisted-by: Claude:opus-4.8
+Signed-off-by: Zhao Li <enderaoelyther@gmail.com>
+Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com
+[edit commit message to not overclaim lack of validation nor
+ understate driver impact]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/s1g.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
+index 5af4a0c6c6424..abc338e22e59c 100644
+--- a/net/mac80211/s1g.c
++++ b/net/mac80211/s1g.c
+@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
+       struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.s1g.variable;
+       struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
++      if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
++          twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
++              return;
++
+       twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
+       /* broadcast TWT not supported yet */
+-- 
+2.53.0
+
diff --git a/queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch b/queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch
new file mode 100644 (file)
index 0000000..27e3872
--- /dev/null
@@ -0,0 +1,54 @@
+From 40ac3e5e75bad5acf1a1b85db3a84cb6a17efa97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 20:08:04 -0300
+Subject: x86/boot: Add volatile, clobbers and zero-length test in memcmp()
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit a8c171c107c0b61a5e7e10cedab0fb72aeaf640d ]
+
+Add the volatile qualifier and clobbers parameter to prevent bugs with
+instruction reordering and optimization.
+
+Also add TEST for the zero-length case to set ZF, as, if the count register
+is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF
+flag undetermined.
+
+  [ bp: Add a comment about the len==0 case. ]
+
+Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code")
+Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com
+Suggested-by: Borislav Petkov <bp@alien8.de>
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/boot/string.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
+index b25c6a9303b73..3a2bba7c25e9d 100644
+--- a/arch/x86/boot/string.c
++++ b/arch/x86/boot/string.c
+@@ -32,8 +32,15 @@
+ int memcmp(const void *s1, const void *s2, size_t len)
+ {
+       bool diff;
+-      asm("repe cmpsb"
+-          : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len));
++
++      /*
++       * Make sure ZF is properly set in the len==0 case because in it,
++       * RCX==0 and the REPE; CMPSB won't get executed.
++       */
++      asm volatile("test %3, %3\n\t"
++                   "repe cmpsb"
++                   : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)
++                   : : "cc", "memory");
+       return diff;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch b/queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch
new file mode 100644 (file)
index 0000000..4fb9683
--- /dev/null
@@ -0,0 +1,144 @@
+From 18d3d050b25025fb9eb3bd9e21df259f44e56345 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:05 +0200
+Subject: xsk: drain continuation descs after overflow in xsk_build_skb()
+
+From: Jason Xing <kernelxing@tencent.com>
+
+[ Upstream commit bd44a6dcd4248883de90f5dad53ae80066e27096 ]
+
+Fix generic xmit path multi-buffer logic when packets are either too big
+(count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is
+included in fragmented packet. Introduce xdp_sock::drain_cont and act
+upon this flag - when it is set, keep on consuming descriptors from
+AF_XDP Tx ring and put them directly onto Cq. Previously these
+descriptors were silently lost and could never be reached again.
+
+Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
+Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/
+Reviewed-by: Jason Xing <kernelxing@tencent.com>
+Co-developed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com> # wrapped cq addr submission onto routine
+Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Signed-off-by: Jason Xing <kernelxing@tencent.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xdp_sock.h |  1 +
+ net/xdp/xsk.c          | 45 +++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
+index ebac60a3d8a17..8b51876efbed1 100644
+--- a/include/net/xdp_sock.h
++++ b/include/net/xdp_sock.h
+@@ -80,6 +80,7 @@ struct xdp_sock {
+        * call of __xsk_generic_xmit().
+        */
+       struct sk_buff *skb;
++      bool drain_cont;
+       struct list_head map_list;
+       /* Protects map_list */
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 0a6203c425766..8dadc39ca81f3 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -736,6 +736,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
+       spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
+ }
++static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool,
++                                           struct xdp_desc *desc)
++{
++      unsigned long flags;
++      u32 idx;
++
++      spin_lock_irqsave(&pool->cq_prod_lock, flags);
++      idx = xskq_get_prod(pool->cq);
++      xskq_prod_write_addr(pool->cq, idx, desc->addr);
++      xskq_prod_submit_n(pool->cq, 1);
++      spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
++}
++
+ static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n)
+ {
+       spin_lock(&pool->cq->cq_cached_prod_lock);
+@@ -1027,13 +1040,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+ static int __xsk_generic_xmit(struct sock *sk)
+ {
+       struct xdp_sock *xs = xdp_sk(sk);
+-      bool sent_frame = false;
+       struct xdp_desc desc;
+       struct sk_buff *skb;
++      u32 cached_cons;
+       u32 max_batch;
+       int err = 0;
+       mutex_lock(&xs->mutex);
++      cached_cons = xs->tx->cached_cons;
+       /* Since we dropped the RCU read lock, the socket state might have changed. */
+       if (unlikely(!xsk_is_bound(xs))) {
+@@ -1062,11 +1076,21 @@ static int __xsk_generic_xmit(struct sock *sk)
+                       goto out;
+               }
++              if (unlikely(xs->drain_cont)) {
++                      xsk_cq_submit_addr_single_locked(xs->pool, &desc);
++                      xs->tx->invalid_descs++;
++                      xskq_cons_release(xs->tx);
++                      xs->drain_cont = xp_mb_desc(&desc);
++                      continue;
++              }
++
+               skb = xsk_build_skb(xs, &desc);
+               if (IS_ERR(skb)) {
+                       err = PTR_ERR(skb);
+                       if (err != -EOVERFLOW)
+                               goto out;
++                      if (xp_mb_desc(&desc))
++                              xs->drain_cont = true;
+                       err = 0;
+                       continue;
+               }
+@@ -1095,18 +1119,33 @@ static int __xsk_generic_xmit(struct sock *sk)
+                       goto out;
+               }
+-              sent_frame = true;
+               xs->skb = NULL;
+       }
+       if (xskq_has_descs(xs->tx)) {
++              bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc);
++
++              err = xsk_cq_reserve_locked(xs->pool);
++              if (err) {
++                      xs->tx->invalid_descs--;
++                      if (xs->skb)
++                              xsk_drop_skb(xs->skb);
++                      xs->drain_cont = drain;
++                      err = -EAGAIN;
++                      goto out;
++              }
++
+               if (xs->skb)
+                       xsk_drop_skb(xs->skb);
++
++              xsk_cq_submit_addr_single_locked(xs->pool, &desc);
++
+               xskq_cons_release(xs->tx);
++              xs->drain_cont = xp_mb_desc(&desc);
+       }
+ out:
+-      if (sent_frame)
++      if (xs->tx->cached_cons != cached_cons)
+               __xsk_tx_release(xs);
+       mutex_unlock(&xs->mutex);
+-- 
+2.53.0
+
diff --git a/queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch b/queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch
new file mode 100644 (file)
index 0000000..961fcf1
--- /dev/null
@@ -0,0 +1,106 @@
+From cdcda7c54b36ee4bda5141c242e2a5d28c774ee8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:04 +0200
+Subject: xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx
+
+From: Jason Xing <kernelxing@tencent.com>
+
+[ Upstream commit a3c8382ebce4780c6b3ace2c09bc342313ac0186 ]
+
+This patch is inspired by the check[1] from sashiko. It says when
+overflow happens, the address of cq to be published is invalid.
+Actually the severer thing is the whole process of publishing the
+address of cq in this particular case is not right: it should truely
+publish the address and advance the cached_prod in cq as long as it
+reads descriptors from txq.
+
+The following is the full analysis.
+xsk_drop_skb() is called in three places, which all discard a partially
+built multi-buffer skb:
+1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS
+2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in
+   the TX ring prevents the partial packet from completing
+3) xsk_release(): socket close while xs->skb holds an incomplete packet
+
+In all three cases, the TX descriptors for the already-processed frags
+have been consumed from the TX ring (xskq_cons_release), and CQ slots
+have been reserved. However, xsk_drop_skb() calls xsk_consume_skb()
+which cancels the CQ reservations via xsk_cq_cancel_locked(). Since
+the buffer addresses never appear in the completion queue, userspace
+permanently loses track of these buffers.
+
+Fix this by letting consume_skb() trigger the existing xsk_destruct_skb
+destructor, which already submits buffer addresses to the CQ via
+xsk_cq_submit_addr_locked().
+
+Note that cancelling the descriptors back to the TX ring (via
+xskq_cons_cancel_n) is not a appropriate option because an oversized
+packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely,
+which is an obviously deadlock bug in the TX path.
+
+Also move the desc->addr assignment in xsk_build_skb() above the
+overflow check so that the current descriptor's address is recorded
+before a potential -EOVERFLOW jump to free_err, consistent with the
+zerocopy path in xsk_build_skb_zerocopy().
+
+[1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/
+
+Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
+Acked-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Signed-off-by: Jason Xing <kernelxing@tencent.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xdp/xsk.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index f8c8a8c9dfba5..0a6203c425766 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -793,8 +793,11 @@ static void xsk_consume_skb(struct sk_buff *skb)
+ static void xsk_drop_skb(struct sk_buff *skb)
+ {
+-      xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb);
+-      xsk_consume_skb(skb);
++      struct xdp_sock *xs = xdp_sk(skb->sk);
++
++      xs->tx->invalid_descs += xsk_get_num_desc(skb);
++      consume_skb(skb);
++      xs->skb = NULL;
+ }
+ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
+@@ -876,7 +879,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
+                       return ERR_PTR(-ENOMEM);
+               /* in case of -EOVERFLOW that could happen below,
+-               * xsk_consume_skb() will release this node as whole skb
++               * xsk_drop_skb() will release this node as whole skb
+                * would be dropped, which implies freeing all list elements
+                */
+               xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
+@@ -968,6 +971,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+                               goto free_err;
+                       }
++                      xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
++
+                       if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) {
+                               err = -EOVERFLOW;
+                               goto free_err;
+@@ -985,8 +990,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+                       skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE);
+                       refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc);
+-
+-                      xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
+               }
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch b/queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch
new file mode 100644 (file)
index 0000000..7ee4fcc
--- /dev/null
@@ -0,0 +1,140 @@
+From 22d91d590e32e553428a11d0da6301eb3b67d577 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:06 +0200
+Subject: xsk: provide sufficient space in pool->tx_descs
+
+From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+
+[ Upstream commit 08c9a8e794b4694c100dafcb80e069e29ad81b64 ]
+
+The temporary Tx descriptor array in an XSK buffer pool is currently
+sized from the Tx ring of the socket that creates the pool.
+
+This is insufficient for shared-UMEM Tx. A later socket may have a
+larger Tx ring and submit a valid multi-buffer packet containing more
+descriptors than the first socket's ring, while still remaining within
+the device's xdp_zc_max_segs limit.
+
+A packet-framed batch parser bounded by the temporary array cannot reach
+the end-of-packet descriptor in that case. It leaves the packet on the
+Tx ring and encounters the same packet on every subsequent attempt,
+stalling Tx processing for that socket.
+
+Size the temporary descriptor array to the larger of the first Tx ring
+and the device's xdp_zc_max_segs capability. This keeps the array large
+enough to inspect one maximum-sized valid packet. Larger shared Tx rings
+do not require further resizing, as they can be processed over multiple
+batches.
+
+Following commit will actually address the data path side.
+
+Fixes: d5581966040f ("xsk: support ZC Tx multi-buffer in batch API")
+Reviewed-by: Jason Xing <kernelxing@tencent.com>
+Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Link: https://patch.msgid.link/20260719135609.147823-4-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xsk_buff_pool.h |  6 ++++--
+ net/xdp/xsk.c               | 10 +++++++---
+ net/xdp/xsk_buff_pool.c     | 12 ++++++++----
+ 3 files changed, 19 insertions(+), 9 deletions(-)
+
+diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h
+index ccb3b350001f2..f5e737a830559 100644
+--- a/include/net/xsk_buff_pool.h
++++ b/include/net/xsk_buff_pool.h
+@@ -102,12 +102,14 @@ struct xsk_buff_pool {
+ /* AF_XDP core. */
+ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
+-                                              struct xdp_umem *umem);
++                                              struct xdp_umem *umem,
++                                              u32 max_segs);
+ int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *dev,
+                 u16 queue_id, u16 flags);
+ int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs,
+                        struct net_device *dev, u16 queue_id);
+-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs);
++int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
++                    u32 max_segs);
+ void xp_destroy(struct xsk_buff_pool *pool);
+ void xp_get_pool(struct xsk_buff_pool *pool);
+ bool xp_put_pool(struct xsk_buff_pool *pool);
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 8dadc39ca81f3..161ec3d47f053 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -1524,7 +1524,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
+                        * and/or device.
+                        */
+                       xs->pool = xp_create_and_assign_umem(xs,
+-                                                           umem_xs->umem);
++                                                           umem_xs->umem,
++                                                           dev->xdp_zc_max_segs);
+                       if (!xs->pool) {
+                               err = -ENOMEM;
+                               sockfd_put(sock);
+@@ -1556,7 +1557,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
+                        * utilizes
+                        */
+                       if (xs->tx && !xs->pool->tx_descs) {
+-                              err = xp_alloc_tx_descs(xs->pool, xs);
++                              err = xp_alloc_tx_descs(xs->pool, xs,
++                                                      dev->xdp_zc_max_segs);
+                               if (err) {
+                                       xp_put_pool(xs->pool);
+                                       xs->pool = NULL;
+@@ -1574,7 +1576,9 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
+               goto out_unlock;
+       } else {
+               /* This xsk has its own umem. */
+-              xs->pool = xp_create_and_assign_umem(xs, xs->umem);
++              xs->pool = xp_create_and_assign_umem(xs, xs->umem,
++                                                   dev->xdp_zc_max_segs);
++
+               if (!xs->pool) {
+                       err = -ENOMEM;
+                       goto out_unlock;
+diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
+index d981cfdd85357..419dc0ffbf7ef 100644
+--- a/net/xdp/xsk_buff_pool.c
++++ b/net/xdp/xsk_buff_pool.c
+@@ -42,9 +42,12 @@ void xp_destroy(struct xsk_buff_pool *pool)
+       kvfree(pool);
+ }
+-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs)
++int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
++                    u32 max_segs)
+ {
+-      pool->tx_descs = kvzalloc_objs(*pool->tx_descs, xs->tx->nentries);
++      u32 nentries = max(xs->tx->nentries, max_segs);
++
++      pool->tx_descs = kvzalloc_objs(*pool->tx_descs, nentries);
+       if (!pool->tx_descs)
+               return -ENOMEM;
+@@ -52,7 +55,8 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs)
+ }
+ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
+-                                              struct xdp_umem *umem)
++                                              struct xdp_umem *umem,
++                                              u32 max_segs)
+ {
+       bool unaligned = umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG;
+       struct xsk_buff_pool *pool;
+@@ -69,7 +73,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
+               goto out;
+       if (xs->tx)
+-              if (xp_alloc_tx_descs(pool, xs))
++              if (xp_alloc_tx_descs(pool, xs, max_segs))
+                       goto out;
+       pool->chunk_mask = ~((u64)umem->chunk_size - 1);
+-- 
+2.53.0
+
diff --git a/queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch b/queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch
new file mode 100644 (file)
index 0000000..cd7b698
--- /dev/null
@@ -0,0 +1,516 @@
+From a90131b8674f9b2600302b4c6ae084011afb4a17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 15:56:07 +0200
+Subject: xsk: reclaim invalid Tx descriptors in ZC batch path
+
+From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+
+[ Upstream commit 72f2b4516faf55d4dfac2414649d3cffa5fd2c5e ]
+
+The zero-copy Tx batch parser stops when it encounters an invalid
+descriptor. If this happens after one or more continuation descriptors,
+the Tx consumer can be advanced past fragments that are neither submitted
+to the driver nor returned to userspace through the completion ring.
+
+A similar problem occurs when a packet exceeds xdp_zc_max_segs. The
+descriptors consumed up to the limit are released without completion, and
+the remaining continuation descriptors can subsequently be interpreted
+as the beginning of another packet.
+
+Parse Tx batches in packet units and distinguish descriptors belonging to
+complete valid packets from descriptors consumed while draining an
+invalid or oversized packet. Return the former to the driver and append
+the latter to the CQ address area so userspace can reclaim their UMEM
+frames.
+
+Treat a standalone invalid descriptor as a one-descriptor reclaim-only
+packet. Advancing the Tx-ring consumer releases the ring slot, but does
+not by itself return ownership of the referenced UMEM frame to userspace.
+
+Once draining starts, continue until the packet's end-of-packet
+descriptor is consumed. Preserve the drain state on the socket when EOP
+has not yet been supplied, so draining can continue during a later call.
+Leave incomplete but otherwise valid packets on the Tx ring.
+
+Shared-UMEM pools using multi-buffer Tx also need packet-framed parsing.
+Walk their Tx sockets one packet at a time, preserving the existing
+per-socket fairness scheme, instead of using the legacy one-descriptor
+fallback. Keep that fallback for shared pools that do not use
+multi-buffer Tx. Since the drain state is maintained per socket and both
+the singular and shared paths can resume an interrupted drain, changing
+the socket list from singular to shared requires no special bind-time
+transition.
+
+CQ entries are positional, and drivers may complete only part of the Tx
+work returned by xsk_tx_peek_release_desc_batch(). Therefore, reclaim-only
+entries cannot be published immediately when earlier driver-visible
+descriptors are still outstanding.
+
+Track the number of driver-visible CQ entries preceding the reclaim
+entries. Let xsk_tx_completed() publish partial hardware Tx completions,
+and publish the reclaim entries only after every earlier Tx descriptor
+has completed. Complete a reclaim-only batch immediately when there is no
+driver-visible work in front of it, and prevent another Tx batch from
+being appended while reclaim entries remain pending.
+
+Also cap batch processing by the size of the pool's temporary descriptor
+array, as Tx rings belonging to sockets sharing a UMEM may have different
+sizes.
+
+This ensures that every invalid Tx descriptor consumed by the ZC batch
+path is either submitted to the driver as part of a valid packet or
+returned to userspace without violating CQ completion ordering.
+
+Reviewed-by: Jason Xing <kernelxing@tencent.com>
+Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
+Acked-by: Stanislav Fomichev <sdf@fomichev.me>
+Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
+Link: https://patch.msgid.link/20260719135609.147823-5-maciej.fijalkowski@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/networking/af_xdp.rst |  54 ++++----
+ include/net/xsk_buff_pool.h         |   3 +
+ net/xdp/xsk.c                       | 187 +++++++++++++++++++++++++---
+ net/xdp/xsk_buff_pool.c             |   1 +
+ net/xdp/xsk_queue.h                 |  65 +++++++---
+ 5 files changed, 248 insertions(+), 62 deletions(-)
+
+diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst
+index 50d92084a49c6..cc3f0d16b28fb 100644
+--- a/Documentation/networking/af_xdp.rst
++++ b/Documentation/networking/af_xdp.rst
+@@ -43,12 +43,13 @@ UMEM also has two rings: the FILL ring and the COMPLETION ring. The
+ FILL ring is used by the application to send down addr for the kernel
+ to fill in with RX packet data. References to these frames will then
+ appear in the RX ring once each packet has been received. The
+-COMPLETION ring, on the other hand, contains frame addr that the
+-kernel has transmitted completely and can now be used again by user
+-space, for either TX or RX. Thus, the frame addrs appearing in the
+-COMPLETION ring are addrs that were previously transmitted using the
+-TX ring. In summary, the RX and FILL rings are used for the RX path
+-and the TX and COMPLETION rings are used for the TX path.
++COMPLETION ring, on the other hand, contains frame addresses from Tx
++descriptors that the kernel has finished processing and that can now be
++used again by user space, for either Tx or Rx. This includes frames whose
++transmission has completed as well as frames referenced by invalid Tx
++descriptors rejected by the kernel. A completion therefore returns
++ownership of a frame to user space, but does not by itself guarantee that
++the packet was successfully transmitted.
+ The socket is then finally bound with a bind() call to a device and a
+ specific queue id on that device, and it is not until bind is
+@@ -169,14 +170,15 @@ chunks mode, then the incoming addr will be left untouched.
+ UMEM Completion Ring
+ ~~~~~~~~~~~~~~~~~~~~
+-The COMPLETION Ring is used transfer ownership of UMEM frames from
++The COMPLETION Ring is used to transfer ownership of UMEM frames from
+ kernel-space to user-space. Just like the FILL ring, UMEM indices are
+-used.
+-
+-Frames passed from the kernel to user-space are frames that has been
+-sent (TX ring) and can be used by user-space again.
+-
+-The user application consumes UMEM addrs from this ring.
++used. Frames passed from the kernel to user-space are frames referenced
++by Tx descriptors that the kernel has finished processing and can be
++used by user-space again. This includes both frames whose transmission
++has completed and frames referenced by invalid Tx descriptors that were
++rejected and reclaimed by the kernel. A completion entry does not
++guarantee successful packet transmission. The user application consumes
++UMEM addrs from this ring.
+ RX Ring
+@@ -504,21 +506,25 @@ will be treated as an invalid descriptor.
+ These are the semantics for producing packets onto AF_XDP Tx ring
+ consisting of multiple frames:
+-* When an invalid descriptor is found, all the other
+-  descriptors/frames of this packet are marked as invalid and not
+-  completed. The next descriptor is treated as the start of a new
+-  packet, even if this was not the intent (because we cannot guess
+-  the intent). As before, if your program is producing invalid
+-  descriptors you have a bug that must be fixed.
++* When an invalid descriptor is found, the complete packet is treated as
++  invalid. The kernel consumes descriptors through the descriptor marking
++  the end of the packet and returns all their frame addresses through the
++  COMPLETION ring. A standalone invalid descriptor is treated as a
++  one-descriptor invalid packet. The descriptor following the end of the
++  invalid packet is treated as the start of a new packet. As before, if
++  your program is producing invalid descriptors you have a bug that must
++  be fixed. Rejected descriptors are reported in the ``tx_invalid_descs``
++  statistic.
+ * Zero length descriptors are treated as invalid descriptors.
+ * For copy mode, the maximum supported number of frames in a packet is
+-  equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all
+-  descriptors accumulated so far are dropped and treated as
+-  invalid. To produce an application that will work on any system
+-  regardless of this config setting, limit the number of frags to 18,
+-  as the minimum value of the config is 17.
++  equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all descriptors
++  through the end of the oversized packet are consumed, treated as invalid,
++  and their frame addresses are returned through the COMPLETION ring. To
++  produce an application that will work on any system regardless of this
++  config setting, limit the number of frags to 18, as the minimum value of
++  the config is 17.
+ * For zero-copy mode, the limit is up to what the NIC HW
+   supports. Usually at least five on the NICs we have checked. We
+diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h
+index f5e737a830559..2bb1d122b1bc6 100644
+--- a/include/net/xsk_buff_pool.h
++++ b/include/net/xsk_buff_pool.h
+@@ -78,6 +78,9 @@ struct xsk_buff_pool {
+       u32 chunk_size;
+       u32 chunk_shift;
+       u32 frame_len;
++      u32 tx_descs_nentries;
++      u32 reclaim_descs;
++      u32 tx_zc_pending_descs;
+       u32 xdp_zc_max_segs;
+       u8 tx_metadata_len; /* inherited from umem */
+       u8 cached_need_wakeup;
+diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
+index 161ec3d47f053..855fe92358bac 100644
+--- a/net/xdp/xsk.c
++++ b/net/xdp/xsk.c
+@@ -498,6 +498,23 @@ void __xsk_map_flush(struct list_head *flush_list)
+ void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries)
+ {
++      u32 reclaim_descs = READ_ONCE(pool->reclaim_descs);
++
++      if (unlikely(reclaim_descs)) {
++              u32 pending_descs = READ_ONCE(pool->tx_zc_pending_descs);
++
++              if (nb_entries < pending_descs) {
++                      WRITE_ONCE(pool->tx_zc_pending_descs,
++                                 pending_descs - nb_entries);
++                      xskq_prod_submit_n(pool->cq, nb_entries);
++                      return;
++              }
++
++              WRITE_ONCE(pool->tx_zc_pending_descs, 0);
++              nb_entries += reclaim_descs;
++              WRITE_ONCE(pool->reclaim_descs, 0);
++      }
++
+       xskq_prod_submit_n(pool->cq, nb_entries);
+ }
+ EXPORT_SYMBOL(xsk_tx_completed);
+@@ -573,24 +590,157 @@ static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entr
+       return nb_pkts;
+ }
++static void xsk_tx_commit_batch(struct xsk_buff_pool *pool,
++                              struct xsk_tx_batch *batch)
++{
++      u32 nb_descs = xsk_tx_batch_cq_descs(batch);
++      u32 cq_cached_prod;
++
++      if (!nb_descs)
++              return;
++
++      cq_cached_prod = pool->cq->cached_prod;
++      xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_descs);
++
++      if (unlikely(batch->reclaim_descs)) {
++              u32 cq_pending_descs;
++
++              /* CQ is positional. Descriptors already written but not
++               * submitted must complete before any reclaim-only descriptors
++               * appended below.
++               */
++              cq_pending_descs = cq_cached_prod - xskq_get_prod(pool->cq);
++
++              WRITE_ONCE(pool->tx_zc_pending_descs,
++                         batch->tx_descs + cq_pending_descs);
++              WRITE_ONCE(pool->reclaim_descs, batch->reclaim_descs);
++              if (unlikely(!pool->tx_zc_pending_descs))
++                      xsk_tx_completed(pool, 0);
++      }
++}
++
++static struct xsk_tx_batch
++__xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_sock *xs,
++                               struct xdp_desc *descs, u32 max_descs)
++{
++      struct xsk_tx_batch batch = {};
++      u32 entries;
++
++      entries = xskq_cons_nb_entries(xs->tx, max_descs);
++      if (!entries)
++              return batch;
++
++      batch = xskq_cons_read_desc_batch(xs, pool, descs, max_descs);
++      if (!xsk_tx_batch_cq_descs(&batch)) {
++              xs->tx->queue_empty_descs++;
++      } else {
++              __xskq_cons_release(xs->tx);
++              xs->sk.sk_write_space(&xs->sk);
++      }
++      return batch;
++}
++
++static struct xsk_tx_batch
++xsk_tx_peek_release_shared_desc_batch(struct xsk_buff_pool *pool, u32 max_descs)
++{
++      u32 cq_descs_before, cq_descs_after;
++      struct xsk_tx_batch sum_batch = {};
++      bool budget_exhausted;
++      u32 per_socket_budget;
++      struct xdp_sock *xs;
++
++      /* The fairness quota must allow one maximum-sized valid packet. */
++      per_socket_budget = max_t(u32, MAX_PER_SOCKET_BUDGET,
++                                pool->xdp_zc_max_segs);
++
++again:
++      budget_exhausted = false;
++      cq_descs_before = xsk_tx_batch_cq_descs(&sum_batch);
++      list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) {
++              u32 budget, budget_left, offset, remaining, used;
++              struct xsk_tx_batch curr_batch;
++
++              /* Once reclaim-only descriptors have been appended to the CQ
++               * address area, do not append driver-visible Tx descriptors
++               * from another socket after them. xsk_tx_completed() relies on
++               * all driver-visible descriptors preceding all reclaim-only
++               * descriptors in CQ order.
++               */
++              if (sum_batch.reclaim_descs)
++                      break;
++
++              /* be gentle when playing with pool->tx_descs */
++              offset = xsk_tx_batch_cq_descs(&sum_batch);
++              if (offset >= max_descs)
++                      break;
++
++              if (xs->tx_budget_spent >= per_socket_budget) {
++                      if (xskq_cons_nb_entries(xs->tx, 1))
++                              budget_exhausted = true;
++                      continue;
++              }
++
++              budget_left = per_socket_budget - xs->tx_budget_spent;
++              remaining = max_descs - offset;
++              budget = min(remaining, budget_left);
++
++              curr_batch = __xsk_tx_peek_release_desc_batch(pool, xs,
++                                                            pool->tx_descs + offset,
++                                                            budget);
++              used = xsk_tx_batch_cq_descs(&curr_batch);
++              if (!used) {
++                      if (curr_batch.budget_limited && budget_left < remaining)
++                              budget_exhausted = true;
++                      continue;
++              }
++
++              xs->tx_budget_spent += used;
++              sum_batch.tx_descs += curr_batch.tx_descs;
++              sum_batch.reclaim_descs = curr_batch.reclaim_descs;
++      }
++
++      cq_descs_after = xsk_tx_batch_cq_descs(&sum_batch);
++
++      if (sum_batch.reclaim_descs || cq_descs_after >= max_descs)
++              return sum_batch;
++
++      /* Continue filling the batch while this pass made progress */
++      if (cq_descs_before != cq_descs_after)
++              goto again;
++
++      if (!budget_exhausted)
++              return sum_batch;
++
++      list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list)
++              xs->tx_budget_spent = 0;
++      goto again;
++}
++
+ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
+ {
++      struct xsk_tx_batch batch = {};
+       struct xdp_sock *xs;
++      bool umem_shared;
+       rcu_read_lock();
+-      if (!list_is_singular(&pool->xsk_tx_list)) {
+-              /* Fallback to the non-batched version */
+-              rcu_read_unlock();
+-              return xsk_tx_peek_release_fallback(pool, nb_pkts);
+-      }
++      if (unlikely(READ_ONCE(pool->reclaim_descs)))
++              goto out;
+-      xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list);
+-      if (!xs) {
+-              nb_pkts = 0;
++      xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock,
++                                  tx_list);
++      if (!xs)
+               goto out;
+-      }
+-      nb_pkts = xskq_cons_nb_entries(xs->tx, nb_pkts);
++      nb_pkts = min(nb_pkts, pool->tx_descs_nentries);
++      if (!nb_pkts)
++              goto out;
++
++      umem_shared = !list_is_singular(&pool->xsk_tx_list);
++
++      if (umem_shared && !(pool->umem->flags & XDP_UMEM_SG_FLAG)) {
++              rcu_read_unlock();
++              return xsk_tx_peek_release_fallback(pool, nb_pkts);
++      }
+       /* This is the backpressure mechanism for the Tx path. Try to
+        * reserve space in the completion queue for all packets, but
+@@ -602,19 +752,16 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
+       if (!nb_pkts)
+               goto out;
+-      nb_pkts = xskq_cons_read_desc_batch(xs->tx, pool, nb_pkts);
+-      if (!nb_pkts) {
+-              xs->tx->queue_empty_descs++;
+-              goto out;
+-      }
+-
+-      __xskq_cons_release(xs->tx);
+-      xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_pkts);
+-      xs->sk.sk_write_space(&xs->sk);
++      batch = umem_shared ?
++              xsk_tx_peek_release_shared_desc_batch(pool, nb_pkts) :
++              __xsk_tx_peek_release_desc_batch(pool, xs,
++                                               pool->tx_descs,
++                                               nb_pkts);
++      xsk_tx_commit_batch(pool, &batch);
+ out:
+       rcu_read_unlock();
+-      return nb_pkts;
++      return batch.tx_descs;
+ }
+ EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch);
+diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
+index 419dc0ffbf7ef..4538223f44082 100644
+--- a/net/xdp/xsk_buff_pool.c
++++ b/net/xdp/xsk_buff_pool.c
+@@ -51,6 +51,7 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
+       if (!pool->tx_descs)
+               return -ENOMEM;
++      pool->tx_descs_nentries = nentries;
+       return 0;
+ }
+diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
+index 3e3fbb73d23e2..1bc42c8902f4b 100644
+--- a/net/xdp/xsk_queue.h
++++ b/net/xdp/xsk_queue.h
+@@ -58,6 +58,17 @@ struct parsed_desc {
+       u32 valid;
+ };
++struct xsk_tx_batch {
++      u32 tx_descs;
++      u32 reclaim_descs;
++      bool budget_limited;
++};
++
++static inline u32 xsk_tx_batch_cq_descs(const struct xsk_tx_batch *batch)
++{
++      return batch->tx_descs + batch->reclaim_descs;
++}
++
+ /* The structure of the shared state of the rings are a simple
+  * circular buffer, as outlined in
+  * Documentation/core-api/circular-buffers.rst. For the Rx and
+@@ -263,17 +274,18 @@ static inline void parse_desc(struct xsk_queue *q, struct xsk_buff_pool *pool,
+       parsed->mb = xp_mb_desc(desc);
+ }
+-static inline
+-u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
+-                            u32 max)
++static inline struct xsk_tx_batch
++xskq_cons_read_desc_batch(struct xdp_sock *xs, struct xsk_buff_pool *pool,
++                        struct xdp_desc *descs, u32 max)
+ {
+-      u32 cached_cons = q->cached_cons, nb_entries = 0;
+-      struct xdp_desc *descs = pool->tx_descs;
+-      u32 total_descs = 0, nr_frags = 0;
++      bool drain = READ_ONCE(xs->drain_cont);
++      u32 cached_cons, nb_entries = 0;
++      struct xsk_tx_batch batch = {};
++      struct xsk_queue *q = xs->tx;
++      u32 nr_frags = 0;
++
++      cached_cons = q->cached_cons;
+-      /* track first entry, if stumble upon *any* invalid descriptor, rewind
+-       * current packet that consists of frags and stop the processing
+-       */
+       while (cached_cons != q->cached_prod && nb_entries < max) {
+               struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring;
+               u32 idx = cached_cons & q->ring_mask;
+@@ -283,25 +295,42 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
+               cached_cons++;
+               parse_desc(q, pool, &descs[nb_entries], &parsed);
+               if (unlikely(!parsed.valid))
+-                      break;
++                      drain = true;
++
++              nr_frags++;
++              nb_entries++;
+               if (likely(!parsed.mb)) {
+-                      total_descs += (nr_frags + 1);
+-                      nr_frags = 0;
+-              } else {
+-                      nr_frags++;
+-                      if (nr_frags == pool->xdp_zc_max_segs) {
++                      if (unlikely(drain)) {
++                              batch.reclaim_descs = nr_frags;
++                              WRITE_ONCE(xs->drain_cont, false);
+                               nr_frags = 0;
+                               break;
+                       }
++
++                      batch.tx_descs += nr_frags;
++                      nr_frags = 0;
++                      continue;
++              }
++
++              if (nr_frags == pool->xdp_zc_max_segs)
++                      drain = true;
++      }
++
++      if (nr_frags) {
++              if (drain) {
++                      batch.reclaim_descs = nr_frags;
++                      WRITE_ONCE(xs->drain_cont, true);
++              } else {
++                      if (nb_entries == max)
++                              batch.budget_limited = true;
++                      cached_cons -= nr_frags;
+               }
+-              nb_entries++;
+       }
+-      cached_cons -= nr_frags;
+       /* Release valid plus any invalid entries */
+       xskq_cons_release_n(q, cached_cons - q->cached_cons);
+-      return total_descs;
++      return batch;
+ }
+ /* Functions for consumers */
+-- 
+2.53.0
+