summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap5
-rw-r--r--CREDITS7
-rw-r--r--Documentation/admin-guide/cgroup-v1/rdma.rst66
-rw-r--r--Documentation/admin-guide/cgroup-v2.rst20
-rw-r--r--Documentation/crypto/libcrypto-blockcipher.rst6
-rw-r--r--Documentation/crypto/libcrypto-hash.rst38
-rw-r--r--Documentation/crypto/libcrypto-signature.rst2
-rw-r--r--Documentation/crypto/libcrypto.rst5
-rw-r--r--Documentation/devicetree/bindings/mmc/mtk-sd.yaml29
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sm8250.yaml1
-rw-r--r--Documentation/scheduler/sched-ext.rst8
-rw-r--r--MAINTAINERS14
-rw-r--r--Makefile2
-rw-r--r--arch/arc/configs/axs101_defconfig1
-rw-r--r--arch/arc/configs/axs103_defconfig1
-rw-r--r--arch/arc/configs/axs103_smp_defconfig1
-rw-r--r--arch/arc/configs/tb10x_defconfig1
-rw-r--r--arch/arc/kernel/smp.c6
-rw-r--r--arch/arm/mach-ixp4xx/Kconfig1
-rw-r--r--arch/arm/mach-pxa/Kconfig3
-rw-r--r--arch/arm64/boot/dts/nvidia/tegra234.dtsi24
-rw-r--r--arch/arm64/boot/dts/nvidia/tegra264.dtsi4
-rw-r--r--arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts41
-rw-r--r--arch/arm64/include/asm/kvm_nested.h8
-rw-r--r--arch/arm64/kvm/at.c8
-rw-r--r--arch/arm64/kvm/emulate-nested.c37
-rw-r--r--arch/arm64/kvm/hyp/include/hyp/switch.h11
-rw-r--r--arch/arm64/kvm/hyp/nvhe/ffa.c44
-rw-r--r--arch/arm64/kvm/hyp/nvhe/pkvm.c3
-rw-r--r--arch/arm64/kvm/hyp/nvhe/sys_regs.c3
-rw-r--r--arch/arm64/kvm/hyp/pgtable.c15
-rw-r--r--arch/arm64/kvm/inject_fault.c18
-rw-r--r--arch/arm64/kvm/mmio.c7
-rw-r--r--arch/arm64/kvm/nested.c164
-rw-r--r--arch/arm64/kvm/pkvm.c2
-rw-r--r--arch/arm64/kvm/vgic/vgic-its.c2
-rw-r--r--arch/arm64/kvm/vgic/vgic.c20
-rw-r--r--arch/powerpc/include/asm/preempt.h16
-rw-r--r--arch/powerpc/include/asm/uaccess.h2
-rw-r--r--arch/powerpc/kernel/dt_cpu_ftrs.c9
-rw-r--r--arch/powerpc/kernel/time.c4
-rw-r--r--arch/powerpc/lib/vmx-helper.c2
-rw-r--r--arch/powerpc/platforms/85xx/common.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c6
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig1
-rw-r--r--arch/powerpc/platforms/pseries/papr_platform_attributes.c8
-rw-r--r--arch/riscv/Kconfig2
-rw-r--r--arch/riscv/include/asm/cacheflush.h5
-rw-r--r--arch/riscv/include/asm/io.h8
-rw-r--r--arch/riscv/kernel/entry.S2
-rw-r--r--arch/riscv/kernel/sys_hwprobe.c1
-rw-r--r--arch/riscv/kernel/vdso/Makefile6
-rw-r--r--arch/riscv/kernel/vdso/rt_sigreturn.S10
-rw-r--r--arch/riscv/mm/init.c2
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c3
-rw-r--r--arch/s390/kvm/dat.c2
-rw-r--r--arch/s390/kvm/gmap.c7
-rw-r--r--arch/s390/kvm/kvm-s390.c6
-rw-r--r--arch/s390/kvm/pci.c12
-rw-r--r--arch/s390/lib/csum-partial.c2
-rw-r--r--arch/x86/boot/compressed/acpi.c7
-rw-r--r--arch/x86/boot/early_serial_console.c11
-rw-r--r--arch/x86/kernel/cpu/Makefile1
-rw-r--r--arch/x86/kvm/irq.c4
-rw-r--r--arch/x86/kvm/lapic.c8
-rw-r--r--arch/x86/kvm/svm/sev.c6
-rw-r--r--arch/x86/kvm/svm/svm.c7
-rw-r--r--arch/x86/kvm/trace.h2
-rw-r--r--arch/x86/kvm/vmx/nested.c89
-rw-r--r--arch/x86/kvm/vmx/tdx.c6
-rw-r--r--arch/x86/kvm/vmx/vmx.h7
-rw-r--r--block/bio.c64
-rw-r--r--block/blk-cgroup.c4
-rw-r--r--block/blk-map.c9
-rw-r--r--block/blk.h13
-rw-r--r--block/elevator.c9
-rw-r--r--block/error-injection.c7
-rw-r--r--block/partitions/aix.c9
-rw-r--r--crypto/Kconfig4
-rw-r--r--drivers/ata/sata_dwc_460ex.c38
-rw-r--r--drivers/block/loop.c32
-rw-r--r--drivers/bluetooth/btqca.c2
-rw-r--r--drivers/bluetooth/btrtl.c5
-rw-r--r--drivers/bluetooth/hci_qca.c4
-rw-r--r--drivers/cpufreq/cpufreq.c3
-rw-r--r--drivers/cpufreq/intel_pstate.c6
-rw-r--r--drivers/dpll/dpll_netlink.c3
-rw-r--r--drivers/firmware/arm_ffa/driver.c54
-rw-r--r--drivers/firmware/arm_scmi/Kconfig4
-rw-r--r--drivers/firmware/arm_scmi/clock.c11
-rw-r--r--drivers/firmware/arm_scmi/notify.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c10
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c23
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c551
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c70
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c45
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c23
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h116
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c17
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c7
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c16
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c12
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h12
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c45
-rw-r--r--drivers/gpu/drm/drm_panel_backlight_quirks.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c7
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c33
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_user.c2
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_gt_pm.c8
-rw-r--r--drivers/gpu/drm/tegra/sor.c6
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c58
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h3
-rw-r--r--drivers/gpu/drm/xe/xe_bo_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_device.c4
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.c2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_exec_queue_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c70
-rw-r--r--drivers/gpu/drm/xe/xe_guc_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c5
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c21
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h4
-rw-r--r--drivers/gpu/drm/xe/xe_module.c49
-rw-r--r--drivers/gpu/drm/xe/xe_module.h5
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.c9
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c9
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c3
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c32
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.h3
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c33
-rw-r--r--drivers/gpu/drm/xe/xe_vm_madvise.c19
-rw-r--r--drivers/gpu/drm/xe/xe_wopcm.c15
-rw-r--r--drivers/i2c/busses/i2c-imx.c36
-rw-r--r--drivers/i2c/busses/i2c-k1.c2
-rw-r--r--drivers/i2c/busses/i2c-mlxbf.c4
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c2
-rw-r--r--drivers/infiniband/core/cma.c2
-rw-r--r--drivers/infiniband/core/mad.c30
-rw-r--r--drivers/infiniband/core/verbs.c6
-rw-r--r--drivers/infiniband/hw/erdma/erdma_qp.c2
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hem.c2
-rw-r--r--drivers/infiniband/hw/irdma/verbs.c26
-rw-r--r--drivers/infiniband/hw/mana/wr.c2
-rw-r--r--drivers/infiniband/hw/mlx5/wr.c16
-rw-r--r--drivers/infiniband/sw/siw/siw_verbs.c44
-rw-r--r--drivers/memstick/core/ms_block.c4
-rw-r--r--drivers/mmc/core/block.c3
-rw-r--r--drivers/mmc/core/mmc_test.c18
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c91
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c14
-rw-r--r--drivers/mmc/host/vub300.c36
-rw-r--r--drivers/mtd/devices/mchp23k256.c2
-rw-r--r--drivers/mtd/maps/Kconfig6
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/uclinux.c118
-rw-r--r--drivers/mtd/mtd_virt_concat.c8
-rw-r--r--drivers/mtd/mtdcore.c23
-rw-r--r--drivers/mtd/mtdpart.c8
-rw-r--r--drivers/mtd/mtdswap.c5
-rw-r--r--drivers/mtd/nand/ecc-mtk.c24
-rw-r--r--drivers/mtd/nand/onenand/onenand_samsung.c7
-rw-r--r--drivers/mtd/nand/raw/Kconfig1
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c9
-rw-r--r--drivers/mtd/nand/raw/ingenic/ingenic_ecc.c7
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c12
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c12
-rw-r--r--drivers/mtd/nand/raw/ndfc.c2
-rw-r--r--drivers/mtd/nand/spi/core.c2
-rw-r--r--drivers/net/can/Kconfig7
-rw-r--r--drivers/net/can/peak_canfd/peak_canfd.c2
-rw-r--r--drivers/net/can/peak_canfd/peak_canfd_user.h2
-rw-r--r--drivers/net/can/peak_canfd/peak_pciefd_main.c4
-rw-r--r--drivers/net/can/sja1000/peak_pci.c4
-rw-r--r--drivers/net/can/sja1000/peak_pcmcia.c4
-rw-r--r--drivers/net/can/usb/esd_usb.c5
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb.c2
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c4
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.h2
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_fd.c2
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_pro.c2
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_pro.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/st.c1
-rw-r--r--drivers/net/macsec.c7
-rw-r--r--drivers/net/ppp/ppp_generic.c18
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c8
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_rx.c4
-rw-r--r--drivers/net/wireless/intersil/p54/txrx.c8
-rw-r--r--drivers/net/wireless/marvell/libertas/firmware.c1
-rw-r--r--drivers/net/wireless/marvell/libertas/tx.c7
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/main.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/join.c1
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c12
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.c2
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_hal.c8
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c12
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim_main.c16
-rw-r--r--drivers/pmdomain/imx/imx8m-blk-ctrl.c46
-rw-r--r--drivers/pmdomain/imx/imx93-blk-ctrl.c60
-rw-r--r--drivers/pmdomain/mediatek/mtk-pm-domains.c40
-rw-r--r--drivers/regulator/ltc3676.c10
-rw-r--r--drivers/reset/reset-imx7.c6
-rw-r--r--drivers/reset/reset-sunxi.c4
-rw-r--r--drivers/reset/spacemit/reset-spacemit-k3.c2
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c2
-rw-r--r--drivers/scsi/elx/efct/efct_hw.c2
-rw-r--r--drivers/scsi/elx/efct/efct_unsol.c1
-rw-r--r--drivers/scsi/hosts.c2
-rw-r--r--drivers/scsi/hpsa.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c3
-rw-r--r--drivers/scsi/scsi_error.c22
-rw-r--r--drivers/scsi/scsi_lib.c8
-rw-r--r--drivers/scsi/scsi_priv.h2
-rw-r--r--drivers/scsi/sg.c7
-rw-r--r--drivers/soc/tegra/fuse/tegra-apbmisc.c2
-rw-r--r--drivers/spi/spi-cadence-quadspi.c12
-rw-r--r--drivers/spi/spi-dw-dma.c3
-rw-r--r--drivers/target/target_core_fabric_lib.c89
-rw-r--r--drivers/target/target_core_internal.h3
-rw-r--r--drivers/target/target_core_pr.c12
-rw-r--r--drivers/xen/xen-scsiback.c30
-rw-r--r--fs/btrfs/file-item.c26
-rw-r--r--fs/btrfs/free-space-cache.c3
-rw-r--r--fs/btrfs/ioctl.c18
-rw-r--r--fs/btrfs/lzo.c15
-rw-r--r--fs/btrfs/print-tree.c8
-rw-r--r--fs/btrfs/props.c16
-rw-r--r--fs/btrfs/relocation.c15
-rw-r--r--fs/btrfs/tree-checker.c6
-rw-r--r--fs/erofs/ishare.c8
-rw-r--r--fs/erofs/super.c19
-rw-r--r--fs/erofs/zmap.c3
-rw-r--r--fs/nfsd/nfsctl.c7
-rw-r--r--fs/smb/client/cifsfs.c10
-rw-r--r--fs/smb/client/smb1maperror.c24
-rw-r--r--fs/smb/client/smb1maperror_test.c1
-rw-r--r--fs/smb/client/smb2maperror.c12
-rw-r--r--fs/smb/client/smb2maperror_test.c1
-rw-r--r--fs/smb/client/smb2misc.c34
-rw-r--r--fs/smb/client/smb2ops.c183
-rw-r--r--fs/smb/client/smb2pdu.c19
-rw-r--r--fs/smb/client/smb2proto.h3
-rw-r--r--fs/smb/common/fscc.h5
-rw-r--r--fs/smb/server/mgmt/user_session.c25
-rw-r--r--fs/smb/server/mgmt/user_session.h2
-rw-r--r--fs/smb/server/oplock.c6
-rw-r--r--fs/smb/server/smb2misc.c5
-rw-r--r--fs/smb/server/smb2pdu.c50
-rw-r--r--fs/smb/server/smb2pdu.h4
-rw-r--r--fs/xfs/libxfs/xfs_defer.c2
-rw-r--r--fs/xfs/scrub/cow_repair.c175
-rw-r--r--fs/xfs/scrub/dirtree.c16
-rw-r--r--fs/xfs/scrub/dqiterate.c2
-rw-r--r--fs/xfs/scrub/inode_repair.c4
-rw-r--r--fs/xfs/scrub/rgsuper.c14
-rw-r--r--fs/xfs/scrub/rtbitmap.c2
-rw-r--r--fs/xfs/scrub/rtrefcount.c2
-rw-r--r--fs/xfs/scrub/rtrmap.c8
-rw-r--r--fs/xfs/scrub/scrub.h6
-rw-r--r--fs/xfs/scrub/trace.h35
-rw-r--r--fs/xfs/scrub/xfarray.c3
-rw-r--r--fs/xfs/xfs_bmap_util.c89
-rw-r--r--fs/xfs/xfs_bmap_util.h4
-rw-r--r--fs/xfs/xfs_log_cil.c2
-rw-r--r--fs/xfs/xfs_log_recover.c26
-rw-r--r--fs/xfs/xfs_reflink.c36
-rw-r--r--fs/xfs/xfs_sysfs.c28
-rw-r--r--fs/xfs/xfs_sysfs.h2
-rw-r--r--fs/xfs/xfs_trace.h41
-rw-r--r--fs/xfs/xfs_zone_alloc.c8
-rw-r--r--include/drm/drm_utils.h1
-rw-r--r--include/dt-bindings/power/fsl,imx93-power.h1
-rw-r--r--include/dt-bindings/reset/altr,rst-mgr-s10.h2
-rw-r--r--include/linux/arm_ffa.h9
-rw-r--r--include/linux/btf.h1
-rw-r--r--include/linux/can/dev/peak_canfd.h2
-rw-r--r--include/linux/ieee80211-eht.h12
-rw-r--r--include/linux/skmsg.h14
-rw-r--r--include/net/bluetooth/hci_core.h1
-rw-r--r--include/net/cfg80211.h4
-rw-r--r--include/net/sctp/structs.h3
-rw-r--r--include/net/xfrm.h2
-rw-r--r--include/scsi/scsi_device.h1
-rw-r--r--include/scsi/scsi_host.h3
-rw-r--r--include/soc/tegra/pmc.h5
-rw-r--r--io_uring/bpf-ops.c2
-rw-r--r--io_uring/fs.c3
-rw-r--r--io_uring/kbuf.c4
-rw-r--r--kernel/bpf/btf.c29
-rw-r--r--kernel/bpf/verifier.c97
-rw-r--r--kernel/cgroup/cpuset.c7
-rw-r--r--kernel/sched/ext/ext.c177
-rw-r--r--kernel/sched/ext/internal.h23
-rw-r--r--lib/bug.c2
-rw-r--r--lib/crypto/Kconfig3
-rw-r--r--lib/crypto/md5.c14
-rw-r--r--mm/sparse-vmemmap.c2
-rw-r--r--net/bluetooth/hci_event.c18
-rw-r--r--net/bluetooth/hci_sync.c66
-rw-r--r--net/bluetooth/mgmt.c105
-rw-r--r--net/can/bcm.c646
-rw-r--r--net/can/isotp.c296
-rw-r--r--net/can/j1939/transport.c18
-rw-r--r--net/can/raw.c66
-rw-r--r--net/core/bpf_sk_storage.c2
-rw-r--r--net/core/dev.c10
-rw-r--r--net/core/sock.c3
-rw-r--r--net/core/sock_map.c2
-rw-r--r--net/ipv4/fib_trie.c2
-rw-r--r--net/ipv4/tcp_bpf.c17
-rw-r--r--net/ipv4/tcp_ipv4.c6
-rw-r--r--net/ipv6/netfilter.c4
-rw-r--r--net/ipv6/tcp_ipv6.c6
-rw-r--r--net/ipv6/xfrm6_policy.c1
-rw-r--r--net/iucv/af_iucv.c4
-rw-r--r--net/llc/llc_conn.c1
-rw-r--r--net/mac80211/cfg.c11
-rw-r--r--net/mac80211/ibss.c13
-rw-r--r--net/mac80211/iface.c8
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/mlme.c12
-rw-r--r--net/mac80211/nan.c35
-rw-r--r--net/mac80211/rx.c34
-rw-r--r--net/mac80211/sta_info.c15
-rw-r--r--net/mac80211/tx.c17
-rw-r--r--net/mac80211/util.c3
-rw-r--r--net/mpls/af_mpls.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_app.c10
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c6
-rw-r--r--net/netfilter/nf_conncount.c6
-rw-r--r--net/netfilter/nf_conntrack_ecache.c2
-rw-r--r--net/netfilter/nf_flow_table_core.c6
-rw-r--r--net/netfilter/xt_nat.c9
-rw-r--r--net/netfilter/xt_physdev.c5
-rw-r--r--net/openvswitch/flow_netlink.c201
-rw-r--r--net/sched/act_tunnel_key.c14
-rw-r--r--net/sctp/sm_statefuns.c23
-rw-r--r--net/wireless/core.c14
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/mlme.c101
-rw-r--r--net/wireless/nl80211.c25
-rw-r--r--net/wireless/pmsr.c34
-rw-r--r--net/wireless/scan.c18
-rw-r--r--net/wireless/sme.c6
-rw-r--r--net/xfrm/xfrm_device.c13
-rw-r--r--net/xfrm/xfrm_iptfs.c1
-rw-r--r--net/xfrm/xfrm_nat_keepalive.c15
-rw-r--r--net/xfrm/xfrm_policy.c4
-rw-r--r--net/xfrm/xfrm_state.c12
-rw-r--r--net/xfrm/xfrm_user.c41
-rw-r--r--security/landlock/net.c8
-rw-r--r--security/landlock/ruleset.h4
-rw-r--r--security/landlock/task.c9
-rw-r--r--security/selinux/hooks.c42
-rw-r--r--sound/firewire/bebob/bebob.h1
-rw-r--r--sound/firewire/dice/dice.h1
-rw-r--r--sound/firewire/digi00x/digi00x.h1
-rw-r--r--sound/firewire/fireface/ff.h1
-rw-r--r--sound/firewire/fireworks/fireworks.h1
-rw-r--r--sound/firewire/motu/motu.h1
-rw-r--r--sound/firewire/oxfw/oxfw.h1
-rw-r--r--sound/firewire/tascam/tascam.h1
-rw-r--r--sound/hda/codecs/conexant.c3
-rw-r--r--sound/hda/codecs/realtek/alc269.c49
-rw-r--r--sound/hda/codecs/side-codecs/Kconfig2
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c30
-rw-r--r--sound/hda/core/regmap.c4
-rw-r--r--sound/soc/amd/acp/Kconfig10
-rw-r--r--sound/soc/amd/acp/Makefile3
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c47
-rw-r--r--sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c4
-rw-r--r--sound/soc/amd/ps/pci-ps.c33
-rw-r--r--sound/soc/codecs/cs42l43-jack.c3
-rw-r--r--sound/soc/codecs/rt712-sdca.c11
-rw-r--r--sound/soc/codecs/tas2562.c5
-rw-r--r--sound/soc/intel/boards/sof_sdw.c1
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c5
-rw-r--r--sound/soc/qcom/sc8280xp.c1
-rw-r--r--sound/usb/caiaq/device.c17
-rw-r--r--sound/usb/caiaq/input.c6
-rw-r--r--sound/usb/mixer.c2
-rw-r--r--sound/usb/quirks.c6
-rw-r--r--tools/sched_ext/include/scx/cid.bpf.h88
-rw-r--r--tools/testing/selftests/alsa/mixer-test.c5
-rw-r--r--tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c36
-rw-r--r--tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c57
-rw-r--r--tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c43
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_basic.c45
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_listen.c21
-rw-r--r--tools/testing/selftests/bpf/prog_tests/verifier.c2
-rw-r--r--tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c77
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c27
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c16
-rw-r--r--tools/testing/selftests/bpf/test_maps.c13
-rwxr-xr-xtools/testing/selftests/drivers/net/netconsole/netcons_resume.sh3
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc27
-rw-r--r--tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc4
-rw-r--r--tools/testing/selftests/kvm/Makefile.kvm1
-rw-r--r--tools/testing/selftests/kvm/arm64/mmio_sign_ext.c255
-rw-r--r--tools/testing/selftests/kvm/x86/sev_migrate_tests.c47
-rw-r--r--tools/testing/selftests/landlock/net_test.c97
-rw-r--r--tools/testing/selftests/landlock/scoped_signal_test.c67
-rwxr-xr-xtools/testing/selftests/net/netfilter/nft_flowtable.sh55
-rw-r--r--tools/testing/selftests/sched_ext/Makefile1
-rw-r--r--tools/testing/selftests/sched_ext/nohz_tick.bpf.c65
-rw-r--r--tools/testing/selftests/sched_ext/nohz_tick.c347
-rw-r--r--tools/tracing/rtla/Makefile14
-rw-r--r--tools/tracing/rtla/src/common.c1
-rw-r--r--virt/kvm/kvm_main.c16
437 files changed, 6660 insertions, 2451 deletions
diff --git a/.mailmap b/.mailmap
index e8fa40478028..81da60babfe6 100644
--- a/.mailmap
+++ b/.mailmap
@@ -399,6 +399,7 @@ Jens Axboe <axboe@kernel.dk> <jens.axboe@oracle.com>
Jens Axboe <axboe@kernel.dk> <axboe@fb.com>
Jens Axboe <axboe@kernel.dk> <axboe@meta.com>
Jens Osterkamp <Jens.Osterkamp@de.ibm.com>
+Jens Wiklander <jenswi@kernel.org> <jens.wiklander@linaro.org>
Jernej Skrabec <jernej.skrabec@gmail.com> <jernej.skrabec@siol.net>
Jesper Dangaard Brouer <hawk@kernel.org> <brouer@redhat.com>
Jesper Dangaard Brouer <hawk@kernel.org> <hawk@comx.dk>
@@ -826,8 +827,8 @@ Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> <sriram.yagnaraman@est.tech>
Stanislav Fomichev <sdf@fomichev.me> <sdf@google.com>
Stanislav Fomichev <sdf@fomichev.me> <stfomichev@gmail.com>
Stefan Wahren <wahrenst@gmx.net> <stefan.wahren@i2se.com>
-Stéphane Grosjean <stephane.grosjean@hms-networks.com> <s.grosjean@peak-system.com>
-Stéphane Grosjean <stephane.grosjean@hms-networks.com> <stephane.grosjean@free.fr>
+Stéphane Grosjean <s.grosjean@peak-system.fr> <s.grosjean@peak-system.com>
+Stéphane Grosjean <s.grosjean@peak-system.fr> <stephane.grosjean@free.fr>
Stéphane Witzmann <stephane.witzmann@ubpmes.univ-bpclermont.fr>
Stephen Hemminger <stephen@networkplumber.org> <shemminger@linux-foundation.org>
Stephen Hemminger <stephen@networkplumber.org> <shemminger@osdl.org>
diff --git a/CREDITS b/CREDITS
index 84793a967a0b..91c51c14e993 100644
--- a/CREDITS
+++ b/CREDITS
@@ -3626,6 +3626,13 @@ S: 69 rue Dunois
S: 75013 Paris
S: France
+N: Wolfram Sang
+E: wsa@kernel.org
+W: sang-engineering.com
+P: rsa4096/140DE4CC14A029B6 3991 B1EA B9E2 6751 A4F7 645D 140D E4CC 14A0 29B6
+D: I2C Maintainer 2012 - 2026
+S: Berlin, Germany
+
N: Aleksa Sarai
E: cyphar@cyphar.com
W: https://www.cyphar.com/
diff --git a/Documentation/admin-guide/cgroup-v1/rdma.rst b/Documentation/admin-guide/cgroup-v1/rdma.rst
index e69369b7252e..abddf34d2667 100644
--- a/Documentation/admin-guide/cgroup-v1/rdma.rst
+++ b/Documentation/admin-guide/cgroup-v1/rdma.rst
@@ -9,6 +9,7 @@ RDMA Controller
1-2. Why RDMA controller needed?
1-3. How is RDMA controller implemented?
2. Usage Examples
+ 3. RDMA Interface Files
1. Overview
===========
@@ -115,3 +116,68 @@ Following resources can be accounted by rdma controller.
(d) Delete resource limit::
echo mlx4_0 hca_handle=max hca_object=max > /sys/fs/cgroup/rdma/1/rdma.max
+
+3. RDMA Interface Files
+========================
+
+The following interface files are available in each non-root RDMA cgroup.
+
+ rdma.max
+ A read-write file which describes the configured resource limit
+ for an RDMA/IB device. See the Usage Examples above.
+
+ rdma.current
+ A read-only file which describes the current resource usage.
+
+ rdma.peak
+ A read-only nested-keyed file which shows the historical high
+ watermark of resource usage per device since the cgroup was created.
+
+ An example for mlx4 and ocrdma device follows::
+
+ mlx4_0 hca_handle=1 hca_object=20
+ ocrdma1 hca_handle=0 hca_object=23
+
+ rdma.events
+ A read-only nested-keyed file which exists on non-root cgroups
+ and contains the following keys:
+
+ max
+ The number of times a process in this cgroup or its
+ descendants attempted an RDMA resource allocation that
+ was rejected because a rdma.max limit in the subtree
+ was reached. This is a hierarchical counter propagated
+ upward to all ancestor cgroups. A value change in this
+ file generates a file modified event.
+
+ alloc_fail
+ The number of RDMA resource allocation attempts that
+ originated in this cgroup or its descendants and failed
+ due to a rdma.max limit being reached. This is a
+ hierarchical counter propagated upward.
+
+ An example for mlx4 device follows::
+
+ mlx4_0 hca_handle.max=5 hca_handle.alloc_fail=3 hca_object.max=0 hca_object.alloc_fail=0
+
+ rdma.events.local
+ Similar to rdma.events but the fields are local to the cgroup,
+ i.e. not hierarchical. The file modified event generated on this
+ file reflects only the local events.
+
+ The following nested keys are defined.
+
+ max
+ The number of times a process in this cgroup or its
+ descendants attempted an RDMA resource allocation that
+ was rejected because this cgroup's own rdma.max limit
+ was reached.
+
+ alloc_fail
+ The number of RDMA resource allocation attempts
+ originating from this cgroup that failed due to this
+ cgroup's or an ancestor's rdma.max limit.
+
+ An example for mlx4 device follows::
+
+ mlx4_0 hca_handle.max=5 hca_handle.alloc_fail=0 hca_object.max=0 hca_object.alloc_fail=0
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 993446ab66d0..14b8c571c0d1 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1570,7 +1570,7 @@ The following nested keys are defined.
sock (npn)
Amount of memory used in network transmission buffers
- vmalloc (npn)
+ vmalloc
Amount of memory used for vmap backed memory.
shmem
@@ -1735,7 +1735,7 @@ The following nested keys are defined.
Number of pages written from zswap to swap.
zswap_incomp
- Number of incompressible pages currently stored in zswap
+ Amount of memory used by incompressible pages currently stored in zswap
without compression. These pages could not be compressed to
a size smaller than PAGE_SIZE, so they are stored as-is.
@@ -2257,10 +2257,11 @@ groups D and F will influence each other. Group G will influence nobody::
So the ideal way to configure this is to set io.latency in groups A, B, and C.
Generally you do not want to set a value lower than the latency your device
supports. Experiment to find the value that works best for your workload.
-Start at higher than the expected latency for your device and watch the
-avg_lat value in io.stat for your workload group to get an idea of the
-latency you see during normal operation. Use the avg_lat value as a basis for
-your real setting, setting at 10-15% higher than the value in io.stat.
+Start at higher than the expected latency for your device and, with
+blkcg_debug_stats enabled, watch the avg_lat value in io.stat for your
+workload group to get an idea of the latency you see during normal operation.
+Use the avg_lat value as a basis for your real setting, setting at 10-15%
+higher than the value in io.stat.
How IO Latency Throttling Works
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2298,7 +2299,9 @@ IO Latency Interface Files
io.stat
If the controller is enabled you will see extra stats in io.stat in
- addition to the normal ones.
+ addition to the normal ones. These debug stats are only emitted when
+ the blkcg_debug_stats module parameter is enabled (it is disabled by
+ default).
depth
This is the current queue depth for the group.
@@ -2934,7 +2937,8 @@ include/linux/misc_cgroup.h.
Misc Interface Files
~~~~~~~~~~~~~~~~~~~~
-Miscellaneous controller provides 3 interface files. If two misc resources (res_a and res_b) are registered then:
+Miscellaneous controller provides the following interface files. If two misc
+resources (res_a and res_b) are registered then:
misc.capacity
A read-only flat-keyed file shown only in the root cgroup. It shows
diff --git a/Documentation/crypto/libcrypto-blockcipher.rst b/Documentation/crypto/libcrypto-blockcipher.rst
index dd5ce2f8b515..fd85e27fab8d 100644
--- a/Documentation/crypto/libcrypto-blockcipher.rst
+++ b/Documentation/crypto/libcrypto-blockcipher.rst
@@ -6,14 +6,14 @@ Block ciphers
AES
---
-Support for the AES block cipher.
+This API provides support for the AES block cipher.
.. kernel-doc:: include/crypto/aes.h
DES
---
-Support for the DES block cipher. This algorithm is obsolete and is supported
-only for backwards compatibility.
+This API provides support for the DES block cipher. This algorithm is obsolete
+and is supported only for backwards compatibility.
.. kernel-doc:: include/crypto/des.h
diff --git a/Documentation/crypto/libcrypto-hash.rst b/Documentation/crypto/libcrypto-hash.rst
index 4248e6fdc952..fa4c54236af6 100644
--- a/Documentation/crypto/libcrypto-hash.rst
+++ b/Documentation/crypto/libcrypto-hash.rst
@@ -6,81 +6,83 @@ Hash functions, MACs, and XOFs
AES-CMAC and AES-XCBC-MAC
-------------------------
-Support for the AES-CMAC and AES-XCBC-MAC message authentication codes.
+This API provides support for the AES-CMAC and AES-XCBC-MAC message
+authentication codes.
.. kernel-doc:: include/crypto/aes-cbc-macs.h
BLAKE2b
-------
-Support for the BLAKE2b cryptographic hash function.
+This API provides support for the BLAKE2b cryptographic hash function.
.. kernel-doc:: include/crypto/blake2b.h
BLAKE2s
-------
-Support for the BLAKE2s cryptographic hash function.
+This API provides support for the BLAKE2s cryptographic hash function.
.. kernel-doc:: include/crypto/blake2s.h
GHASH and POLYVAL
-----------------
-Support for the GHASH and POLYVAL universal hash functions. These algorithms
-are used only as internal components of other algorithms.
+This API provides support for the GHASH and POLYVAL universal hash functions.
+These algorithms are used only as internal components of other algorithms.
.. kernel-doc:: include/crypto/gf128hash.h
MD5
---
-Support for the MD5 cryptographic hash function and HMAC-MD5. This algorithm is
-obsolete and is supported only for backwards compatibility.
+This API provides support for the MD5 cryptographic hash function and HMAC-MD5.
+This algorithm is obsolete and is supported only for backwards compatibility.
.. kernel-doc:: include/crypto/md5.h
NH
--
-Support for the NH universal hash function. This algorithm is used only as an
-internal component of other algorithms.
+This API provides support for the NH universal hash function. This algorithm is
+used only as an internal component of other algorithms.
.. kernel-doc:: include/crypto/nh.h
Poly1305
--------
-Support for the Poly1305 universal hash function. This algorithm is used only
-as an internal component of other algorithms.
+This API provides support for the Poly1305 universal hash function. This
+algorithm is used only as an internal component of other algorithms.
.. kernel-doc:: include/crypto/poly1305.h
SHA-1
-----
-Support for the SHA-1 cryptographic hash function and HMAC-SHA1. This algorithm
-is obsolete and is supported only for backwards compatibility.
+This API provides support for the SHA-1 cryptographic hash function and
+HMAC-SHA1. This algorithm is obsolete and is supported only for backwards
+compatibility.
.. kernel-doc:: include/crypto/sha1.h
SHA-2
-----
-Support for the SHA-2 family of cryptographic hash functions, including SHA-224,
-SHA-256, SHA-384, and SHA-512. This also includes their corresponding HMACs:
-HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.
+This API provides support for the SHA-2 family of cryptographic hash functions,
+including SHA-224, SHA-256, SHA-384, and SHA-512. This also includes their
+corresponding HMACs: HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.
.. kernel-doc:: include/crypto/sha2.h
SHA-3
-----
-The SHA-3 functions are documented in :ref:`sha3`.
+The SHA-3 API is documented in :ref:`sha3`.
SM3
---
-Support for the SM3 cryptographic hash function.
+This API provides support for the SM3 cryptographic hash function.
.. kernel-doc:: include/crypto/sm3.h
diff --git a/Documentation/crypto/libcrypto-signature.rst b/Documentation/crypto/libcrypto-signature.rst
index e80d59fa51b6..2a6dc793f0de 100644
--- a/Documentation/crypto/libcrypto-signature.rst
+++ b/Documentation/crypto/libcrypto-signature.rst
@@ -6,6 +6,6 @@ Digital signature algorithms
ML-DSA
------
-Support for the ML-DSA digital signature algorithm.
+This API provides support for the ML-DSA digital signature algorithm.
.. kernel-doc:: include/crypto/mldsa.h
diff --git a/Documentation/crypto/libcrypto.rst b/Documentation/crypto/libcrypto.rst
index a1557d45b0e5..0733e603d229 100644
--- a/Documentation/crypto/libcrypto.rst
+++ b/Documentation/crypto/libcrypto.rst
@@ -4,8 +4,9 @@
Crypto library
==============
-``lib/crypto/`` provides faster and easier access to cryptographic algorithms
-than the traditional crypto API.
+The Linux kernel's crypto library (``lib/crypto/``) provides kernel-internal
+users of cryptographic algorithms with faster and easier access to those
+algorithms than the traditional kernel crypto API.
Each cryptographic algorithm is supported via a set of dedicated functions.
"Crypto agility", where needed, is left to calling code.
diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml
index eb3755bdfdf7..a4d032224dce 100644
--- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml
+++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml
@@ -193,7 +193,6 @@ allOf:
- mediatek,mt8183-mmc
- mediatek,mt8186-mmc
- mediatek,mt8188-mmc
- - mediatek,mt8189-mmc
- mediatek,mt8195-mmc
- mediatek,mt8196-mmc
- mediatek,mt8516-mmc
@@ -348,6 +347,34 @@ allOf:
- const: axi_cg
- const: ahb_cg
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt8189-mmc
+ then:
+ properties:
+ clocks:
+ minItems: 6
+ items:
+ - description: source clock
+ - description: HCLK which used for host
+ - description: independent source clock gate
+ - description: bus clock used for internal register access
+ - description: peripheral bus clock gate
+ - description: AXI bus clock gate
+ - description: crypto clock used for data encrypt/decrypt (optional)
+ clock-names:
+ minItems: 6
+ items:
+ - const: source
+ - const: hclk
+ - const: source_cg
+ - const: bus_clk
+ - const: pclk_cg
+ - const: axi_cg
+ - const: crypto
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
index 2eed2277511f..4988e7ed6e34 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,sc8280xp-lpass-rx-macro
- items:
- enum:
+ - qcom,eliza-lpass-rx-macro
- qcom,kaanapali-lpass-rx-macro
- qcom,sm8650-lpass-rx-macro
- qcom,sm8750-lpass-rx-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
index e5e65e226a02..d8682ff2e82c 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,sc8280xp-lpass-tx-macro
- items:
- enum:
+ - qcom,eliza-lpass-tx-macro
- qcom,kaanapali-lpass-tx-macro
- qcom,sm8650-lpass-tx-macro
- qcom,sm8750-lpass-tx-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index 5c42b2b323ee..aea31fbdad37 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,sc8280xp-lpass-va-macro
- items:
- enum:
+ - qcom,eliza-lpass-va-macro
- qcom,glymur-lpass-va-macro
- qcom,kaanapali-lpass-va-macro
- qcom,sm8650-lpass-va-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
index d5f22b5cf021..9fedd80532e2 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
@@ -20,6 +20,7 @@ properties:
- qcom,sc8280xp-lpass-wsa-macro
- items:
- enum:
+ - qcom,eliza-lpass-wsa-macro
- qcom,glymur-lpass-wsa-macro
- qcom,kaanapali-lpass-wsa-macro
- qcom,sm8650-lpass-wsa-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index 15f38622b98b..dae440ecab59 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -23,6 +23,7 @@ properties:
- const: qcom,sdm845-sndcard
- items:
- enum:
+ - qcom,eliza-sndcard
- qcom,kaanapali-sndcard
- qcom,sm8550-sndcard
- qcom,sm8650-sndcard
diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst
index 4b1ffd03f516..2771ea4cc14a 100644
--- a/Documentation/scheduler/sched-ext.rst
+++ b/Documentation/scheduler/sched-ext.rst
@@ -493,8 +493,9 @@ a freshly woken up task gets on a CPU.
Where to Look
=============
-* ``include/linux/sched/ext.h`` defines the core data structures, ops table
- and constants.
+* ``include/linux/sched/ext.h`` defines the core data structures and
+ constants, while the ops table (``struct sched_ext_ops``) is defined in
+ ``kernel/sched/ext/internal.h``.
* ``kernel/sched/ext/ext.c`` contains sched_ext core implementation and helpers.
The functions prefixed with ``scx_bpf_`` can be called from the BPF
@@ -555,7 +556,8 @@ ABI Instability
===============
The APIs provided by sched_ext to BPF schedulers programs have no stability
-guarantees. This includes the ops table callbacks and constants defined in
+guarantees. This includes the ops table callbacks defined in
+``kernel/sched/ext/internal.h`` and the constants defined in
``include/linux/sched/ext.h``, as well as the ``scx_bpf_`` kfuncs defined in
``kernel/sched/ext/ext.c`` and ``kernel/sched/ext/idle.c``.
diff --git a/MAINTAINERS b/MAINTAINERS
index 806bd2d80d15..a674e36529f7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2767,12 +2767,12 @@ F: arch/arm/mach-ep93xx/
F: drivers/iio/adc/ep93xx_adc.c
ARM/CIX SOC SUPPORT
-M: Peter Chen <peter.chen@cixtech.com>
+M: Gary Yang <gary.yang@cixtech.com>
M: Fugang Duan <fugang.duan@cixtech.com>
R: CIX Linux Kernel Upstream Group <cix-kernel-upstream@cixtech.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git
+T: git https://github.com/cixtech/linux-mainline.git
F: Documentation/devicetree/bindings/arm/cix.yaml
F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
F: arch/arm64/boot/dts/cix/
@@ -6209,6 +6209,7 @@ F: include/dt-bindings/sound/cs*
F: include/linux/mfd/cs42l43*
F: include/sound/cs*
F: sound/hda/codecs/cirrus*
+F: sound/hda/codecs/side-codecs/cirrus*
F: sound/hda/codecs/side-codecs/cs*
F: sound/hda/codecs/side-codecs/hda_component*
F: sound/soc/codecs/cs*
@@ -14191,6 +14192,7 @@ F: virt/kvm/*
KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
M: Marc Zyngier <maz@kernel.org>
M: Oliver Upton <oupton@kernel.org>
+R: Fuad Tabba <tabba@google.com>
R: Joey Gouly <joey.gouly@arm.com>
R: Steffen Eiden <seiden@linux.ibm.com>
R: Suzuki K Poulose <suzuki.poulose@arm.com>
@@ -20195,7 +20197,7 @@ W: http://www.onsemi.com
F: drivers/net/phy/ncn*
OP-TEE DRIVER
-M: Jens Wiklander <jens.wiklander@linaro.org>
+M: Jens Wiklander <jenswi@kernel.org>
L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-optee-devices
@@ -23313,7 +23315,7 @@ L: spacemit@lists.linux.dev
S: Maintained
W: https://github.com/spacemit-com/linux/wiki
C: irc://irc.libera.chat/spacemit
-T: git https://github.com/spacemit-com/linux
+T: https://git.kernel.org/pub/scm/linux/kernel/git/spacemit/linux.git
F: arch/riscv/boot/dts/spacemit/
N: spacemit
K: spacemit
@@ -23572,7 +23574,7 @@ F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
F: drivers/media/platform/sunxi/sun8i-rotate/
RPMB SUBSYSTEM
-M: Jens Wiklander <jens.wiklander@linaro.org>
+M: Jens Wiklander <jenswi@kernel.org>
L: linux-kernel@vger.kernel.org
S: Supported
F: drivers/misc/rpmb-core.c
@@ -26526,7 +26528,7 @@ F: drivers/media/i2c/tw9910.c
F: include/media/i2c/tw9910.h
TEE SUBSYSTEM
-M: Jens Wiklander <jens.wiklander@linaro.org>
+M: Jens Wiklander <jenswi@kernel.org>
R: Sumit Garg <sumit.garg@kernel.org>
L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers)
S: Maintained
diff --git a/Makefile b/Makefile
index bfb47ad8cb9a..11539c3fd405 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 7
PATCHLEVEL = 2
SUBLEVEL = 0
-EXTRAVERSION = -rc3
+EXTRAVERSION = -rc4
NAME = Baby Opossum Posse
# *DOCUMENTATION*
diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig
index f930396d9dae..870e5291b7db 100644
--- a/arch/arc/configs/axs101_defconfig
+++ b/arch/arc/configs/axs101_defconfig
@@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
# CONFIG_HWMON is not set
CONFIG_DRM=m
CONFIG_DRM_I2C_ADV7511=m
diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig
index 6b779dee5ea0..d45e4d335998 100644
--- a/arch/arc/configs/axs103_defconfig
+++ b/arch/arc/configs/axs103_defconfig
@@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
# CONFIG_HWMON is not set
CONFIG_FB=y
CONFIG_FRAMEBUFFER_CONSOLE=y
diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig
index a89b50d5369d..f986c0205f13 100644
--- a/arch/arc/configs/axs103_smp_defconfig
+++ b/arch/arc/configs/axs103_smp_defconfig
@@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
# CONFIG_HWMON is not set
CONFIG_DRM=m
CONFIG_DRM_I2C_ADV7511=m
diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig
index 865fbc19ef03..6e396a9ddb8b 100644
--- a/arch/arc/configs/tb10x_defconfig
+++ b/arch/arc/configs/tb10x_defconfig
@@ -61,7 +61,6 @@ CONFIG_SERIAL_8250_DW=y
CONFIG_I2C=y
# CONFIG_I2C_COMPAT is not set
CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
# CONFIG_USB_SUPPORT is not set
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c
index b2f2c59279a6..2d99dffed0ce 100644
--- a/arch/arc/kernel/smp.c
+++ b/arch/arc/kernel/smp.c
@@ -22,6 +22,7 @@
#include <linux/irqdomain.h>
#include <linux/export.h>
#include <linux/of_fdt.h>
+#include <linux/string.h>
#include <asm/mach_desc.h>
#include <asm/setup.h>
@@ -43,9 +44,10 @@ static int __init arc_get_cpu_map(const char *name, struct cpumask *cpumask)
{
unsigned long dt_root = of_get_flat_dt_root();
const char *buf;
+ int len;
- buf = of_get_flat_dt_prop(dt_root, name, NULL);
- if (!buf)
+ buf = of_get_flat_dt_prop(dt_root, name, &len);
+ if (!buf || !memchr(buf, '\0', len))
return -EINVAL;
if (cpulist_parse(buf, cpumask))
diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig
index cb46802f5ce5..7f812020e082 100644
--- a/arch/arm/mach-ixp4xx/Kconfig
+++ b/arch/arm/mach-ixp4xx/Kconfig
@@ -14,6 +14,5 @@ menuconfig ARCH_IXP4XX
select IXP4XX_TIMER
select USB_EHCI_BIG_ENDIAN_DESC
select USB_EHCI_BIG_ENDIAN_MMIO
- select USE_OF
help
Support for Intel's IXP4XX (XScale) family of processors.
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 66e26990e2c8..c478fb8a6f78 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -22,7 +22,6 @@ config MACH_PXA25X_DT
select PINCTRL
select POWER_SUPPLY
select PXA25x
- select USE_OF
help
Include support for Marvell PXA25x based platforms using
the device tree. Needn't select any other machine while
@@ -33,7 +32,6 @@ config MACH_PXA27X_DT
select PINCTRL
select POWER_SUPPLY
select PXA27x
- select USE_OF
help
Include support for Marvell PXA27x based platforms using
the device tree. Needn't select any other machine while
@@ -47,7 +45,6 @@ config MACH_PXA3XX_DT
select PINCTRL
select POWER_SUPPLY
select PXA3xx
- select USE_OF
help
Include support for Marvell PXA3xx based platforms using
the device tree. Needn't select any other machine while
diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 8e0c51e496e2..820670dd6042 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -5355,7 +5355,7 @@
#size-cells = <0>;
cpu0_0: cpu@0 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00000>;
@@ -5374,7 +5374,7 @@
};
cpu0_1: cpu@100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00100>;
@@ -5393,7 +5393,7 @@
};
cpu0_2: cpu@200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00200>;
@@ -5412,7 +5412,7 @@
};
cpu0_3: cpu@300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00300>;
@@ -5431,7 +5431,7 @@
};
cpu1_0: cpu@10000 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10000>;
@@ -5450,7 +5450,7 @@
};
cpu1_1: cpu@10100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10100>;
@@ -5469,7 +5469,7 @@
};
cpu1_2: cpu@10200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10200>;
@@ -5488,7 +5488,7 @@
};
cpu1_3: cpu@10300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10300>;
@@ -5507,7 +5507,7 @@
};
cpu2_0: cpu@20000 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20000>;
@@ -5526,7 +5526,7 @@
};
cpu2_1: cpu@20100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20100>;
@@ -5545,7 +5545,7 @@
};
cpu2_2: cpu@20200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20200>;
@@ -5564,7 +5564,7 @@
};
cpu2_3: cpu@20300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20300>;
diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 2d8e7e37830f..2d2cb1a3d95c 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -3208,7 +3208,7 @@
};
gpcdma: dma-controller@8400000 {
- compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma";
+ compatible = "nvidia,tegra264-gpcdma";
reg = <0x0 0x08400000 0x0 0x210000>;
interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>,
@@ -4070,7 +4070,7 @@
d-cache-sets = <256>;
};
- cpu1: cpu@1 {
+ cpu1: cpu@10000 {
compatible = "arm,neoverse-v3ae";
device_type = "cpu";
reg = <0x10000>;
diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
index d2b3fc08954a..0ab303863155 100644
--- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
+++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
@@ -107,6 +107,47 @@
reg = <0x0 0x8c400000 0x0 0x02000000>;
no-map;
};
+
+ /* DRAM controller inline ECC areas */
+ ecc@10cccc0000 {
+ reg = <0x10 0xcccc0000 0x0 0x33340000>;
+ no-map;
+ };
+
+ ecc@12cccc0000 {
+ reg = <0x12 0xcccc0000 0x0 0x33340000>;
+ no-map;
+ };
+
+ ecc@14cccc0000 {
+ reg = <0x14 0xcccc0000 0x0 0x33340000>;
+ no-map;
+ };
+
+ ecc@16cccc0000 {
+ reg = <0x16 0xcccc0000 0x0 0x33340000>;
+ no-map;
+ };
+
+ ecc@18cccc0000 {
+ reg = <0x18 0xcccc0000 0x0 0x33340000>;
+ no-map;
+ };
+
+ ecc@1a66660000 {
+ reg = <0x1a 0x66660000 0x0 0x999a0000>;
+ no-map;
+ };
+
+ ecc@1c66660000 {
+ reg = <0x1c 0x66660000 0x0 0x999a0000>;
+ no-map;
+ };
+
+ ecc@1e66660000 {
+ reg = <0x1e 0x66660000 0x0 0x999a0000>;
+ no-map;
+ };
};
};
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index cdf3e8422ea1..012d711034d1 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -388,6 +388,14 @@ struct s1_walk_result {
bool failed;
};
+static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
+{
+ wr->fst = fst;
+ wr->ptw = s1ptw;
+ wr->s2 = s1ptw;
+ wr->failed = true;
+}
+
int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
struct s1_walk_result *wr, u64 va);
int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa,
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index b8ded434c63f..640f2dc00a8b 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -11,14 +11,6 @@
#include <asm/kvm_mmu.h>
#include <asm/lsui.h>
-static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
-{
- wr->fst = fst;
- wr->ptw = s1ptw;
- wr->s2 = s1ptw;
- wr->failed = true;
-}
-
#define S1_MMU_DISABLED (-127)
static int get_ia_size(struct s1_walk_info *wi)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index e688bc5139c1..3c82f392845d 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2746,17 +2746,33 @@ static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr)
(spsr & PSR_MODE32_BIT) ||
(vcpu_el2_tge_is_set(vcpu) && (mode == PSR_MODE_EL1t ||
mode == PSR_MODE_EL1h))) {
+ u64 mask;
+
/*
- * The guest is playing with our nerves. Preserve EL, SP,
- * masks, flags from the existing PSTATE, and set IL.
- * The HW will then generate an Illegal State Exception
- * immediately after ERET.
+ * On an illegal exception return, the flags and masks are
+ * taken from the SPSR while PSTATE.{EL,SP,nRW} and, if
+ * FEAT_GCS, PSTATE.EXLOCK are unchanged (R_VWJHB). Set IL
+ * so the HW generates an Illegal State Exception right
+ * after ERET.
*/
- spsr = *vcpu_cpsr(vcpu);
+ mask = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT |
+ PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT;
+
+ if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, IMP))
+ mask |= PSR_PAN_BIT;
+ if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, NMI, IMP))
+ mask |= ALLINT_ALLINT;
+ /* FEAT_SPE_EXC and FEAT_TRBE_EXC also gate PSTATE.PM one day... */
+ if (kvm_has_feat(vcpu->kvm, ID_AA64DFR1_EL1, EBEP, IMP))
+ mask |= BIT_ULL(32); /* SPSR_ELx.PM */
+
+ spsr &= mask;
- spsr &= (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT |
- PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT |
- PSR_MODE_MASK | PSR_MODE32_BIT);
+ mask = PSR_MODE_MASK | PSR_MODE32_BIT;
+ if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, GCS, IMP))
+ mask |= BIT_ULL(34); /* PSTATE.EXLOCK */
+
+ spsr |= *vcpu_cpsr(vcpu) & mask;
spsr |= PSR_IL_BIT;
}
@@ -2784,7 +2800,7 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
* ERET handling, and the guest will have a little surprise.
*/
if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE) && !(spsr & PSR_IL_BIT)) {
- esr &= ESR_ELx_ERET_ISS_ERETA;
+ esr &= (ESR_ELx_ERET_ISS_ERETA | ESR_ELx_IL);
esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_FPAC);
kvm_inject_nested_sync(vcpu, esr);
return;
@@ -2826,6 +2842,7 @@ static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2,
break;
case except_type_serror:
kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR);
+ vcpu_write_sys_reg(vcpu, esr_el2, ESR_EL2);
break;
default:
WARN_ONCE(1, "Unsupported EL2 exception injection %d\n", type);
@@ -2950,6 +2967,6 @@ int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr)
* vSError injection. Manually populate EC for an emulated SError
* exception.
*/
- esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR);
+ esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR) | ESR_ELx_IL;
return kvm_inject_nested(vcpu, esr, except_type_serror);
}
diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
index 18131e395e24..4bf624a49591 100644
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@ -448,16 +448,19 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu)
static inline bool kvm_hyp_handle_mops(struct kvm_vcpu *vcpu, u64 *exit_code)
{
+ u64 spsr;
+
*vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR);
arm64_mops_reset_regs(vcpu_gp_regs(vcpu), vcpu->arch.fault.esr_el2);
write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
/*
* Finish potential single step before executing the prologue
- * instruction.
+ * instruction. Modify the hardware SPSR_EL2 directly, as vcpu_cpsr()
+ * may hold a synthetic (vEL2) value for a guest hypervisor.
*/
- *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
- write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
+ spsr = read_sysreg_el2(SYS_SPSR);
+ write_sysreg_el2(spsr & ~DBG_SPSR_SS, SYS_SPSR);
return true;
}
@@ -602,8 +605,6 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code)
return false;
break;
case ESR_ELx_EC_SYS64:
- if (WARN_ON_ONCE(!is_hyp_ctxt(vcpu)))
- return false;
fallthrough;
case ESR_ELx_EC_SVE:
if (!sve_guest)
diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
index 1af722771178..a327c2bbb6b6 100644
--- a/arch/arm64/kvm/hyp/nvhe/ffa.c
+++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
@@ -352,7 +352,7 @@ static u32 __ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges,
u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE;
u64 pfn = hyp_phys_to_pfn(range->address);
- if (!PAGE_ALIGNED(sz))
+ if (!PAGE_ALIGNED(sz | range->address))
break;
if (__pkvm_host_share_ffa(pfn, sz / PAGE_SIZE))
@@ -372,7 +372,7 @@ static u32 __ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges,
u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE;
u64 pfn = hyp_phys_to_pfn(range->address);
- if (!PAGE_ALIGNED(sz))
+ if (!PAGE_ALIGNED(sz | range->address))
break;
if (__pkvm_host_unshare_ffa(pfn, sz / PAGE_SIZE))
@@ -476,11 +476,12 @@ static void __do_ffa_mem_xfer(const u64 func_id,
DECLARE_REG(u32, fraglen, ctxt, 2);
DECLARE_REG(u64, addr_mbz, ctxt, 3);
DECLARE_REG(u32, npages_mbz, ctxt, 4);
+ u32 offset, nr_ranges, checked_offset, em_mem_access_off;
struct ffa_mem_region_attributes *ep_mem_access;
struct ffa_composite_mem_region *reg;
struct ffa_mem_region *buf;
- u32 offset, nr_ranges, checked_offset;
int ret = 0;
+ size_t mem_region_len = FFA_MEM_REGION_SZ(hyp_ffa_version);
if (addr_mbz || npages_mbz || fraglen > len ||
fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
@@ -488,8 +489,7 @@ static void __do_ffa_mem_xfer(const u64 func_id,
goto out;
}
- if (fraglen < sizeof(struct ffa_mem_region) +
- sizeof(struct ffa_mem_region_attributes)) {
+ if (fraglen < mem_region_len + ffa_emad_size_get(hyp_ffa_version)) {
ret = FFA_RET_INVALID_PARAMETERS;
goto out;
}
@@ -508,8 +508,13 @@ static void __do_ffa_mem_xfer(const u64 func_id,
buf = hyp_buffers.tx;
memcpy(buf, host_buffers.tx, fraglen);
- ep_mem_access = (void *)buf +
- ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
+ em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
+ if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ goto out_unlock;
+ }
+
+ ep_mem_access = (void *)buf + em_mem_access_off;
offset = ep_mem_access->composite_off;
if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
ret = FFA_RET_INVALID_PARAMETERS;
@@ -574,9 +579,9 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
DECLARE_REG(u32, handle_lo, ctxt, 1);
DECLARE_REG(u32, handle_hi, ctxt, 2);
DECLARE_REG(u32, flags, ctxt, 3);
+ u32 offset, len, fraglen, fragoff, em_mem_access_off;
struct ffa_mem_region_attributes *ep_mem_access;
struct ffa_composite_mem_region *reg;
- u32 offset, len, fraglen, fragoff;
struct ffa_mem_region *buf;
int ret = 0;
u64 handle;
@@ -599,16 +604,22 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
len = res->a1;
fraglen = res->a2;
- ep_mem_access = (void *)buf +
- ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
+ em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
+ if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ ffa_rx_release(res);
+ goto out_unlock;
+ }
+
+ ep_mem_access = (void *)buf + em_mem_access_off;
offset = ep_mem_access->composite_off;
/*
* We can trust the SPMD to get this right, but let's at least
* check that we end up with something that doesn't look _completely_
* bogus.
*/
- if (WARN_ON(offset > len ||
- fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)) {
+ if (offset + CONSTITUENTS_OFFSET(0) > len ||
+ fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
ret = FFA_RET_ABORTED;
ffa_rx_release(res);
goto out_unlock;
@@ -636,11 +647,16 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
ffa_rx_release(res);
}
+ reg = (void *)buf + offset;
+ if (offset + CONSTITUENTS_OFFSET(reg->addr_range_cnt) > len) {
+ ret = FFA_RET_ABORTED;
+ goto out_unlock;
+ }
+
ffa_mem_reclaim(res, handle_lo, handle_hi, flags);
if (res->a0 != FFA_SUCCESS)
goto out_unlock;
- reg = (void *)buf + offset;
/* If the SPMD was happy, then we should be too. */
WARN_ON(ffa_host_unshare_ranges(reg->constituents,
reg->addr_range_cnt));
@@ -864,7 +880,7 @@ out_unlock:
bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
{
- struct arm_smccc_1_2_regs res;
+ struct arm_smccc_1_2_regs res = {0};
/*
* There's no way we can tell what a non-standard SMC call might
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 3b2c4fbc34d8..24d6f164129a 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -1056,7 +1056,8 @@ static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa)
/* Fake up a data abort (level 3 translation fault on write) */
vcpu->arch.fault.esr_el2 = (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT) |
- ESR_ELx_WNR | ESR_ELx_FSC_FAULT |
+ ESR_ELx_IL | ESR_ELx_WNR |
+ ESR_ELx_FSC_FAULT |
FIELD_PREP(ESR_ELx_FSC_LEVEL, 3);
/* Shuffle the IPA around into the HPFAR */
diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
index 8c3fbb413a06..b1411fb54139 100644
--- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
+++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
@@ -268,6 +268,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr)
write_sysreg_el1(esr, SYS_ESR);
write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR);
+ write_sysreg_el1(read_sysreg_el2(SYS_SPSR), SYS_SPSR);
write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
}
@@ -278,7 +279,7 @@ static void inject_sync64(struct kvm_vcpu *vcpu, u64 esr)
*/
static void inject_undef64(struct kvm_vcpu *vcpu)
{
- inject_sync64(vcpu, (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT));
+ inject_sync64(vcpu, (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL);
}
static u64 read_id_reg(const struct kvm_vcpu *vcpu,
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 91a7dfad6686..b74dd5ce1efd 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -1370,16 +1370,19 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
if (prot & KVM_PGTABLE_PROT_W)
set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
- ret = stage2_set_xn_attr(prot, &xn);
- if (ret)
- return ret;
+ if (prot & KVM_PGTABLE_PROT_X) {
+ ret = stage2_set_xn_attr(prot, &xn);
+ if (ret)
+ return ret;
- set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
- clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+ set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+ clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+ }
ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags);
if (!ret || ret == -EAGAIN)
- kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level);
+ kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr,
+ (ret == -EAGAIN) ? TLBI_TTL_UNKNOWN : level);
return ret;
}
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index 89982bd3345f..d6c4fc16f879 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -138,11 +138,10 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr
pend_sync_exception(vcpu);
/*
- * Build an {i,d}abort, depending on the level and the
- * instruction set. Report an external synchronous abort.
+ * Build an {i,d}abort, depending on the level.
+ * Report an external synchronous abort.
*/
- if (kvm_vcpu_trap_il_is32bit(vcpu))
- esr |= ESR_ELx_IL;
+ esr |= ESR_ELx_IL;
/*
* Here, the guest runs in AArch64 mode when in EL1. If we get
@@ -170,14 +169,7 @@ void kvm_inject_sync(struct kvm_vcpu *vcpu, u64 esr)
static void inject_undef64(struct kvm_vcpu *vcpu)
{
- u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT);
-
- /*
- * Build an unknown exception, depending on the instruction
- * set.
- */
- if (kvm_vcpu_trap_il_is32bit(vcpu))
- esr |= ESR_ELx_IL;
+ u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL;
kvm_inject_sync(vcpu, esr);
}
@@ -389,7 +381,7 @@ int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr)
*/
if (!serror_is_masked(vcpu)) {
pend_serror_exception(vcpu);
- esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR);
+ esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR) | ESR_ELx_IL;
vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu));
return 1;
}
diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
index e2285ed8c91d..d1c3a352d5a2 100644
--- a/arch/arm64/kvm/mmio.c
+++ b/arch/arm64/kvm/mmio.c
@@ -126,6 +126,10 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
len = kvm_vcpu_dabt_get_as(vcpu);
data = kvm_mmio_read_buf(run->mmio.data, len);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
+ &data);
+ data = vcpu_data_host_to_guest(vcpu, data, len);
+
if (kvm_vcpu_dabt_issext(vcpu) &&
len < sizeof(unsigned long)) {
mask = 1U << ((len * 8) - 1);
@@ -135,9 +139,6 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
if (!kvm_vcpu_dabt_issf(vcpu))
data = data & 0xffffffff;
- trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
- &data);
- data = vcpu_data_host_to_guest(vcpu, data, len);
vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data);
}
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index fb54f6dad995..dfb96edbdc43 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -24,6 +24,7 @@ struct vncr_tlb {
struct s1_walk_result wr;
u64 hpa;
+ bool hpa_writable;
/* -1 when not mapped on a CPU */
int cpu;
@@ -1401,15 +1402,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
gfn = vt->wr.pa >> PAGE_SHIFT;
memslot = gfn_to_memslot(vcpu->kvm, gfn);
- if (!memslot)
+ if (!memslot) {
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
return -EFAULT;
+ }
*is_gmem = kvm_slot_has_gmem(memslot);
if (!*is_gmem) {
pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0,
&writable, &page);
- if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
+ if (is_error_noslot_pfn(pfn)) {
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
return -EFAULT;
+ }
} else {
ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
if (ret) {
@@ -1417,6 +1422,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
write_fault, false, false);
return ret;
}
+
+ writable = !(memslot->flags & KVM_MEM_READONLY);
+ }
+
+ /*
+ * FIXME: This check is too restrictive as KVM allows cacheable memory
+ * attributes for PFNMAP VMAs that have cacheable attributes in host
+ * stage-1.
+ */
+ if (!pfn_is_map_memory(pfn)) {
+ kvm_release_faultin_page(vcpu->kvm, page, true, false);
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
+ return -EINVAL;
}
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
@@ -1427,116 +1445,100 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
vt->gva = va;
vt->hpa = pfn << PAGE_SHIFT;
+ vt->hpa_writable = writable;
vt->valid = true;
vt->cpu = -1;
kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);
- kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw);
+ kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable);
}
- if (vt->wr.pw)
+ if (vt->wr.pw && vt->hpa_writable)
mark_page_dirty(vcpu->kvm, gfn);
return 0;
}
-static void inject_vncr_perm(struct kvm_vcpu *vcpu)
+static void handle_vncr_perm(struct kvm_vcpu *vcpu)
{
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
u64 esr = kvm_vcpu_get_esr(vcpu);
+ u64 fsc;
+
+ /*
+ * Promote to an external abort if the stage-1 permits writes but the
+ * HPA is read-only (e.g. RO memslot).
+ */
+ if (kvm_is_write_fault(vcpu) && vt->wr.pw && !vt->hpa_writable)
+ fsc = ESR_ELx_FSC_EXTABT;
+ /*
+ * Otherwise, inject a permission fault using the guest's translation
+ * level rather than the host's.
+ */
+ else
+ fsc = ESR_ELx_FSC_PERM_L(vt->wr.level);
- /* Adjust the fault level to reflect that of the guest's */
esr &= ~ESR_ELx_FSC;
- esr |= FIELD_PREP(ESR_ELx_FSC,
- ESR_ELx_FSC_PERM_L(vt->wr.level));
+ esr |= FIELD_PREP(ESR_ELx_FSC, fsc);
kvm_inject_nested_sync(vcpu, esr);
}
-static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu)
-{
- struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
-
- lockdep_assert_held_read(&vcpu->kvm->mmu_lock);
-
- if (!vt->valid)
- return false;
-
- if (read_vncr_el2(vcpu) != vt->gva)
- return false;
-
- if (vt->wr.nG)
- return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid;
-
- return true;
-}
-
int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
{
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
u64 esr = kvm_vcpu_get_esr(vcpu);
+ bool is_gmem = false;
+ bool perm;
+ int ret;
WARN_ON_ONCE(!(esr & ESR_ELx_VNCR));
if (kvm_vcpu_abt_issea(vcpu))
return kvm_handle_guest_sea(vcpu);
- if (esr_fsc_is_permission_fault(esr)) {
- inject_vncr_perm(vcpu);
- } else if (esr_fsc_is_translation_fault(esr)) {
- bool valid, is_gmem = false;
- int ret;
-
- scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
- valid = kvm_vncr_tlb_lookup(vcpu);
-
- if (!valid)
- ret = kvm_translate_vncr(vcpu, &is_gmem);
- else
- ret = -EPERM;
-
- switch (ret) {
- case -EAGAIN:
- /* Let's try again... */
- break;
- case -ENOMEM:
- /*
- * For guest_memfd, this indicates that it failed to
- * create a folio to back the memory. Inform userspace.
- */
- if (is_gmem)
- return 0;
- /* Otherwise, let's try again... */
- break;
- case -EFAULT:
- case -EIO:
- case -EHWPOISON:
- if (is_gmem)
- return 0;
- fallthrough;
- case -EINVAL:
- case -ENOENT:
- case -EACCES:
- /*
- * Translation failed, inject the corresponding
- * exception back to EL2.
- */
- BUG_ON(!vt->wr.failed);
+ if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) {
+ KVM_BUG(1, vcpu->kvm, "Unhandled VNCR abort, ESR=%llx\n", esr);
+ return -EIO;
+ }
- esr &= ~ESR_ELx_FSC;
- esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
+ ret = kvm_translate_vncr(vcpu, &is_gmem);
+ switch (ret) {
+ case -EAGAIN:
+ /* Let's try again... */
+ return 1;
+ case -ENOMEM:
+ /*
+ * For guest_memfd, this indicates that it failed to
+ * create a folio to back the memory. Inform userspace.
+ */
+ if (is_gmem)
+ return 0;
+ /* Otherwise, let's try again... */
+ break;
+ case -EFAULT:
+ case -EIO:
+ case -EHWPOISON:
+ if (is_gmem)
+ return 0;
+ fallthrough;
+ case -EINVAL:
+ case -ENOENT:
+ case -EACCES:
+ /*
+ * Translation failed, inject the corresponding
+ * exception back to EL2.
+ */
+ esr &= ~ESR_ELx_FSC;
+ esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
- kvm_inject_nested_sync(vcpu, esr);
- break;
- case -EPERM:
- /* Hack to deal with POE until we get kernel support */
- inject_vncr_perm(vcpu);
- break;
- case 0:
- break;
- }
- } else {
- WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr);
+ kvm_inject_nested_sync(vcpu, esr);
+ break;
+ case 0:
+ perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr;
+ if (!perm)
+ handle_vncr_perm(vcpu);
+ break;
}
return 1;
@@ -1574,7 +1576,7 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
vt->cpu = smp_processor_id();
- if (vt->wr.pw && vt->wr.pr)
+ if (vt->hpa_writable && vt->wr.pw && vt->wr.pr)
prot = PAGE_KERNEL;
else if (vt->wr.pr)
prot = PAGE_KERNEL_RO;
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 053e4f733e4b..428723b1b0f5 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -352,7 +352,7 @@ static int __pkvm_pgtable_stage2_reclaim(struct kvm_pgtable *pgt, u64 start, u64
page = pfn_to_page(mapping->pfn);
WARN_ON_ONCE(mapping->nr_pages != 1);
unpin_user_pages_dirty_lock(&page, 1, true);
- account_locked_vm(current->mm, 1, false);
+ account_locked_vm(kvm->mm, 1, false);
pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
kfree(mapping);
}
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 4477f870c7b3..740b39875728 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -508,6 +508,8 @@ static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db)
struct kvm_io_device *kvm_io_dev;
struct vgic_io_device *iodev;
+ guard(srcu)(&kvm->srcu);
+
kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db);
if (!kvm_io_dev)
return ERR_PTR(-EINVAL);
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 5a4768d8cd4f..ccb7e3a90cd0 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -203,6 +203,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) {
if (irq_is_lpi(vcpu->kvm, irq->intid)) {
raw_spin_lock(&irq->irq_lock);
+ irq->pending_latch = false;
list_del(&irq->ap_list);
irq->vcpu = NULL;
raw_spin_unlock(&irq->irq_lock);
@@ -792,7 +793,11 @@ retry:
continue;
}
- /* This interrupt looks like it has to be migrated. */
+ /*
+ * This interrupt looks like it has to be migrated,
+ * make sure it is kept alive while locks are dropped.
+ */
+ vgic_get_irq_ref(irq);
raw_spin_unlock(&irq->irq_lock);
raw_spin_unlock(&vgic_cpu->ap_list_lock);
@@ -815,15 +820,16 @@ retry:
raw_spin_lock(&irq->irq_lock);
/*
- * If the affinity has been preserved, move the
- * interrupt around. Otherwise, it means things have
- * changed while the interrupt was unlocked, and we
- * need to replay this.
+ * If the interrupt is still ours and its affinity has
+ * been preserved, move it around. Otherwise, it means
+ * things have changed while the interrupt was unlocked
+ * (it may even have been taken off the list with its
+ * affinity left untouched), and we need to replay this.
*
* In all cases, we cannot trust the list not to have
* changed, so we restart from the beginning.
*/
- if (target_vcpu == vgic_target_oracle(irq)) {
+ if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) {
struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;
list_del(&irq->ap_list);
@@ -836,6 +842,8 @@ retry:
raw_spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock);
raw_spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock);
+ deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq);
+
if (target_vcpu_needs_kick) {
kvm_make_request(KVM_REQ_IRQ_PENDING, target_vcpu);
kvm_vcpu_kick(target_vcpu);
diff --git a/arch/powerpc/include/asm/preempt.h b/arch/powerpc/include/asm/preempt.h
deleted file mode 100644
index 000e2b9681f3..000000000000
--- a/arch/powerpc/include/asm/preempt.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __ASM_POWERPC_PREEMPT_H
-#define __ASM_POWERPC_PREEMPT_H
-
-#include <asm-generic/preempt.h>
-
-#if defined(CONFIG_PREEMPT_DYNAMIC)
-#include <linux/jump_label.h>
-DECLARE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched);
-#define need_irq_preemption() \
- (static_branch_unlikely(&sk_dynamic_irqentry_exit_cond_resched))
-#else
-#define need_irq_preemption() (IS_ENABLED(CONFIG_PREEMPTION))
-#endif
-
-#endif /* __ASM_POWERPC_PREEMPT_H */
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 7b8c56962c31..49039074b33f 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -537,7 +537,7 @@ static inline void __user *mask_user_address(const void __user *ptr)
if (IS_ENABLED(CONFIG_PPC64))
return mask_user_address_simple(ptr);
- if (IS_ENABLED(CONFIG_E500))
+ if (IS_ENABLED(CONFIG_PPC_E500))
return mask_user_address_isel(ptr);
if (TASK_SIZE <= UL(SZ_2G) && border >= UL(SZ_2G))
return mask_user_address_simple(ptr);
diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c
index 3af6c06af02f..e5853daa6a48 100644
--- a/arch/powerpc/kernel/dt_cpu_ftrs.c
+++ b/arch/powerpc/kernel/dt_cpu_ftrs.c
@@ -704,6 +704,15 @@ static void __init cpufeatures_setup_start(u32 isa)
if (isa >= ISA_V3_1) {
cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31;
cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1;
+
+ /*
+ * CPU_FTR_P11_PVR is a kernel-internal flag to identify
+ * Power11 and later processors. While ISA v3.1 is supported
+ * by Power10+, this flag specifically indicates Power11+
+ * for code that needs to distinguish between P10 and P11.
+ */
+ if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11)
+ cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR;
}
}
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 3460d1a5a97c..11145c40183d 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -377,7 +377,6 @@ void vtime_task_switch(struct task_struct *prev)
}
}
-#ifdef CONFIG_NO_HZ_COMMON
/**
* vtime_reset - Fast forward vtime entry clocks
*
@@ -394,6 +393,7 @@ void vtime_reset(void)
#endif
}
+#ifdef CONFIG_NO_HZ_COMMON
/**
* vtime_dyntick_start - Inform vtime about entry to idle-dynticks
*
@@ -933,6 +933,7 @@ static void __init set_decrementer_max(void)
static void __init init_decrementer_clockevent(void)
{
register_decrementer_clockevent(smp_processor_id());
+ vtime_reset();
}
void secondary_cpu_time_init(void)
@@ -948,6 +949,7 @@ void secondary_cpu_time_init(void)
/* FIME: Should make unrelated change to move snapshot_timebase
* call here ! */
register_decrementer_clockevent(smp_processor_id());
+ vtime_reset();
}
/*
diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-helper.c
index 57e897b60db8..cc9fb72cb4eb 100644
--- a/arch/powerpc/lib/vmx-helper.c
+++ b/arch/powerpc/lib/vmx-helper.c
@@ -46,7 +46,7 @@ int exit_vmx_usercopy(void)
* set and we are preemptible. The hack here is to schedule a
* decrementer to fire here and reschedule for us if necessary.
*/
- if (need_irq_preemption() && need_resched())
+ if (need_resched())
set_dec(1);
return 0;
}
diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c
index 757811155587..c11deb2f50ed 100644
--- a/arch/powerpc/platforms/85xx/common.c
+++ b/arch/powerpc/platforms/85xx/common.c
@@ -42,6 +42,8 @@ static const struct of_device_id mpc85xx_common_ids[] __initconst = {
{ .compatible = "fsl,qoriq-pcie-v2.3", },
{ .compatible = "fsl,qoriq-pcie-v2.2", },
{ .compatible = "fsl,fman", },
+ /* IFC NAND and NOR controllers */
+ { .compatible = "fsl,ifc", },
{},
};
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index f6de8c1169d5..de7494748fec 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -268,10 +268,12 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma,
if (write && !(vma->vm_flags & VM_WRITE))
return -EACCES;
+ if (offset >= LS_SIZE)
+ return -EFAULT;
if (spu_acquire(ctx))
return -EINTR;
- if ((offset + len) > vma->vm_end)
- len = vma->vm_end - offset;
+ if ((offset + len) > LS_SIZE)
+ len = LS_SIZE - offset;
local_store = ctx->ops->get_ls(ctx);
if (write)
memcpy_toio(local_store + offset, buf, len);
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index f7052b131a4c..74910ce3a541 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -154,6 +154,7 @@ config HV_PERF_CTRS
config VPA_PMU
tristate "VPA PMU events"
depends on KVM_BOOK3S_64_HV && HV_PERF_CTRS
+ default m
help
Enable access to the VPA PMU counters via perf. This enables
code that support measurement for KVM on PowerVM(KoP) feature.
diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c
index c6159870de0e..9c3758aa54c6 100644
--- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c
+++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c
@@ -271,11 +271,9 @@ retry:
esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs);
temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL);
- if (temp_esi_buf)
- esi_buf = temp_esi_buf;
- else
- return -ENOMEM;
-
+ if (!temp_esi_buf)
+ goto out_free_esi_buf;
+ esi_buf = temp_esi_buf;
goto retry;
}
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c0a6992933e4..f7028caaeae0 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -157,7 +157,7 @@ config RISCV
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS if MMU
select HAVE_DYNAMIC_FTRACE if MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE)
- select FUNCTION_ALIGNMENT_4B if HAVE_DYNAMIC_FTRACE && RISCV_ISA_C
+ select FUNCTION_ALIGNMENT_4B if DYNAMIC_FTRACE && RISCV_ISA_C
select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS if HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI)
select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h
index 8cfe59483a8f..c2b0a2928f06 100644
--- a/arch/riscv/include/asm/cacheflush.h
+++ b/arch/riscv/include/asm/cacheflush.h
@@ -40,7 +40,7 @@ do { \
flush_icache_mm(vma->vm_mm, 0); \
} while (0)
-#ifdef CONFIG_64BIT
+#if defined(CONFIG_64BIT) && defined(CONFIG_MMU)
/* This is accessed in assembly code. cpumask_var_t would be too complex. */
extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS);
extern char _end[];
@@ -56,7 +56,8 @@ static inline void mark_new_valid_map(void)
#define flush_cache_vmap flush_cache_vmap
static inline void flush_cache_vmap(unsigned long start, unsigned long end)
{
- if (is_vmalloc_or_module_addr((void *)start))
+ if (is_vmalloc_or_module_addr((void *)start) ||
+ (start >= VMEMMAP_START && end <= VMEMMAP_END))
mark_new_valid_map();
}
#define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end)
diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h
index 09bb5f57a9d3..92d5f831f349 100644
--- a/arch/riscv/include/asm/io.h
+++ b/arch/riscv/include/asm/io.h
@@ -102,12 +102,14 @@ __io_reads_ins(reads, u32, l, __io_br(), __io_ar(addr))
#define readsw(addr, buffer, count) __readsw(addr, buffer, count)
#define readsl(addr, buffer, count) __readsl(addr, buffer, count)
+#ifdef CONFIG_HAS_IOPORT
__io_reads_ins(ins, u8, b, __io_pbr(), __io_par(addr))
__io_reads_ins(ins, u16, w, __io_pbr(), __io_par(addr))
__io_reads_ins(ins, u32, l, __io_pbr(), __io_par(addr))
#define insb(addr, buffer, count) __insb(PCI_IOBASE + (addr), buffer, count)
#define insw(addr, buffer, count) __insw(PCI_IOBASE + (addr), buffer, count)
#define insl(addr, buffer, count) __insl(PCI_IOBASE + (addr), buffer, count)
+#endif
__io_writes_outs(writes, u8, b, __io_bw(), __io_aw())
__io_writes_outs(writes, u16, w, __io_bw(), __io_aw())
@@ -116,26 +118,32 @@ __io_writes_outs(writes, u32, l, __io_bw(), __io_aw())
#define writesw(addr, buffer, count) __writesw(addr, buffer, count)
#define writesl(addr, buffer, count) __writesl(addr, buffer, count)
+#ifdef CONFIG_HAS_IOPORT
__io_writes_outs(outs, u8, b, __io_pbw(), __io_paw())
__io_writes_outs(outs, u16, w, __io_pbw(), __io_paw())
__io_writes_outs(outs, u32, l, __io_pbw(), __io_paw())
#define outsb(addr, buffer, count) __outsb(PCI_IOBASE + (addr), buffer, count)
#define outsw(addr, buffer, count) __outsw(PCI_IOBASE + (addr), buffer, count)
#define outsl(addr, buffer, count) __outsl(PCI_IOBASE + (addr), buffer, count)
+#endif
#ifdef CONFIG_64BIT
__io_reads_ins(reads, u64, q, __io_br(), __io_ar(addr))
#define readsq(addr, buffer, count) __readsq(addr, buffer, count)
+#ifdef CONFIG_HAS_IOPORT
__io_reads_ins(ins, u64, q, __io_pbr(), __io_par(addr))
#define insq(addr, buffer, count) __insq(PCI_IOBASE + (addr), buffer, count)
+#endif
__io_writes_outs(writes, u64, q, __io_bw(), __io_aw())
#define writesq(addr, buffer, count) __writesq(addr, buffer, count)
+#ifdef CONFIG_HAS_IOPORT
__io_writes_outs(outs, u64, q, __io_pbr(), __io_paw())
#define outsq(addr, buffer, count) __outsq(PCI_IOBASE + (addr), buffer, count)
#endif
+#endif
#include <asm-generic/io.h>
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index 08df724e13b9..d799c4e56f80 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -137,7 +137,7 @@ SYM_CODE_START(handle_exception)
.Lrestore_kernel_tpsp:
csrr tp, CSR_SCRATCH
-#ifdef CONFIG_64BIT
+#if defined(CONFIG_64BIT) && defined(CONFIG_MMU)
/*
* The RISC-V kernel does not flush TLBs on all CPUS after each new
* vmalloc mapping or kfence_unprotect(), which may result in
diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c
index 1659d31fd288..caf6762427c8 100644
--- a/arch/riscv/kernel/sys_hwprobe.c
+++ b/arch/riscv/kernel/sys_hwprobe.c
@@ -450,6 +450,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
if (cpusetsize > cpumask_size())
cpusetsize = cpumask_size();
+ cpumask_clear(&cpus);
ret = copy_from_user(&cpus, cpus_user, cpusetsize);
if (ret)
return -EFAULT;
diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile
index a842dc034571..43ee881f6c6f 100644
--- a/arch/riscv/kernel/vdso/Makefile
+++ b/arch/riscv/kernel/vdso/Makefile
@@ -69,9 +69,9 @@ CPPFLAGS_$(vdso_lds) += -DHAS_VGETTIMEOFDAY
endif
# Disable -pg to prevent insert call site
-CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)
-CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)
-CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)
+CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO)
+CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO)
+CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO)
# Force dependency
$(obj)/$(vdso_o): $(obj)/$(vdso_so)
diff --git a/arch/riscv/kernel/vdso/rt_sigreturn.S b/arch/riscv/kernel/vdso/rt_sigreturn.S
index e82987dc3739..d6f96b1abe40 100644
--- a/arch/riscv/kernel/vdso/rt_sigreturn.S
+++ b/arch/riscv/kernel/vdso/rt_sigreturn.S
@@ -7,11 +7,19 @@
#include <asm/unistd.h>
#include <asm/assembler.h>
+/*
+ * WARNING: Do NOT add a CFI landing pad at the start of this function.
+ * Unwinders such as libgcc identify the sigreturn trampoline by matching the
+ * instruction sequence. Adding a landing pad here would break unwinding from
+ * signal handlers.
+ *
+ * This trampoline is used only for signal return and not via an indirect
+ * call/jump from userspace, so adding CFI landing pad is unnecessary.
+ */
.text
SYM_FUNC_START(__vdso_rt_sigreturn)
.cfi_startproc
.cfi_signal_frame
- vdso_lpad
li a7, __NR_rt_sigreturn
ecall
.cfi_endproc
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 5b1b3c88b4d1..3e450890be07 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -37,7 +37,9 @@
#include "../kernel/head.h"
+#if defined(CONFIG_64BIT) && defined(CONFIG_MMU)
DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS);
+#endif
struct kernel_mapping kernel_map __ro_after_init;
EXPORT_SYMBOL(kernel_map);
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index 7aa655664ecc..2076ac22e2c4 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/perf_event.h>
+#include <linux/nospec.h>
#include <asm/cpu_mf.h>
#include <asm/hwctrset.h>
@@ -768,6 +769,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
if (!is_userspace_event(ev)) {
if (ev >= ARRAY_SIZE(cpumf_generic_events_user))
return -EOPNOTSUPP;
+ ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_user));
ev = cpumf_generic_events_user[ev];
}
} else if (!attr->exclude_kernel && attr->exclude_user) {
@@ -778,6 +780,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
if (!is_userspace_event(ev)) {
if (ev >= ARRAY_SIZE(cpumf_generic_events_basic))
return -EOPNOTSUPP;
+ ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_basic));
ev = cpumf_generic_events_basic[ev];
}
}
diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c
index 5f1960ec982d..ed4259d17629 100644
--- a/arch/s390/kvm/dat.c
+++ b/arch/s390/kvm/dat.c
@@ -570,6 +570,8 @@ static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *tabl
else if (walk->ops->pte_entry)
rc = dat_pte_walk_range(max(start, cur), min(end, next),
dereference_pmd(crste.pmd), walk);
+ if (rc)
+ break;
}
}
return rc;
diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c
index 298fbaecec28..8abb4f55b306 100644
--- a/arch/s390/kvm/gmap.c
+++ b/arch/s390/kvm/gmap.c
@@ -1374,8 +1374,13 @@ struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *pare
/* Only allow one real-space gmap shadow. */
list_for_each_entry(sg, &parent->children, list) {
if (sg->guest_asce.r) {
- scoped_guard(write_lock, &parent->kvm->mmu_lock)
+ if (write_trylock(&parent->kvm->mmu_lock)) {
gmap_unshadow(sg);
+ write_unlock(&parent->kvm->mmu_lock);
+ } else {
+ gmap_put(new);
+ return ERR_PTR(-EAGAIN);
+ }
break;
}
}
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 23c817595e28..150b5dd2170e 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1280,8 +1280,10 @@ static int kvm_s390_vm_stop_migration(struct kvm *kvm)
* PGSTEs might have cmma_d set.
*/
WRITE_ONCE(kvm->arch.migration_mode, 0);
- if (kvm->arch.use_cmma)
- kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION);
+ if (!kvm->arch.use_cmma)
+ return 0;
+
+ kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION);
/* Clear cmma_d on all existing PGSTEs and set cmma_dirty_pages to 0. */
gmap_set_cmma_all_clean(kvm->arch.gmap);
atomic64_set(&kvm->arch.cmma_dirty_pages, 0);
diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c
index 5b075c38998e..720bb58cabe2 100644
--- a/arch/s390/kvm/pci.c
+++ b/arch/s390/kvm/pci.c
@@ -300,9 +300,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
gaite->gisc = fib->fmt0.isc;
gaite->count++;
- gaite->aisbo = fib->fmt0.aisbo;
- gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb &
- ~PAGE_MASK));
+ if (fib->fmt0.sum == 1) {
+ gaite->aisbo = fib->fmt0.aisbo;
+ gaite->aisb = virt_to_phys(page_address(aisb_page) +
+ (fib->fmt0.aisb & ~PAGE_MASK));
+ } else {
+ gaite->aisbo = 0;
+ gaite->aisb = 0;
+ }
aift->kzdev[zdev->aisb] = zdev->kzdev;
spin_unlock_irq(&aift->gait_lock);
@@ -328,6 +333,7 @@ unpin2:
unpin1:
unpin_user_page(aibv_page);
out:
+ kvm_s390_gisc_unregister(kvm, fib->fmt0.isc);
return rc;
}
diff --git a/arch/s390/lib/csum-partial.c b/arch/s390/lib/csum-partial.c
index 458abd9bac70..9d74ceff136c 100644
--- a/arch/s390/lib/csum-partial.c
+++ b/arch/s390/lib/csum-partial.c
@@ -23,7 +23,7 @@ static __always_inline __wsum csum_copy(void *dst, const void *src, int len, __w
if (!cpu_has_vx()) {
if (copy)
memcpy(dst, src, len);
- return cksm(dst, len, sum);
+ return cksm(src, len, sum);
}
kernel_fpu_begin(&vxstate, KERNEL_VXR_V16V23);
fpu_vlvgf(16, (__force u32)sum, 1);
diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c
index f196b1d1ddf8..aed27604c11f 100644
--- a/arch/x86/boot/compressed/acpi.c
+++ b/arch/x86/boot/compressed/acpi.c
@@ -184,10 +184,15 @@ static unsigned long get_cmdline_acpi_rsdp(void)
char val[MAX_ADDR_LEN] = { };
int ret;
- ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN);
+ ret = cmdline_find_option("acpi_rsdp", val, sizeof(val));
if (ret < 0)
return 0;
+ if (ret >= sizeof(val)) {
+ warn("acpi_rsdp= value too long; ignoring");
+ return 0;
+ }
+
if (boot_kstrtoul(val, 16, &addr))
return 0;
#endif
diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c
index 023bf1c3de8b..5b83beab89e1 100644
--- a/arch/x86/boot/early_serial_console.c
+++ b/arch/x86/boot/early_serial_console.c
@@ -117,7 +117,7 @@ static unsigned int probe_baud(int port)
static void parse_console_uart8250(void)
{
char optstr[64], *options;
- int baud = DEFAULT_BAUD;
+ int baud;
int port = 0;
/*
@@ -136,10 +136,13 @@ static void parse_console_uart8250(void)
else
return;
- if (options && (options[0] == ','))
- baud = simple_strtoull(options + 1, &options, 0);
- else
+ if (options && (options[0] == ',')) {
+ baud = simple_strtoull(options + 1, NULL, 0);
+ if (!baud)
+ baud = DEFAULT_BAUD;
+ } else {
baud = probe_baud(port);
+ }
if (port)
early_serial_init(port, baud);
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index d2e8a849f180..5b1070ec85d9 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -46,7 +46,6 @@ obj-$(CONFIG_CPU_SUP_HYGON) += hygon.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o
obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o
-obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o
obj-$(CONFIG_CPU_SUP_ZHAOXIN) += zhaoxin.o
obj-$(CONFIG_CPU_SUP_VORTEX_32) += vortex.o
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index 8c62c6d4d5c1..cb8ac4b9b0d7 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -488,8 +488,10 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) {
ret = kvm_pi_update_irte(irqfd, &irqfd->irq_entry);
- if (ret)
+ if (ret) {
kvm->arch.nr_possible_bypass_irqs--;
+ irqfd->producer = NULL;
+ }
}
spin_unlock_irq(&kvm->irqfds.lock);
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 6f30bbdddb5a..38bba9a1114c 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -3371,6 +3371,12 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
struct kvm_lapic *apic)
{
int vector;
+
+ if (unlikely(!pv_eoi_enabled(vcpu))) {
+ __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
+ return;
+ }
+
/*
* PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host
* and KVM_PV_EOI_ENABLED in guest memory as follows:
@@ -3382,8 +3388,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
* KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset:
* -> host enabled PV EOI, guest executed EOI.
*/
- BUG_ON(!pv_eoi_enabled(vcpu));
-
if (pv_eoi_test_and_clr_pending(vcpu))
return;
vector = apic_set_eoi(apic);
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 427229347876..944aaea6501f 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2129,8 +2129,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd)
if (ret)
return ret;
+ /* Do not allow SNP VM migration until additional state transfer is implemented */
if (kvm->arch.vm_type != source_kvm->arch.vm_type ||
- sev_guest(kvm) || !sev_guest(source_kvm)) {
+ sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm)) {
ret = -EINVAL;
goto out_unlock;
}
@@ -2851,8 +2852,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd)
* disallow out-of-band SEV/SEV-ES init if the target is already an
* SEV guest, or if vCPUs have been created. KVM relies on vCPUs being
* created after SEV/SEV-ES initialization, e.g. to init intercepts.
+ * Also do not allow SNP VM mirroring until additional state transfer is implemented.
*/
- if (sev_guest(kvm) || !sev_guest(source_kvm) ||
+ if (sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm) ||
is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) {
ret = -EINVAL;
goto e_unlock;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 4d2bacd00ec4..d0971685034b 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -571,7 +571,12 @@ static int svm_enable_virtualization_cpu(void)
return r;
sd = per_cpu_ptr(&svm_data, me);
- sd->asid_generation = 1;
+ /*
+ * Bump the current asid_generation value to ensure any vCPU that
+ * previously ran on this CPU sees a stale generation and is forced
+ * to acquire a new ASID, preventing a latent ASID collision.
+ */
+ sd->asid_generation++;
sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
sd->next_asid = sd->max_asid + 1;
sd->min_asid = max_sev_asid + 1;
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index 0db25bba17f6..93de876c318c 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -490,7 +490,7 @@ TRACE_EVENT(kvm_inj_exception,
TP_printk("%s%s%s%s%s",
__print_symbolic(__entry->exception, kvm_trace_sym_exc),
!__entry->has_error ? "" : " (",
- !__entry->has_error ? "" : __print_symbolic(__entry->error_code, { }),
+ !__entry->has_error ? "" : __print_symbolic(__entry->error_code),
!__entry->has_error ? "" : ")",
__entry->reinjected ? " [reinjected]" : "")
);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 6957bb6f5cf7..220d42ebc82e 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -582,6 +582,9 @@ static int nested_vmx_check_msr_bitmap_controls(struct kvm_vcpu *vcpu,
static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu,
struct vmcs12 *vmcs12)
{
+ gpa_t vtpr_gpa = vmcs12->virtual_apic_page_addr + APIC_TASKPRI;
+ u32 vtpr;
+
if (!nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW))
return 0;
@@ -591,6 +594,32 @@ static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu,
if (CC(!nested_cpu_has_vid(vmcs12) && vmcs12->tpr_threshold >> 4))
return -EINVAL;
+ /*
+ * Do the illegal vTPR vs. TPR Threshold consistency check if and only
+ * if KVM is configured to WARN on missed consistency checks, otherwise
+ * it's a waste of time. KVM needs to rely on hardware to fully detect
+ * an illegal combination due to the vTPR being writable by L1 at all
+ * times (it's an in-memory value, not a VMCS field). I.e. even if the
+ * check passes now, it might fail at the actual VM-Enter.
+ *
+ * If reading guest memory fails, skip the check as KVM's de facto ABI
+ * for VMX instruction accesses to non-existent memory is to provide
+ * PCI Bus Error semantics (reads return 0xFFs), in which case the vTPR
+ * is guaranteed to greater than or equal to the threshold.
+ *
+ * Note! Deliberately use the VM-scoped API when reading guest memory,
+ * to ensure the read doesn't hit SMRAM when restoring L2 state on RSM,
+ * and only perform the check when in KVM_RUN, to avoid a false failure
+ * if userspace hasn't yet configured memslots during state restore.
+ */
+ if (warn_on_missed_cc && vcpu->wants_to_run &&
+ nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) &&
+ !nested_cpu_has_vid(vmcs12) &&
+ !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) &&
+ !kvm_read_guest(vcpu->kvm, vtpr_gpa, &vtpr, sizeof(vtpr)) &&
+ CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > ((vtpr >> 4) & GENMASK(3, 0))))
+ return -EINVAL;
+
return 0;
}
@@ -3104,38 +3133,6 @@ static int nested_vmx_check_controls(struct kvm_vcpu *vcpu,
return 0;
}
-static int nested_vmx_check_controls_late(struct kvm_vcpu *vcpu,
- struct vmcs12 *vmcs12)
-{
- void *vapic = to_vmx(vcpu)->nested.virtual_apic_map.hva;
- u32 vtpr = vapic ? (*(u32 *)(vapic + APIC_TASKPRI)) >> 4 : 0;
-
- /*
- * Don't bother with the consistency checks if KVM isn't configured to
- * WARN on missed consistency checks, as KVM needs to rely on hardware
- * to fully detect an illegal vTPR vs. TRP Threshold combination due to
- * the vTPR being writable by L1 at all times (it's an in-memory value,
- * not a VMCS field). I.e. even if the check passes now, it might fail
- * at the actual VM-Enter.
- *
- * Keying off the module param also allows treating an invalid vAPIC
- * mapping as a consistency check failure without increasing the risk
- * of breaking a "real" VM.
- */
- if (!warn_on_missed_cc)
- return 0;
-
- if ((exec_controls_get(to_vmx(vcpu)) & CPU_BASED_TPR_SHADOW) &&
- nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) &&
- !nested_cpu_has_vid(vmcs12) &&
- !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) &&
- (CC(!vapic) ||
- CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > (vtpr & GENMASK(3, 0)))))
- return -EINVAL;
-
- return 0;
-}
-
static int nested_vmx_check_address_space_size(struct kvm_vcpu *vcpu,
struct vmcs12 *vmcs12)
{
@@ -3661,19 +3658,14 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
&vmx->nested.pre_vmenter_ssp_tbl);
/*
- * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled. In the
- * event of a "late" VM-Fail, i.e. a VM-Fail detected by hardware but
- * not KVM, KVM must unwind its software model to the pre-VM-Entry host
- * state. When EPT is disabled, GUEST_CR3 holds KVM's shadow CR3, not
- * L1's "real" CR3, which causes nested_vmx_restore_host_state() to
- * corrupt vcpu->arch.cr3. Stuffing vmcs01.GUEST_CR3 results in the
- * unwind naturally setting arch.cr3 to the correct value. Smashing
- * vmcs01.GUEST_CR3 is safe because nested VM-Exits, and the unwind,
- * reset KVM's MMU, i.e. vmcs01.GUEST_CR3 is guaranteed to be
- * overwritten with a shadow CR3 prior to re-entering L1.
+ * Stash L1's CR3, so that in the event of a "late" VM-Fail, i.e. a
+ * VM-Fail detected by hardware but not KVM, KVM can unwind its
+ * software model to the pre-VM-Entry host state. When EPT is
+ * disabled, GUEST_CR3 holds KVM's shadow CR3, not L1's "real" CR3,
+ * and so simply restoring from vmcs01.GUEST_CR3 would corrupt
+ * vcpu->arch.cr3.
*/
- if (!enable_ept)
- vmcs_writel(GUEST_CR3, vcpu->arch.cr3);
+ vmx->nested.pre_vmenter_cr3 = kvm_read_cr3(vcpu);
vmx_switch_vmcs(vcpu, &vmx->nested.vmcs02);
@@ -3685,11 +3677,6 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
return NVMX_VMENTRY_KVM_INTERNAL_ERROR;
}
- if (nested_vmx_check_controls_late(vcpu, vmcs12)) {
- vmx_switch_vmcs(vcpu, &vmx->vmcs01);
- return NVMX_VMENTRY_VMFAIL;
- }
-
if (nested_vmx_check_guest_state(vcpu, vmcs12,
&entry_failure_code)) {
exit_reason.basic = EXIT_REASON_INVALID_STATE;
@@ -3774,6 +3761,8 @@ vmentry_fail_vmexit:
if (!from_vmentry)
return NVMX_VMENTRY_VMEXIT;
+ nested_put_vmcs12_pages(vcpu);
+
load_vmcs12_host_state(vcpu, vmcs12);
vmcs12->vm_exit_reason = exit_reason.full;
if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx))
@@ -4990,7 +4979,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu)
vmx_set_cr4(vcpu, vmcs_readl(CR4_READ_SHADOW));
nested_ept_uninit_mmu_context(vcpu);
- vcpu->arch.cr3 = vmcs_readl(GUEST_CR3);
+ vcpu->arch.cr3 = vmx->nested.pre_vmenter_cr3;
kvm_register_mark_available(vcpu, VCPU_REG_CR3);
/*
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 989ab29b8c6f..545b03d9d10b 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -2797,7 +2797,11 @@ static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd)
goto out;
}
- if (init_vm->cpuid.padding) {
+ /*
+ * Reject the request if userspace changes cpuid.nent between the
+ * initial read and the subsequent copy.
+ */
+ if (init_vm->cpuid.padding || init_vm->cpuid.nent != nr_user_entries) {
ret = -EINVAL;
goto out;
}
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index de9de0d2016c..dc8517f15bc4 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -160,6 +160,13 @@ struct nested_vmx {
bool preemption_timer_expired;
/*
+ * Used to restore L1's CR3 if hardware detects a VM-Fail Consistency
+ * Check that KVM does not, in which case KVM needs to unwind CR3 back
+ * to its pre-VM-Enter state, NOT to vmcs01.HOST_CR3.
+ */
+ unsigned long pre_vmenter_cr3;
+
+ /*
* Used to snapshot MSRs that are conditionally loaded on VM-Enter in
* order to propagate the guest's pre-VM-Enter value into vmcs02. For
* emulation of VMLAUNCH/VMRESUME, the snapshot will be of L1's value.
diff --git a/block/bio.c b/block/bio.c
index f2a5f4d0a967..6a2f6fc3413e 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -555,6 +555,14 @@ struct bio *bio_alloc_bioset(struct block_device *bdev, unsigned short nr_vecs,
bio = bio_alloc_percpu_cache(bs);
} else {
opf &= ~REQ_ALLOC_CACHE;
+ }
+
+ /*
+ * For a bioset without a percpu cache, or when the percpu cache was
+ * empty, try a slab allocation with optimistic GFP_ flags before
+ * falling back to the mempool.
+ */
+ if (!bio) {
p = kmem_cache_alloc(bs->bio_slab, gfp);
if (p)
bio = p + bs->front_pad;
@@ -1191,7 +1199,7 @@ void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter)
* for the next iteration.
*/
static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
- unsigned len_align_mask)
+ struct bio_vec *bv, unsigned len_align_mask)
{
size_t nbytes = bio->bi_iter.bi_size & len_align_mask;
@@ -1200,23 +1208,16 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
iov_iter_revert(iter, nbytes);
bio->bi_iter.bi_size -= nbytes;
- do {
- struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
-
- if (nbytes < bv->bv_len) {
- bv->bv_len -= nbytes;
- break;
- }
-
+ while (nbytes >= bv->bv_len) {
if (bio_flagged(bio, BIO_PAGE_PINNED))
unpin_user_page(bv->bv_page);
- bio->bi_vcnt--;
+ if (!--bio->bi_vcnt)
+ return -EFAULT;
nbytes -= bv->bv_len;
- } while (nbytes);
-
- if (!bio->bi_vcnt)
- return -EFAULT;
+ bv--;
+ }
+ bv->bv_len -= nbytes;
return 0;
}
@@ -1276,7 +1277,8 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
if (is_pci_p2pdma_page(bio->bi_io_vec->bv_page))
bio->bi_opf |= REQ_NOMERGE;
- return bio_iov_iter_align_down(bio, iter, len_align_mask);
+ return bio_iov_iter_align_down(bio, iter,
+ &bio->bi_io_vec[bio->bi_vcnt - 1], len_align_mask);
}
static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size,
@@ -1285,7 +1287,8 @@ static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size,
struct folio *folio;
while (*size > minsize) {
- folio = folio_alloc(gfp | __GFP_NORETRY, get_order(*size));
+ folio = folio_alloc(gfp | __GFP_NORETRY | __GFP_NOWARN,
+ get_order(*size));
if (folio)
return folio;
*size = rounddown_pow_of_two(*size - 1);
@@ -1302,7 +1305,7 @@ static void bio_free_folios(struct bio *bio)
bio_for_each_bvec_all(bv, bio, i) {
struct folio *folio = bvec_folio(bv);
- if (!is_zero_folio(folio))
+ if (!is_zero_folio(folio) && !is_huge_zero_folio(folio))
folio_put(folio);
}
}
@@ -1360,7 +1363,8 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
if (!bio->bi_iter.bi_size)
return -ENOMEM;
- return bio_iov_iter_align_down(bio, iter, minsize - 1);
+ return bio_iov_iter_align_down(bio, iter,
+ &bio->bi_io_vec[bio->bi_vcnt - 1], minsize - 1);
}
static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
@@ -1368,21 +1372,18 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
{
size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M);
struct folio *folio;
+ ssize_t ret;
folio = folio_alloc_greedy(GFP_KERNEL, &len, minsize);
if (!folio)
return -ENOMEM;
do {
- ssize_t ret;
-
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
&bio->bi_vcnt, bio->bi_max_vecs - 1, 0);
if (ret <= 0) {
- if (!bio->bi_vcnt) {
- folio_put(folio);
- return ret;
- }
+ if (!bio->bi_vcnt)
+ goto out_folio_put;
break;
}
len -= ret;
@@ -1398,7 +1399,20 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0);
if (iov_iter_extract_will_pin(iter))
bio_set_flag(bio, BIO_PAGE_PINNED);
- return bio_iov_iter_align_down(bio, iter, minsize - 1);
+
+ /* The first vec stores the bounce buffer, so do not subtract 1 here. */
+ ret = bio_iov_iter_align_down(bio, iter,
+ &bio->bi_io_vec[bio->bi_vcnt], minsize - 1);
+ if (ret)
+ goto out_folio_put;
+
+ /* Update the bounc buffer bv_len to the aligned down size. */
+ bio->bi_io_vec[0].bv_len = bio->bi_iter.bi_size;
+ return 0;
+
+out_folio_put:
+ folio_put(folio);
+ return ret;
}
/**
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index d2a1f5903f24..d9676126c5b5 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -434,15 +434,15 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
blkg->pd[i]->online = true;
}
}
+ blkg->online = true;
}
- blkg->online = true;
spin_unlock(&blkcg->lock);
if (!ret)
return blkg;
/* @blkg failed fully initialized, use the usual release path */
- blkg_put(blkg);
+ percpu_ref_kill(&blkg->refcnt);
return ERR_PTR(ret);
err_free_blkg:
diff --git a/block/blk-map.c b/block/blk-map.c
index 768549f19f97..d1d6bbe0ecf1 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -653,6 +653,7 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len,
gfp_t gfp_mask)
{
unsigned long addr = (unsigned long) kbuf;
+ bool do_copy;
struct bio *bio;
int ret;
@@ -661,7 +662,8 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len,
if (!len || !kbuf)
return -EINVAL;
- if (!blk_rq_aligned(rq->q, addr, len) || object_is_on_stack(kbuf))
+ do_copy = !blk_rq_aligned(rq->q, addr, len) || object_is_on_stack(kbuf);
+ if (do_copy)
bio = bio_copy_kern(rq, kbuf, len, gfp_mask);
else
bio = bio_map_kern(rq, kbuf, len, gfp_mask);
@@ -670,8 +672,11 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len,
return PTR_ERR(bio);
ret = blk_rq_append_bio(rq, bio);
- if (unlikely(ret))
+ if (unlikely(ret)) {
+ if (do_copy)
+ bio_free_pages(bio);
blk_mq_map_bio_put(bio);
+ }
return ret;
}
EXPORT_SYMBOL(blk_rq_map_kern);
diff --git a/block/blk.h b/block/blk.h
index fb95d3c58950..eaac05815cb0 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -717,6 +717,7 @@ static inline int req_ref_read(struct request *req)
static inline u64 blk_time_get_ns(void)
{
struct blk_plug *plug = current->plug;
+ u64 now;
if (!plug || !in_task())
return ktime_get_ns();
@@ -725,12 +726,18 @@ static inline u64 blk_time_get_ns(void)
* 0 could very well be a valid time, but rather than flag "this is
* a valid timestamp" separately, just accept that we'll do an extra
* ktime_get_ns() if we just happen to get 0 as the current time.
+ *
+ * cur_ktime can be zeroed by pre-emption the moment PF_BLOCK_TS is set.
*/
- if (!plug->cur_ktime) {
- plug->cur_ktime = ktime_get_ns();
+ now = READ_ONCE(plug->cur_ktime);
+ if (!now) {
+ now = ktime_get_ns();
+ WRITE_ONCE(plug->cur_ktime, now);
+ /* Ensure PF_BLOCK_TS is set after cur_ktime. */
+ barrier();
current->flags |= PF_BLOCK_TS;
}
- return plug->cur_ktime;
+ return now;
}
static inline ktime_t blk_time_get(void)
diff --git a/block/elevator.c b/block/elevator.c
index 3bcd37c2aa34..2161b6eea680 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -812,8 +812,13 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf,
* reference during concurrent disk deletion:
* update_nr_hwq_lock -> kn->active (via del_gendisk -> kobject_del)
* kn->active -> update_nr_hwq_lock (via this sysfs write path)
+ *
+ * Use the writer lock instead of the reader lock of update_nr_hwq_lock
+ * to serialize the two-stage elevator switch steps in
+ * elevator_change(): the core switch step under the elevator lock and
+ * the elevator_change_done() step outside the elevator lock.
*/
- if (!down_read_trylock(&set->update_nr_hwq_lock)) {
+ if (!down_write_trylock(&set->update_nr_hwq_lock)) {
ret = -EBUSY;
goto out;
}
@@ -824,7 +829,7 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf,
} else {
ret = -ENOENT;
}
- up_read(&set->update_nr_hwq_lock);
+ up_write(&set->update_nr_hwq_lock);
out:
if (ctx.type)
diff --git a/block/error-injection.c b/block/error-injection.c
index cfb83138960c..e14bc4b723ef 100644
--- a/block/error-injection.c
+++ b/block/error-injection.c
@@ -276,9 +276,10 @@ static int blk_error_injection_show(struct seq_file *s, void *private)
rcu_read_lock();
list_for_each_entry_rcu(inj, &disk->error_injection_list, entry) {
- seq_printf(s, "%llu:%llu status=%s,chance=%u",
- inj->start, inj->end,
- blk_status_to_tag(inj->status), inj->chance);
+ seq_printf(s, "%llu:%llu op=%s,status=%s,chance=%u",
+ inj->start, inj->end,
+ blk_op_str(inj->op),
+ blk_status_to_tag(inj->status), inj->chance);
seq_putc(s, '\n');
}
rcu_read_unlock();
diff --git a/block/partitions/aix.c b/block/partitions/aix.c
index f3c4174e003e..689837deba27 100644
--- a/block/partitions/aix.c
+++ b/block/partitions/aix.c
@@ -208,7 +208,14 @@ int aix_partition(struct parsed_partitions *state)
if (n) {
int foundlvs = 0;
- for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) {
+ /*
+ * The lvd array was read as a single sector; only the
+ * struct lvd entries that fit in it are valid. Bound the
+ * scan so an on-disk numlvs larger than that cannot walk
+ * the read buffer out of bounds.
+ */
+ for (i = 0; foundlvs < numlvs && i < state->limit &&
+ i < SECTOR_SIZE / (int)sizeof(struct lvd); i++) {
lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps);
if (lvip[i].pps_per_lv)
foundlvs += 1;
diff --git a/crypto/Kconfig b/crypto/Kconfig
index f1e372195273..b61401bd3ef6 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -358,8 +358,8 @@ config CRYPTO_AES
tristate "AES (Advanced Encryption Standard)"
select CRYPTO_ALGAPI
select CRYPTO_LIB_AES
- select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM
- select CRYPTO_HASH if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM
+ select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n
+ select CRYPTO_HASH if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n
help
AES cipher algorithms (Rijndael)(FIPS-197, ISO/IEC 18033-3)
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 4fc22ce4bd9a..8e3fc713891a 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -19,7 +19,6 @@
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/libata.h>
@@ -226,7 +225,6 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,
struct sata_dwc_device *hsdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
hsdev->dma = devm_kzalloc(dev, sizeof(*hsdev->dma), GFP_KERNEL);
if (!hsdev->dma)
@@ -236,11 +234,9 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,
hsdev->dma->id = pdev->id;
/* Get SATA DMA interrupt number */
- hsdev->dma->irq = irq_of_parse_and_map(np, 1);
- if (!hsdev->dma->irq) {
- dev_err(dev, "no SATA DMA irq\n");
- return -ENODEV;
- }
+ hsdev->dma->irq = platform_get_irq(pdev, 1);
+ if (hsdev->dma->irq < 0)
+ return hsdev->dma->irq;
/* Get physical SATA DMA register base address */
hsdev->dma->regs = devm_platform_ioremap_resource(pdev, 1);
@@ -398,8 +394,7 @@ static void clear_serror(struct ata_port *ap)
static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit)
{
- sata_dwc_writel(&hsdev->sata_dwc_regs->intpr,
- sata_dwc_readl(&hsdev->sata_dwc_regs->intpr));
+ sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, bit);
}
static u32 qcmd_tag_to_mask(u8 tag)
@@ -612,14 +607,9 @@ DRVSTILLBUSY:
status = ap->ops->sff_check_status(ap);
dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);
- tag = 0;
while (tag_mask) {
- while (!(tag_mask & 0x00000001)) {
- tag++;
- tag_mask <<= 1;
- }
-
- tag_mask &= (~0x00000001);
+ tag = __ffs(tag_mask);
+ tag_mask &= ~(1U << tag);
qc = ata_qc_from_tag(ap, tag);
if (unlikely(!qc)) {
dev_err(ap->dev, "failed to get qc");
@@ -1126,7 +1116,6 @@ static const struct ata_port_info sata_dwc_port_info[] = {
static int sata_dwc_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
- struct device_node *np = dev->of_node;
struct sata_dwc_device *hsdev;
u32 idr, versionr;
char *ver = (char *)&versionr;
@@ -1169,18 +1158,13 @@ static int sata_dwc_probe(struct platform_device *ofdev)
/* Save dev for later use in dev_xxx() routines */
hsdev->dev = dev;
- /* Enable SATA Interrupts */
- sata_dwc_enable_interrupts(hsdev);
-
/* Get SATA interrupt number */
- irq = irq_of_parse_and_map(np, 0);
- if (!irq) {
- dev_err(dev, "no SATA DMA irq\n");
- return -ENODEV;
- }
+ irq = platform_get_irq(ofdev, 0);
+ if (irq < 0)
+ return irq;
#ifdef CONFIG_SATA_DWC_OLD_DMA
- if (!of_property_present(np, "dmas")) {
+ if (!of_property_present(dev->of_node, "dmas")) {
err = sata_dwc_dma_init_old(ofdev, hsdev);
if (err)
return err;
@@ -1204,6 +1188,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)
if (err)
dev_err(dev, "failed to activate host");
+ /* Enable SATA Interrupts */
+ sata_dwc_enable_interrupts(hsdev);
return 0;
error_out:
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 310de0463beb..1faecef33009 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1113,6 +1113,7 @@ static void __loop_clr_fd(struct loop_device *lo)
struct queue_limits lim;
struct file *filp;
gfp_t gfp = lo->old_gfp_mask;
+ int err;
spin_lock_irq(&lo->lo_lock);
filp = lo->lo_backing_file;
@@ -1146,26 +1147,21 @@ static void __loop_clr_fd(struct loop_device *lo)
disk_force_media_change(lo->lo_disk);
- if (lo->lo_flags & LO_FLAGS_PARTSCAN) {
- int err;
-
- /*
- * open_mutex has been held already in release path, so don't
- * acquire it if this function is called in such case.
- *
- * If the reread partition isn't from release path, lo_refcnt
- * must be at least one and it can only become zero when the
- * current holder is released.
- */
- err = bdev_disk_changed(lo->lo_disk, false);
- if (err)
- pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
- __func__, lo->lo_number, err);
- /* Device is gone, no point in returning error */
- }
+ /*
+ * Remove all partitions, including partitions added manually with
+ * BLKPG, which may exist even if LO_FLAGS_PARTSCAN is not set.
+ *
+ * open_mutex has been held already in release path, so don't acquire
+ * it here.
+ */
+ err = bdev_disk_changed(lo->lo_disk, false);
+ if (err)
+ pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
+ __func__, lo->lo_number, err);
+ /* Device is gone, no point in returning error */
/*
- * lo->lo_state is set to Lo_unbound here after above partscan has
+ * lo->lo_state is set to Lo_unbound here after removing partitions has
* finished. There cannot be anybody else entering __loop_clr_fd() as
* Lo_rundown state protects us from all the other places trying to
* change the 'lo' device.
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 04ebe290bc78..10c496eaea2c 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -415,7 +415,7 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
idx = 0;
data = tlv->data;
- while (idx < length - sizeof(struct tlv_type_nvm)) {
+ while (idx + sizeof(struct tlv_type_nvm) <= length) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
tag_id = le16_to_cpu(tlv_nvm->tag_id);
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 49ecb18fea45..7f54d2d2d13a 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -797,8 +797,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
- min_size = patch_offset + patch_length;
- if (btrtl_dev->fw_len < min_size)
+ if (patch_length < sizeof(epatch_info->fw_version) ||
+ patch_offset > btrtl_dev->fw_len ||
+ patch_length > btrtl_dev->fw_len - patch_offset)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index b2d1ee3a3d11..1222f97800f4 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1087,6 +1087,10 @@ static void qca_controller_memdump(struct work_struct *work)
if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
+ qca->qca_memdump = NULL;
+ qca->memdump_state = QCA_MEMDUMP_COLLECTED;
+ clear_and_wake_up_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+ clear_bit(QCA_IBS_DISABLED, &qca->flags);
kfree_skb(skb);
mutex_unlock(&qca->hci_memdump_lock);
return;
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 507224c9ecd3..b898b6544069 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -2586,6 +2586,9 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy)
cpu = cpumask_first(policy->related_cpus);
max_freq = arch_scale_freq_ref(cpu);
+ if (!max_freq)
+ max_freq = policy->cpuinfo.max_freq;
+
capped_freq = policy->max;
/*
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 5a0eeb84d382..6e984c114d96 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -1058,12 +1058,14 @@ static void hybrid_clear_cpu_capacity(unsigned int cpunum)
static void hybrid_get_capacity_perf(struct cpudata *cpu)
{
+ u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached);
+
if (READ_ONCE(global.no_turbo)) {
- cpu->capacity_perf = cpu->pstate.max_pstate_physical;
+ cpu->capacity_perf = HWP_GUARANTEED_PERF(hwp_cap);
return;
}
- cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached));
+ cpu->capacity_perf = HWP_HIGHEST_PERF(hwp_cap);
}
static void hybrid_set_capacity_of_cpus(void)
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index bf729cde796a..5703667593a7 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -567,6 +567,9 @@ dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin,
if (!dpll_pin_available(ref_sync_pin))
continue;
ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin);
+ /* Pin may have been unregistered from this dpll already */
+ if (!ref_sync_pin_priv)
+ continue;
if (WARN_ON(!ops->ref_sync_get))
return -EOPNOTSUPP;
ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin,
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index 0f468362c288..8654b3365c9b 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -32,6 +32,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -59,7 +60,9 @@
(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))
#define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0)
-#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK)
+#define RXTX_MAP_MAX_BUFSZ_MASK GENMASK(31, 16)
+#define RXTX_MAP_MIN_BUFSZ(x) (FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x)))
+#define RXTX_MAP_MAX_BUFSZ(x) (FIELD_GET(RXTX_MAP_MAX_BUFSZ_MASK, (x)))
#define FFA_MAX_NOTIFICATIONS 64
@@ -713,30 +716,39 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
struct ffa_composite_mem_region *composite;
struct ffa_mem_region_addr_range *constituents;
struct ffa_mem_region_attributes *ep_mem_access;
- u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg);
+ u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg), ep_offset;
+ u32 emad_end, emad_size = ffa_emad_size_get(drv_info->version);
mem_region->tag = args->tag;
mem_region->flags = args->flags;
mem_region->sender_id = drv_info->vm_id;
mem_region->attributes = ffa_memory_attributes_get(func_id);
+
+ ffa_mem_region_additional_setup(drv_info->version, mem_region);
composite_offset = ffa_mem_desc_offset(buffer, args->nattrs,
drv_info->version);
+ if (composite_offset + sizeof(*composite) > max_fragsize)
+ return -ENXIO;
for (idx = 0; idx < args->nattrs; idx++) {
- ep_mem_access = buffer +
- ffa_mem_desc_offset(buffer, idx, drv_info->version);
+ ep_offset = ffa_mem_desc_offset(buffer, idx, drv_info->version);
+ if (check_add_overflow(ep_offset, emad_size, &emad_end))
+ return -ENXIO;
+
+ if (emad_end > max_fragsize)
+ return -ENXIO;
+
+ ep_mem_access = buffer + ep_offset;
+ memset(ep_mem_access, 0, emad_size);
ep_mem_access->receiver = args->attrs[idx].receiver;
ep_mem_access->attrs = args->attrs[idx].attrs;
ep_mem_access->composite_off = composite_offset;
- ep_mem_access->flag = 0;
- ep_mem_access->reserved = 0;
ffa_emad_impdef_value_init(drv_info->version,
ep_mem_access->impdef_val,
args->attrs[idx].impdef_val);
}
mem_region->handle = 0;
mem_region->ep_count = args->nattrs;
- ffa_mem_region_additional_setup(drv_info->version, mem_region);
composite = buffer + composite_offset;
composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
@@ -769,7 +781,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
constituents = buffer;
}
- if ((void *)constituents - buffer > max_fragsize) {
+ if ((void *)constituents + sizeof(*constituents) - buffer > max_fragsize) {
pr_err("Memory Region Fragment > Tx Buffer size\n");
return -EFAULT;
}
@@ -778,7 +790,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE;
constituents->reserved = 0;
constituents++;
- frag_len += sizeof(struct ffa_mem_region_addr_range);
+ frag_len += sizeof(*constituents);
} while ((args->sg = sg_next(args->sg)));
return ffa_transmit_fragment(func_id, addr, buf_sz, frag_len,
@@ -1139,7 +1151,7 @@ static int ffa_partition_info_get(const char *uuid_str,
uuid_t uuid;
struct ffa_partition_info *pbuf;
- if (uuid_parse(uuid_str, &uuid)) {
+ if (!uuid_str || uuid_parse(uuid_str, &uuid)) {
pr_err("invalid uuid (%s)\n", uuid_str);
return -ENODEV;
}
@@ -2101,7 +2113,7 @@ static int ffa_probe(struct platform_device *pdev)
{
int ret;
u32 buf_sz;
- size_t rxtx_bufsz = SZ_4K;
+ size_t rxtx_min_bufsz = SZ_4K, rxtx_max_bufsz = 0, rxtx_bufsz;
if (IS_BUILTIN(CONFIG_ARM_FFA_TRANSPORT) &&
is_protected_kvm_enabled() && !is_pkvm_initialized())
@@ -2132,15 +2144,18 @@ static int ffa_probe(struct platform_device *pdev)
ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL);
if (!ret) {
if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1)
- rxtx_bufsz = SZ_64K;
+ rxtx_min_bufsz = SZ_64K;
else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2)
- rxtx_bufsz = SZ_16K;
+ rxtx_min_bufsz = SZ_16K;
else
- rxtx_bufsz = SZ_4K;
+ rxtx_min_bufsz = SZ_4K;
+
+ rxtx_max_bufsz = RXTX_MAP_MAX_BUFSZ(buf_sz) * SZ_4K;
+ if (rxtx_max_bufsz != 0 && rxtx_max_bufsz < rxtx_min_bufsz)
+ rxtx_max_bufsz = rxtx_min_bufsz;
}
- rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz);
- drv_info->rxtx_bufsz = rxtx_bufsz;
+ rxtx_bufsz = min_not_zero(PAGE_ALIGN(rxtx_min_bufsz), rxtx_max_bufsz);
drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
if (!drv_info->rx_buffer) {
ret = -ENOMEM;
@@ -2156,10 +2171,17 @@ static int ffa_probe(struct platform_device *pdev)
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
virt_to_phys(drv_info->rx_buffer),
rxtx_bufsz / FFA_PAGE_SIZE);
+ if (ret == -EINVAL && !rxtx_max_bufsz && rxtx_min_bufsz < rxtx_bufsz) {
+ rxtx_bufsz = rxtx_min_bufsz;
+ ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
+ virt_to_phys(drv_info->rx_buffer),
+ rxtx_bufsz / FFA_PAGE_SIZE);
+ }
if (ret) {
pr_err("failed to register FFA RxTx buffers\n");
goto free_pages;
}
+ drv_info->rxtx_bufsz = rxtx_bufsz;
mutex_init(&drv_info->rx_lock);
mutex_init(&drv_info->tx_lock);
diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index e3fb36825978..783c24a20e29 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -96,7 +96,7 @@ config ARM_SCMI_POWER_CONTROL
firmware.
This driver can also be built as a module. If so, the module will be
- called scmi_power_control. Note this may needed early in boot to catch
- early shutdown/reboot SCMI requests.
+ called scmi_power_control. Note this may be needed early in boot to
+ catch early shutdown/reboot SCMI requests.
endmenu
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 42e666a628c7..0278705d809e 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -718,7 +718,7 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
u32 clk_id, unsigned long *rate)
{
- u64 fmin, fmax, ftmp;
+ u64 fmin, fmax, ftmp, step;
struct scmi_clock_info *clk;
struct scmi_clock_desc *clkd;
struct clock_info *ci = ph->get_priv(ph);
@@ -749,11 +749,14 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
return 0;
}
+ step = clkd->r.rates[RATE_STEP];
+ if (!step)
+ return -EINVAL;
+
ftmp = *rate - fmin;
- ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */
- ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]);
+ ftmp = DIV64_U64_ROUND_UP(ftmp, step);
- *rate = ftmp * clkd->r.rates[RATE_STEP] + fmin;
+ *rate = ftmp * step + fmin;
return 0;
}
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 40ec184eedae..0a192cf2deab 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
return -EINVAL;
}
if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) {
- dev_warn(handle->dev,
- "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
- proto_id, evt_id, ktime_to_ns(ts));
+ dev_warn_ratelimited(handle->dev,
+ "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
+ proto_id, evt_id, ktime_to_ns(ts));
return -ENOMEM;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
index aa039e148a5e..8525c45ab209 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
@@ -372,19 +372,59 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev)
}
#ifdef CONFIG_ACPI
+/**
+ * amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device
+ * @adev: AMDGPU device
+ * @vhdr: VFCT image header to check
+ *
+ * VFCT entries contain the PCI bus number as recorded during BIOS POST.
+ * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or
+ * resource conflicts), the runtime bus number may differ from the POST
+ * value. Match by device identity (vendor + device + function) and use
+ * the bus number as a preference: exact bus match is preferred, but when
+ * the bus numbers disagree we accept the entry if the device identity
+ * matches.
+ *
+ * Returns: 0 on match, -ENODEV on no match
+ */
+static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev,
+ VFCT_IMAGE_HEADER *vhdr)
+{
+ /* Vendor and device IDs must always match */
+ if (vhdr->VendorID != adev->pdev->vendor ||
+ vhdr->DeviceID != adev->pdev->device)
+ return -ENODEV;
+
+ if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) ||
+ vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn))
+ return -ENODEV;
+
+ /* Exact bus number match - preferred */
+ if (vhdr->PCIBus == adev->pdev->bus->number)
+ return 0;
+
+ /* Bus mismatch but device identity matches (PCI renumbering case) */
+ dev_notice(adev->dev,
+ "VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n",
+ vhdr->PCIBus, adev->pdev->bus->number,
+ adev->pdev->vendor, adev->pdev->device);
+ return 0;
+}
+
static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
{
struct acpi_table_header *hdr;
acpi_size tbl_size;
UEFI_ACPI_VFCT *vfct;
unsigned int offset;
+ bool r = false;
if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
return false;
tbl_size = hdr->length;
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n");
- return false;
+ goto out;
}
vfct = (UEFI_ACPI_VFCT *)hdr;
@@ -397,36 +437,36 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
offset += sizeof(VFCT_IMAGE_HEADER);
if (offset > tbl_size) {
dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n");
- return false;
+ goto out;
}
offset += vhdr->ImageLength;
if (offset > tbl_size) {
dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n");
- return false;
+ goto out;
}
if (vhdr->ImageLength &&
- vhdr->PCIBus == adev->pdev->bus->number &&
- vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) &&
- vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) &&
- vhdr->VendorID == adev->pdev->vendor &&
- vhdr->DeviceID == adev->pdev->device) {
+ !amdgpu_acpi_vfct_match(adev, vhdr)) {
adev->bios = kmemdup(&vbios->VbiosContent,
vhdr->ImageLength,
GFP_KERNEL);
if (!check_atom_bios(adev, vhdr->ImageLength)) {
amdgpu_bios_release(adev);
- return false;
+ goto out;
}
adev->bios_size = vhdr->ImageLength;
- return true;
+ r = true;
+ goto out;
}
}
dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n");
- return false;
+
+out:
+ acpi_put_table(hdr);
+ return r;
}
#else
static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index e77db76b48b8..6480a344006d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -234,6 +234,9 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p,
drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i,
coredump->ibs[i].gpu_addr,
coredump->ibs[i].ib_size_dw);
+
+ for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++)
+ drm_printf(p, "0xffffffff\n");
}
return;
}
@@ -355,10 +358,14 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
drm_printf(&p, "kernel: %s\n", init_utsname()->release);
drm_printf(&p, "module: " KBUILD_MODNAME "\n");
drm_printf(&p, "time: %ptSp\n", &coredump->reset_time);
+ drm_printf(&p, "pasid: %u\n", coredump->pasid);
+ drm_printf(&p, "vmid: %u\n", coredump->vmid);
if (coredump->reset_task_info.task.pid)
- drm_printf(&p, "process_name: %s PID: %d\n",
+ drm_printf(&p, "process_name: %s TGID: %d thread: %s PID: %d\n",
coredump->reset_task_info.process_name,
+ coredump->reset_task_info.tgid,
+ coredump->reset_task_info.task.comm,
coredump->reset_task_info.task.pid);
/* SOC Information */
@@ -562,6 +569,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
amdgpu_vm_put_task_info(ti);
}
coredump->pasid = job->pasid;
+ coredump->vmid = job->vmid;
coredump->num_ibs = job->num_ibs;
for (i = 0; i < job->num_ibs; ++i) {
coredump->ibs[i].gpu_addr = job->ibs[i].gpu_addr;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
index 2371e20fc68b..63f27337c09a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
@@ -63,6 +63,7 @@ struct amdgpu_coredump_info {
char *formatted;
unsigned int pasid;
+ unsigned int vmid;
int num_ibs;
struct amdgpu_coredump_ib_info ibs[] __counted_by(num_ibs);
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 53335ca96b1d..e5f26e5892ba 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1323,6 +1323,15 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device
if (c->x86_vendor == X86_VENDOR_INTEL)
return false;
+
+ /*
+ * AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't
+ * support PCIe dynamic speed switching.
+ * https://gitlab.freedesktop.org/drm/amd/-/work_items/5436
+ */
+ if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 &&
+ c->x86_model == 0x08)
+ return false;
#endif
return true;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 7b9bb998906d..2860f12915c0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -3137,9 +3137,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev)
case IP_VERSION(11, 5, 3):
case IP_VERSION(11, 5, 4):
case IP_VERSION(11, 5, 6):
+ adev->family = AMDGPU_FAMILY_GC_11_5_0;
+ break;
case IP_VERSION(11, 7, 0):
case IP_VERSION(11, 7, 1):
- adev->family = AMDGPU_FAMILY_GC_11_5_0;
+ adev->family = AMDGPU_FAMILY_GC_11_5_4;
break;
case IP_VERSION(12, 0, 0):
case IP_VERSION(12, 0, 1):
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index f98bfba59a2c..718937777e53 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -276,10 +276,12 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
goto error_free;
}
- r = amdgpu_bo_pin(*bo_ptr, domain);
- if (r) {
- dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
- goto error_unreserve;
+ if (free) {
+ r = amdgpu_bo_pin(*bo_ptr, domain);
+ if (r) {
+ dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
+ goto error_unreserve;
+ }
}
r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo);
@@ -302,7 +304,8 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
return 0;
error_unpin:
- amdgpu_bo_unpin(*bo_ptr);
+ if (free)
+ amdgpu_bo_unpin(*bo_ptr);
error_unreserve:
amdgpu_bo_unreserve(*bo_ptr);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 025625e7e800..eb8bbfc7e6d9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2684,12 +2684,22 @@ void amdgpu_sdma_set_buffer_funcs_scheds(struct amdgpu_device *adev,
return;
}
- /* Navi1x's workaround requires us to limit to a single SDMA sched
- * for ttm.
- */
hub = &adev->vmhub[AMDGPU_GFXHUB(0)];
- adev->mman.num_buffer_funcs_scheds = hub->sdma_invalidation_workaround ?
- 1 : n;
+
+ /*
+ * Allow using multiple SDMA schedulers only on GPUs where
+ * we are allowed to do concurrent VM flushes.
+ * This consideration is necessary because all GART windows
+ * are mapped in VMID 0 (the kernel VMID) so each buffer
+ * entity would flush VMID 0 concurrently.
+ *
+ * Also consider the SDMA invalidation workaround on
+ * Navi 1x GPUs, which also prevents us from using
+ * multiple SDMA engines on VMID 0 at the same time.
+ */
+ adev->mman.num_buffer_funcs_scheds =
+ (adev->vm_manager.concurrent_flush &&
+ !hub->sdma_invalidation_workaround) ? n : 1;
}
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index d854343b3734..572f2949cb64 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -1376,16 +1376,19 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
/* TODO: We probably need a new lock for the queue state */
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
- if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
- continue;
-
- userq_funcs = adev->userq_funcs[queue->queue_type];
- userq_funcs->unmap(queue);
- /* just mark all queues as hung at this point.
- * if unmap succeeds, we could map again
- * in amdgpu_userq_post_reset() if vram is not lost
+ if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ userq_funcs = adev->userq_funcs[queue->queue_type];
+ userq_funcs->unmap(queue);
+ /* just mark all queues as hung at this point.
+ * if unmap succeeds, we could map again
+ * in amdgpu_userq_post_reset() if vram is not lost
+ */
+ queue->state = AMDGPU_USERQ_STATE_HUNG;
+ }
+ /* Force-complete any pending fence regardless of queue state so
+ * that eviction/suspend and queue teardown waiters don't block
+ * forever on a fence that will never signal after the reset.
*/
- queue->state = AMDGPU_USERQ_STATE_HUNG;
amdgpu_userq_fence_driver_force_completion(queue);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index bb99b7c3a010..f224d33c4bc4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -855,12 +855,10 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job,
job->oa_size);
}
- if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) {
- amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
- fence = &job->hw_vm_fence->base;
- /* get a ref for the job */
- dma_fence_get(fence);
- }
+ amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
+ fence = &job->hw_vm_fence->base;
+ /* get a ref for the job */
+ dma_fence_get(fence);
if (vm_flush_needed) {
mutex_lock(&id_mgr->lock);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 678ec611a4f2..67137679a901 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -3103,6 +3103,7 @@ static void deallocate_hiq_sdma_mqd(struct kfd_node *dev,
struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
{
struct device_queue_manager *dqm;
+ int i;
pr_debug("Loading device queue manager\n");
@@ -3231,6 +3232,9 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
deallocate_hiq_sdma_mqd(dev, &dqm->hiq_sdma_mqd);
out_free:
+ for (i = 0; i < KFD_MQD_TYPE_MAX; i++)
+ kfree(dqm->mqd_mgrs[i]);
+
kfree(dqm);
return NULL;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index 28354a4e5dd5..98a5512b701b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -23,6 +23,7 @@
*/
#include <linux/slab.h>
+#include <linux/overflow.h>
#include "kfd_priv.h"
#include "kfd_topology.h"
#include "kfd_svm.h"
@@ -235,7 +236,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
struct kfd_topology_device *topo_dev;
u64 expected_queue_size;
struct amdgpu_vm *vm;
- u32 total_cwsr_size;
+ u64 total_cwsr_size;
int err;
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
@@ -308,8 +309,14 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
goto out_err_unreserve;
}
- total_cwsr_size = (properties->ctx_save_restore_area_size +
- topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size;
+ if (check_mul_overflow(total_cwsr_size,
+ NUM_XCC(pdd->dev->xcc_mask),
+ &total_cwsr_size)) {
+ err = -EINVAL;
+ goto out_err_unreserve;
+ }
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address,
@@ -344,7 +351,7 @@ out_err_release:
int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
{
struct kfd_topology_device *topo_dev;
- u32 total_cwsr_size;
+ u64 total_cwsr_size;
kfd_queue_buffer_put(&properties->wptr_bo);
kfd_queue_buffer_put(&properties->rptr_bo);
@@ -355,8 +362,12 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
if (!topo_dev)
return -EINVAL;
- total_cwsr_size = (properties->ctx_save_restore_area_size +
- topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size;
+ if (check_mul_overflow(total_cwsr_size,
+ NUM_XCC(pdd->dev->xcc_mask),
+ &total_cwsr_size))
+ return -EINVAL;
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 18145d78334f..9c564cd5edee 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -580,89 +580,25 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
queue_work(system_percpu_wq, &offload_work->work);
}
-static void dm_vupdate_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
-
- if (acrtc) {
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
- }
-}
-
/**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
+ * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
+ * @adev: amdgpu device
+ * @acrtc: the CRTC to service
*
- * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
- * event handler.
+ * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
+ * updates and pageflip completion delivery.
+ *
+ * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
+ * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
+ * from dm_crtc_high_irq().
*/
-static void dm_crtc_high_irq(void *interrupt_params)
+static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
+ struct amdgpu_crtc *acrtc)
{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
struct drm_writeback_job *job;
- struct amdgpu_crtc *acrtc;
unsigned long flags;
int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
+ bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
if (acrtc->wb_conn) {
spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
@@ -699,12 +635,17 @@ static void dm_crtc_high_irq(void *interrupt_params)
vrr_active, acrtc->dm_irq_params.active_planes);
/**
- * Core vblank handling at start of front-porch is only possible
- * in non-vrr mode, as only there vblank timestamping will give
- * valid results while done in front-porch. Otherwise defer it
- * to dm_vupdate_high_irq after end of front-porch.
+ * Core vblank handling.
+ *
+ * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
+ * point, which is the correct place to timestamp both VRR and non-VRR
+ * vblanks.
+ *
+ * On DCE this handler runs at the start of front-porch, where only
+ * non-VRR timestamping is valid; VRR vblank is deferred to
+ * dm_vupdate_high_irq() after end of front-porch.
*/
- if (!vrr_active)
+ if (is_dcn || !vrr_active)
amdgpu_dm_crtc_handle_vblank(acrtc);
/**
@@ -737,18 +678,33 @@ static void dm_crtc_high_irq(void *interrupt_params)
}
/*
- * If there aren't any active_planes then DCH HUBP may be clock-gated.
- * In that case, pageflip completion interrupts won't fire and pageflip
- * completion events won't get delivered. Prevent this by sending
- * pending pageflip events from here if a flip is still pending.
+ * Deliver pageflip completion events (DCN only).
*
- * If any planes are enabled, use dm_pflip_high_irq() instead, to
- * avoid race conditions between flip programming and completion,
- * which could cause too early flip completion events.
+ * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
+ * point. Deliver any pending pageflip completion event from here,
+ * once HW has consumed the new address (the OTG no longer reports a
+ * pending flip).
+ *
+ * Also handle the case here where there aren't any active planes and
+ * DCN HUBP may be clock-gated, so the flip-pending status may be
+ * undefined.
*/
- if (adev->family >= AMDGPU_FAMILY_RV &&
- acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->dm_irq_params.active_planes == 0) {
+ if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc->event) {
+
+ if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
+ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
+ acrtc->event = NULL;
+ drm_crtc_vblank_put(&acrtc->base);
+ acrtc->pflip_status = AMDGPU_FLIP_NONE;
+ }
+ /*
+ * If the flip is still pending, leave it armed and
+ * retry on the next vupdate.
+ */
+ } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc->dm_irq_params.active_planes == 0) {
+
if (acrtc->event) {
drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
acrtc->event = NULL;
@@ -760,6 +716,104 @@ static void dm_crtc_high_irq(void *interrupt_params)
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}
+static void dm_vupdate_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+ struct drm_device *drm_dev;
+ struct drm_vblank_crtc *vblank;
+ ktime_t frame_duration_ns, previous_timestamp;
+ unsigned long flags;
+ int vrr_active;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
+ if (!acrtc)
+ return;
+
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+ drm_dev = acrtc->base.dev;
+ vblank = drm_crtc_vblank_crtc(&acrtc->base);
+ previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
+ frame_duration_ns = vblank->time - previous_timestamp;
+
+ if (frame_duration_ns > 0) {
+ trace_amdgpu_refresh_rate_track(acrtc->base.index,
+ frame_duration_ns,
+ ktime_divns(NSEC_PER_SEC, frame_duration_ns));
+ atomic64_set(&irq_params->previous_timestamp, vblank->time);
+ }
+
+ drm_dbg_vbl(drm_dev,
+ "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
+ vrr_active);
+
+ /*
+ * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
+ * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
+ * not used. Run the full handler here.
+ */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
+ dm_crtc_high_irq_handler(adev, acrtc);
+ return;
+ }
+
+ /* DCE only below. */
+
+ /* Core vblank handling is done here after end of front-porch in
+ * vrr mode, as vblank timestamping will give valid results
+ * while now done after front-porch. This will also deliver
+ * page-flip completion events that have been queued to us
+ * if a pageflip happened inside front-porch.
+ */
+ if (vrr_active && acrtc->dm_irq_params.stream) {
+ bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+ bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+ bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
+ == VRR_STATE_ACTIVE_VARIABLE;
+
+ amdgpu_dm_crtc_handle_vblank(acrtc);
+
+ /* BTR processing for pre-DCE12 ASICs */
+ if (adev->family < AMDGPU_FAMILY_AI) {
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+ mod_freesync_handle_v_update(
+ adev->dm.freesync_module,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params);
+
+ if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+ schedule_dc_vmin_vmax(adev,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params.adjust);
+ }
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+ }
+ }
+}
+
+/**
+ * dm_crtc_high_irq() - Handles CRTC interrupt
+ * @interrupt_params: used for determining the CRTC instance
+ *
+ * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
+ *
+ * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
+ * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
+ */
+static void dm_crtc_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
+ if (!acrtc)
+ return;
+
+ dm_crtc_high_irq_handler(adev, acrtc);
+}
+
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/**
* dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
@@ -3298,6 +3352,13 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
*/
if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
+
+ } else if (acrtc && state->stream_status[i].plane_count != 0) {
+ /* DCN only needs to toggle VUPDATE_NO_LOCK */
+ rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable);
+ if (rc)
+ drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n",
+ enable ? "en" : "dis");
}
}
@@ -4069,6 +4130,8 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
caps->aux_support = false;
+ panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
+
if (caps->ext_caps->bits.oled == 1
/*
* ||
@@ -4081,6 +4144,9 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
caps->aux_support = false;
else if (amdgpu_backlight == 1)
caps->aux_support = true;
+ else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
+ panel_backlight_quirk->force_pwm)
+ caps->aux_support = false;
if (caps->aux_support)
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
@@ -4096,8 +4162,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
else
caps->aux_min_input_signal = 1;
- panel_backlight_quirk =
- drm_get_panel_backlight_quirk(aconnector->drm_edid);
if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
if (panel_backlight_quirk->min_brightness) {
caps->min_input_signal =
@@ -4863,38 +4927,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
* for acknowledging and handling.
*/
- /* Use VSTARTUP interrupt */
- for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
- i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
/* Use otg vertical line interrupt */
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
@@ -4966,37 +4998,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
return -ENOMEM;
}
- /* Use GRPH_PFLIP interrupt */
- for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
- i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
/* HPD */
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
&adev->hpd_irq);
@@ -5563,11 +5564,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
caps = &dm->backlight_caps[aconnector->bl_idx];
if (get_brightness_range(caps, &min, &max)) {
if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
else
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
/* min is zero, so max needs to be adjusted */
- props.max_brightness = max - min;
+ props.max_brightness = max;
drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
caps->ac_level, caps->dc_level);
} else
@@ -9674,21 +9675,9 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
if (acrtc_state) {
timing = &acrtc_state->stream->timing;
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >=
- IP_VERSION(3, 2, 0) &&
- !(adev->flags & AMD_IS_APU)) {
- /*
- * DGPUs NV3x and newer that support idle optimizations
- * experience intermittent flip-done timeouts on cursor
- * updates. Restore 5s offdelay behavior for now.
- *
- * Discussion on the issue:
- * https://lore.kernel.org/amd-gfx/20260217191632.1243826-1-sysdadmin@m1k.cloud/
- */
- config.offdelay_ms = 5000;
- config.disable_immediate = false;
- } else if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
- IP_VERSION(3, 5, 0)) {
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
+ IP_VERSION(3, 5, 0) ||
+ !(adev->flags & AMD_IS_APU)) {
/*
* Older HW and DGPU have issues with instant off;
* use a 2 frame offdelay.
@@ -9707,14 +9696,22 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
drm_crtc_vblank_on_config(&acrtc->base,
&config);
- /* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/
+ /*
+ * Since pflip_high_irq is no longer registered for DCN, grab an
+ * extra reference to vupdate irq instead to workaround this
+ * issue:
+ * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936
+ *
+ * The callbacks to drm_vblank_on/off should really take care of
+ * this though.
+ */
switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
case IP_VERSION(3, 0, 0):
case IP_VERSION(3, 0, 2):
case IP_VERSION(3, 0, 3):
case IP_VERSION(3, 2, 0):
- if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n");
+ if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n");
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n");
@@ -9732,8 +9729,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n");
#endif
- if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n");
+ if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n");
}
drm_crtc_vblank_off(&acrtc->base);
@@ -9746,6 +9743,10 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
int irq_type =
amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
+ /* GRPH_PFLIP is not used on DCN; nothing to reapply. */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0)
+ return;
+
/**
* This reads the current state for the IRQ and force reapplies
* the setting to hardware.
@@ -10078,9 +10079,13 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
+ struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
+ /* Only DCE gates vupdate on VRR, keep it enabled for DCN */
+ bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
+
if (!old_vrr_active && new_vrr_active) {
/* Transition VRR inactive -> active:
* While VRR is active, we must not disable vblank irq, as a
@@ -10090,7 +10095,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
* We also need vupdate irq for the actual core vblank handling
* at end of vblank.
*/
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
+ if (vrr_gates_vupdate)
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -10106,7 +10112,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
/* Transition VRR active -> inactive:
* Allow vblank irq disable again for fixed refresh rate.
*/
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
+ if (vrr_gates_vupdate)
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
drm_crtc_vblank_put(new_state->base.crtc);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -10255,6 +10262,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
}
}
+static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
+ struct dm_crtc_state *acrtc_state,
+ bool pflip_update,
+ bool cursor_update)
+{
+ assert_spin_locked(&acrtc->base.dev->event_lock);
+
+ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
+ return;
+
+ if (pflip_update) {
+ drm_crtc_vblank_get(&acrtc->base);
+ WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
+ /* Arm flip completion handling and event delivery after programming. */
+ prepare_flip_isr(acrtc);
+ } else if (cursor_update) {
+ drm_crtc_vblank_get(&acrtc->base);
+ acrtc->event = acrtc->base.state->event;
+ acrtc->base.state->event = NULL;
+ }
+}
+
static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
struct drm_device *dev,
struct amdgpu_display_manager *dm,
@@ -10277,6 +10306,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
bool cursor_update = false;
bool pflip_present = false;
+ bool immediate_flip = false;
+ bool flip_latched_during_prog = false;
bool dirty_rects_changed = false;
bool updated_planes_and_streams = false;
struct {
@@ -10441,6 +10472,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
acrtc_state->update_type == UPDATE_TYPE_FAST &&
get_mem_type(old_plane_state->fb) == get_mem_type(fb);
+ immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate;
+
timestamp_ns = ktime_get_ns();
bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
@@ -10509,39 +10542,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
usleep_range(1000, 1100);
}
- /**
- * Prepare the flip event for the pageflip interrupt to handle.
- *
- * This only works in the case where we've already turned on the
- * appropriate hardware blocks (eg. HUBP) so in the transition case
- * from 0 -> n planes we have to skip a hardware generated event
- * and rely on sending it from software.
- */
- if (acrtc_attach->base.state->event &&
- acrtc_state->active_planes > 0) {
- drm_crtc_vblank_get(pcrtc);
-
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
-
- WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
- prepare_flip_isr(acrtc_attach);
-
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
if (acrtc_state->stream) {
if (acrtc_state->freesync_vrr_info_changed)
bundle->stream_update.vrr_infopacket =
&acrtc_state->stream->vrr_infopacket;
}
- } else if (cursor_update && acrtc_state->active_planes > 0) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- if (acrtc_attach->base.state->event) {
- drm_crtc_vblank_get(pcrtc);
- acrtc_attach->event = acrtc_attach->base.state->event;
- acrtc_attach->base.state->event = NULL;
+ }
+
+ /*
+ * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
+ * event delivery. Only GRPH_FLIP handler can send pflip events, and it
+ * only fires if HW latched to the flip. Maintain legacy behavior by
+ * arming event before programming.
+ */
+ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
+ dm_arm_vblank_event(acrtc_attach, acrtc_state,
+ pflip_present, cursor_update);
}
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
}
/* Update the planes if changed or disable if we don't have any. */
@@ -10633,6 +10651,115 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
amdgpu_dm_commit_cursors(state);
+ /*
+ * DCN specific vblank handling
+ * ============================
+ *
+ * With the event_lock held, arm the vblank event, and determine whether
+ * deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch
+ * point) handler. Do this *after* programming so that the IRQ handler
+ * will not deliver the event before HW laches onto the programmed
+ * values:
+ *
+ * Commit thread IRQ handler HW
+ * -----------------------------------------------------------------
+ * arm_vblank_event()
+ * vupdate()
+ * vupdate_handler()
+ * cook_timestamp()
+ * # prev flip already latched,
+ * # so flip_latched == true.
+ * if event_armed && flip_latched:
+ * send_vblank_event()
+ * # sent before latch, **BAD!**
+ * hw_program()
+ * vupdate()
+ * **latch**
+ *
+ * There's a consequence of arming after: it's possible for HW to latch
+ * between start of HW programming and acrtc->event/pflip_status arming.
+ * When this happens, the IRQ handler will send the event on the next
+ * immediate latch point, even though HW has already latched. This is
+ * handled by optimistically checking for HW latch after programming,
+ * and if latched, send the event immediately:
+ *
+ * Commit thread IRQ handler HW
+ * -----------------------------------------------------------------
+ * hw_program()
+ * vupdate()
+ * **latch**
+ * vupdate_handler()
+ * cook_timestamp()
+ * # event_armed == false
+ * # **no event sent!**
+ * arm_vblank_event()
+ * if flip_latched:
+ * **send_vblank_event()**
+ * disarm_vblank_event()
+ *
+ * The IRQ handler is expected to cook the timestamp, but we need to
+ * cook the timestamp before optimistic sending as well. That's because
+ * the following sequence is possible:
+ *
+ * Commit thread IRQ handler HW
+ * -----------------------------------------------------------------
+ * hw_program()
+ * arm_vblank_event()
+ * vupdate()
+ * **latch**
+ * if flip_latched:
+ * # Need cook before send!
+ * **cook_timestamp()**
+ * send_vblank_event()
+ * disarm_vblank_event()
+ * vupdate_handler()
+ * cook_timestamp()
+ * # event_armed == false
+ * # no event sent!
+ *
+ * Cooking twice is OK, since DRM scanout accurate timestamps report A)
+ * the previous vactive start if currently in vactive, or B) the next
+ * vactive start if currently in vblank (see &get_vblank_counter). 'A)'
+ * is what we want for the optimistic send, and for 'B)', we'll cook a
+ * timestamp no later than the next IRQ handler run.
+ *
+ * The more correct fix is to wrap programming and arming with the
+ * event_lock and thus serializing it with the IRQ handler. However,
+ * there are various sleep-waits within
+ * update_planes_and_stream_adapter() that makes spin locking illegal.
+ * And on full updates, it can take 1-2 frame-times to return (see
+ * commit_planes_for_stream).
+ *
+ * On DCE, GRPH_PFLIP IRQ is used and takes care of this.
+ */
+ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
+ spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+
+ if (updated_planes_and_streams) {
+ flip_latched_during_prog =
+ !dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst);
+ }
+
+ dm_arm_vblank_event(acrtc_attach, acrtc_state,
+ pflip_present, cursor_update);
+
+ /*
+ * Deliver the event immediately on immediate flip, or on a
+ * update that has already latched.
+ */
+ if ((immediate_flip || flip_latched_during_prog) &&
+ acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc_attach->event) {
+ drm_crtc_accurate_vblank_count(&acrtc_attach->base);
+ drm_crtc_send_vblank_event(&acrtc_attach->base,
+ acrtc_attach->event);
+ acrtc_attach->event = NULL;
+ drm_crtc_vblank_put(&acrtc_attach->base);
+ acrtc_attach->pflip_status = AMDGPU_FLIP_NONE;
+ }
+ spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+ }
+
cleanup:
kfree(bundle);
}
@@ -12098,6 +12225,7 @@ skip_modeset:
/* Release extra reference */
if (new_stream)
dc_stream_release(new_stream);
+ new_stream = NULL;
/*
* We want to do dc stream updates that do not require a
@@ -12814,10 +12942,15 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
/* Overlay cursor not supported on HW before DCN
* DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
* as previous DCN generations, so enable native mode on DCN401/420
+ *
+ * Always set native cursor mode when the CRTC is disabled,
+ * to make sure it doesn't cause atomic commits to fail when
+ * they are trying to disable the CRTC.
*/
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) ||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) ||
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1)) {
+ amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
+ !dm_crtc_state->base.enable) {
*cursor_mode = DM_CURSOR_NATIVE_MODE;
return 0;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 3dcedaa67ed8..b43cd68cde67 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -274,7 +274,14 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
drm_crtc_vblank_restore(crtc);
}
- if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
+ /*
+ * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
+ * vblank and pageflip completion events, so enable it whenever vblank
+ * is enabled. On DCE, vupdate is only needed in VRR mode.
+ */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
+ rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
+ } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
if (enable) {
/* vblank irq on -> Only need vupdate irq in vrr mode */
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
@@ -288,36 +295,43 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
if (rc)
return rc;
- /* crtc vblank or vstartup interrupt */
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
- }
-
- if (rc)
- return rc;
-
/*
- * hubp surface flip interrupt
- *
- * We have no guarantee that the frontend index maps to the same
- * backend index - some even map to more than one.
- *
- * TODO: Use a different interrupt or check DC itself for the mapping.
+ * VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on
+ * DCE. On DCN, vblank and pageflip completion are delivered from
+ * VUPDATE_NO_LOCK (enabled above), so don't touch them here.
*/
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
- }
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
+ /* crtc vblank or vstartup interrupt */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
+ }
- if (rc)
- return rc;
+ if (rc)
+ return rc;
+
+ /*
+ * hubp surface flip interrupt
+ *
+ * We have no guarantee that the frontend index maps to the same
+ * backend index - some even map to more than one.
+ *
+ * TODO: Use a different interrupt or check DC itself for the mapping.
+ */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
+ }
+
+ if (rc)
+ return rc;
+ }
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/* crtc vline0 interrupt, only available on DCN+ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index c6f94eb71ffa..6be7f6edd0b2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -1201,11 +1201,25 @@ enum dc_edid_status dm_helpers_read_local_edid(
continue;
edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
- if (!edid ||
- edid->extensions >= sizeof(sink->dc_edid.raw_edid) / EDID_LENGTH)
+ /*
+ * Use the length of the EDID property blob populated by
+ * drm_edid_connector_update() above. It reflects the true number
+ * of EDID blocks, including any HDMI Forum EDID Extension Override
+ * Data Block (HF-EEODB) count, which the raw byte 0x7e extension
+ * count can hide (e.g. HDMI 8K sinks).
+ */
+ if (!edid || !connector->edid_blob_ptr ||
+ connector->edid_blob_ptr->length > sizeof(sink->dc_edid.raw_edid))
return EDID_BAD_INPUT;
- sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1);
+ /*
+ * FIXME: amdgpu_dm today does not consider the HF-EEODB, which
+ * may contain additional mode info for sinks. This is a
+ * workaround until dc_edid is refactored out from DC into
+ * amdgpu_dm's ownership, allowing amdgpu_dm to use drm_edid
+ * directly
+ */
+ sink->dc_edid.length = connector->edid_blob_ptr->length;
memmove(sink->dc_edid.raw_edid, (uint8_t *)edid, sink->dc_edid.length);
/* We don't need the original edid anymore */
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 175106cce5a4..e25b94b65dac 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -6165,6 +6165,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
dal_irq_service_ack(dc->res_pool->irqs, src);
}
+/*
+ * dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG
+ *
+ * @dc: display core context @otg_inst: OTG instance to query
+ *
+ * Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst,
+ * returning true if any of them has not yet latched its programmed surface
+ * address.
+ *
+ * Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state,
+ * so it is safe to call from interrupt context without racing a concurrent
+ * commit that may be updating plane state.
+ *
+ * Return: true if a flip is still pending on the OTG, false otherwise.
+ */
+bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst)
+{
+ bool flip_pending = false;
+ int i;
+
+ if (!dc || !dc->current_state)
+ return false;
+
+ dc_exit_ips_for_hw_access(dc);
+
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+ struct hubp *hubp = pipe_ctx->plane_res.hubp;
+
+ if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg)
+ continue;
+
+ if (pipe_ctx->stream_res.tg->inst != otg_inst)
+ continue;
+
+ if (hubp && hubp->funcs->hubp_is_flip_pending &&
+ hubp->funcs->hubp_is_flip_pending(hubp)) {
+ flip_pending = true;
+ break;
+ }
+ }
+
+ return flip_pending;
+}
+
void dc_power_down_on_boot(struct dc *dc)
{
if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW &&
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 82d02ebbd829..1dc85f6b6689 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -1815,6 +1815,8 @@ struct dc_scratch_space {
bool dp_skip_DID2;
bool dp_skip_reset_segment;
bool dp_skip_fs_144hz;
+ /* Some DP bridges don't work with RBR and must use HBR. */
+ bool dp_skip_rbr;
bool dp_mot_reset_segment;
/* Some USB4 docks do not handle turning off MST DSC once it has been enabled. */
bool dpia_mst_dsc_always_on;
@@ -2881,6 +2883,7 @@ enum dc_irq_source dc_interrupt_to_irq_source(
uint32_t ext_id);
bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable);
void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src);
+bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst);
enum dc_irq_source dc_get_hpd_irq_source_at_index(
struct dc *dc, uint32_t link_index);
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
index 7c293917e6fd..ecb8493ec523 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
@@ -1229,9 +1229,9 @@ static bool get_dp_dto_frequency_100hz(
*/
modulo_hz = REG_READ(MODULO[inst]);
if (modulo_hz) {
- temp = div_u64((uint64_t)clock_hz * dp_dto_ref_khz * 10, modulo_hz);
- ASSERT(temp / 100 <= 0xFFFFFFFFUL);
- *pixel_clk_100hz = (unsigned int)(temp / 100);
+ temp = clock_hz * dp_dto_ref_khz * 10;
+ ASSERT(temp <= UINT_MAX * modulo_hz * 100ULL);
+ *pixel_clk_100hz = div_u64(temp, modulo_hz * 100);
} else
*pixel_clk_100hz = 0;
} else {
@@ -1285,13 +1285,12 @@ static bool dcn401_get_dp_dto_frequency_100hz(const struct clock_source *clock_s
* - target pix_clk_hz = (DPDTO INTEGER * DPDTO MODULO + DPDTO PHASE)
*/
temp = (unsigned long long)dp_dto_integer * modulo_hz + phase_hz;
-
- if (temp / 100 > 0xFFFFFFFFUL) {
+ if (temp > (UINT_MAX * 100ULL)) {
/* pixel rate 100hz should never be this high, if it is, throw an assert and return 0 */
BREAK_TO_DEBUGGER();
*pixel_clk_100hz = 0;
} else {
- *pixel_clk_100hz = (unsigned int)(temp / 100);
+ *pixel_clk_100hz = div_u64(temp, 100);
}
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 7d8951fecd57..29dbb5e410d8 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -623,7 +623,7 @@ static bool detect_dp(struct dc_link *link,
link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
/* NUTMEG requires that we use HBR, doesn't work with RBR. */
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A)
- link->preferred_link_setting.link_rate = LINK_RATE_HIGH;
+ link->wa_flags.dp_skip_rbr = true;
}
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index 47abb4066709..1cd17a0272bc 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -750,8 +750,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
return false;
- if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN)
- initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
+ if (link->wa_flags.dp_skip_rbr) {
+ initial_link_setting.link_rate = LINK_RATE_HIGH;
+ current_link_setting.link_rate = LINK_RATE_HIGH;
+ }
/* search for the minimum link setting that:
* 1. is supported according to the link training result
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
index b92d4f378d60..97ea22af5d2b 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
@@ -992,6 +992,11 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
for (i = 0; i < pool->stream_enc_count; i++) {
if (!res_ctx->is_stream_enc_acquired[i] &&
pool->stream_enc[i]) {
+ /* DP/MST needs a digital encoder; skip analog/no-DP encoders */
+ if (dc_is_dp_signal(stream->signal) &&
+ (!pool->stream_enc[i]->funcs ||
+ !pool->stream_enc[i]->funcs->dp_set_stream_attribute))
+ continue;
/* Store first available for MST second display
* in daisy chain use case
*/
@@ -1014,7 +1019,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
* required for non DP connectors.
*/
- if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT)
+ if (j >= 0 && dc_is_dp_signal(stream->signal))
return pool->stream_enc[j];
return NULL;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
index 7de12b16d7ad..a8241036def2 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
@@ -2142,6 +2142,7 @@ static bool dcn42_resource_construct(
dc->config.use_pipe_ctx_sync_logic = true;
dc->config.dc_mode_clk_limit_support = false;
dc->config.enable_windowed_mpo_odm = true;
+ dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */
/* Use psp mailbox to enable assr */
dc->config.use_assr_psp_message = true;
/* dcn42 and afterward always support external panel replay */
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
index 527d17f29f3b..94e166c0a9b0 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
@@ -22,6 +22,7 @@
#include "dcn35/dcn35_resource.h"
#include "dcn321/dcn321_resource.h"
#include "dcn401/dcn401_resource.h"
+#include "dcn42/dcn42_resource.h"
#include "dcn42/dcn42_resource_fpu.h"
#include "dcn10/dcn10_ipp.h"
@@ -116,6 +117,23 @@
#define regAPG9_APG_DBG_GEN_CONTROL 0x38ae
#define regAPG9_APG_DBG_GEN_CONTROL_BASE_IDX 2
+#define regHUBP0_HUBPREQ_DEBUG_DB 0x05f8
+#define regHUBP0_HUBPREQ_DEBUG_DB_BASE_IDX 2
+#define regHUBP0_HUBPREQ_DEBUG 0x05f9
+#define regHUBP0_HUBPREQ_DEBUG_BASE_IDX 2
+#define regHUBP1_HUBPREQ_DEBUG_DB 0x06d4
+#define regHUBP1_HUBPREQ_DEBUG_DB_BASE_IDX 2
+#define regHUBP1_HUBPREQ_DEBUG 0x06d5
+#define regHUBP1_HUBPREQ_DEBUG_BASE_IDX 2
+#define regHUBP2_HUBPREQ_DEBUG_DB 0x07b0
+#define regHUBP2_HUBPREQ_DEBUG_DB_BASE_IDX 2
+#define regHUBP2_HUBPREQ_DEBUG 0x07b1
+#define regHUBP2_HUBPREQ_DEBUG_BASE_IDX 2
+#define regHUBP3_HUBPREQ_DEBUG_DB 0x088c
+#define regHUBP3_HUBPREQ_DEBUG_DB_BASE_IDX 2
+#define regHUBP3_HUBPREQ_DEBUG 0x088d
+#define regHUBP3_HUBPREQ_DEBUG_BASE_IDX 2
+
enum dcn401_clk_src_array_id {
DCN401_CLK_SRC_PLL0,
DCN401_CLK_SRC_PLL1,
@@ -461,7 +479,7 @@ static const struct dcn_optc_mask optc_mask = {
OPTC_COMMON_MASK_SH_LIST_DCN42B(_MASK)};
#define hubp_regs_init(id) \
- HUBP_REG_LIST_DCN42B_RI(id)
+ HUBP_REG_LIST_DCN42_RI(id)
static struct dcn_hubp2_registers hubp_regs[4];
@@ -1882,9 +1900,7 @@ static struct resource_funcs dcn42b_res_pool_funcs = {
.update_soc_for_wm_a = dcn30_update_soc_for_wm_a,
.add_phantom_pipes = dcn32_add_phantom_pipes,
.calculate_mall_ways_from_bytes = dcn32_calculate_mall_ways_from_bytes,
-#ifdef CONFIG_DRM_AMD_DC_DML21
.prepare_mcache_programming = dcn42b_prepare_mcache_programming,
-#endif
.build_pipe_pix_clk_params = dcn42b_build_pipe_pix_clk_params,
.get_power_profile = dcn401_get_power_profile,
.get_vstartup_for_pipe = dcn401_get_vstartup_for_pipe,
@@ -2087,6 +2103,7 @@ static bool dcn42b_resource_construct(
dc->config.use_pipe_ctx_sync_logic = true;
dc->config.dc_mode_clk_limit_support = false;
dc->config.enable_windowed_mpo_odm = true;
+ dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */
/* Use psp mailbox to enable assr */
dc->config.use_assr_psp_message = true;
/* dcn42 and afterward always support external panel replay */
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h
index 2da3e3c8304a..2824a0e1acc9 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h
@@ -344,7 +344,6 @@
* DCCG_SRII(PHASE, DP_DTO, 3),
* DCCG_SRII(MODULO, DP_DTO, 3),
* SR(DSCCLK3_DTO_PARAM),
- * SR(HDMISTREAMCLK_CNTL),
* SR(SYMCLKD_CLOCK_ENABLE),
* SR(SYMCLKE_CLOCK_ENABLE)
*/
@@ -360,6 +359,7 @@
SR(PHYBSYMCLK_CLOCK_CNTL), \
SR(PHYCSYMCLK_CLOCK_CNTL), \
SR(DPSTREAMCLK_CNTL), \
+ SR(HDMISTREAMCLK_CNTL), \
SR(SYMCLK32_SE_CNTL), \
SR(SYMCLK32_LE_CNTL), \
DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0), \
@@ -542,120 +542,6 @@
SRI_ARR(DC_ABM1_ACE_OFFSET_SLOPE_DATA, ABM, id), \
SRI_ARR(DC_ABM1_ACE_PWL_CNTL, ABM, id)
-/* HUBP */
-/* Not in DCN42B: HUBPREQ_DEBUG_DB and HUBPREQ_DEBUG */
-#define HUBP_REG_LIST_DCN42B_RI(id) \
- SRI_ARR(DCN_DMDATA_VM_CNTL, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_3, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_4, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_5, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_6, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_5, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_6, HUBPREQ, id), \
- HUBP_REG_LIST_DCN_VM_RI(id), \
- SRI_ARR(PREFETCH_SETTINGS, HUBPREQ, id), \
- SRI_ARR(PREFETCH_SETTINGS_C, HUBPREQ, id), \
- SRI_ARR(DCN_VM_SYSTEM_APERTURE_LOW_ADDR, HUBPREQ, id), \
- SRI_ARR(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, HUBPREQ, id), \
- SRI_ARR(CURSOR_SETTINGS, HUBPREQ, id), \
- SRI_ARR(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR0_, id), \
- SRI_ARR(CURSOR_SURFACE_ADDRESS, CURSOR0_, id), \
- SRI_ARR(CURSOR_SIZE, CURSOR0_, id), \
- SRI_ARR(CURSOR_CONTROL, CURSOR0_, id), \
- SRI_ARR(CURSOR_POSITION, CURSOR0_, id), \
- SRI_ARR(CURSOR_HOT_SPOT, CURSOR0_, id), \
- SRI_ARR(CURSOR_DST_OFFSET, CURSOR0_, id), \
- SRI_ARR(DMDATA_ADDRESS_HIGH, CURSOR0_, id), \
- SRI_ARR(DMDATA_ADDRESS_LOW, CURSOR0_, id), \
- SRI_ARR(DMDATA_CNTL, CURSOR0_, id), \
- SRI_ARR(DMDATA_SW_CNTL, CURSOR0_, id), \
- SRI_ARR(DMDATA_QOS_CNTL, CURSOR0_, id), \
- SRI_ARR(DMDATA_SW_DATA, CURSOR0_, id), \
- SRI_ARR(DMDATA_STATUS, CURSOR0_, id), \
- SRI_ARR(FLIP_PARAMETERS_0, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_1, HUBPREQ, id), \
- SRI_ARR(FLIP_PARAMETERS_2, HUBPREQ, id), \
- SRI_ARR(DCN_CUR1_TTU_CNTL0, HUBPREQ, id), \
- SRI_ARR(DCN_CUR1_TTU_CNTL1, HUBPREQ, id), \
- SRI_ARR(DCSURF_FLIP_CONTROL2, HUBPREQ, id), \
- SRI_ARR(VMID_SETTINGS_0, HUBPREQ, id), \
- SRI_ARR(DCHUBP_CNTL, HUBP, id), \
- SRI_ARR(DCSURF_ADDR_CONFIG, HUBP, id), \
- SRI_ARR(DCSURF_TILING_CONFIG, HUBP, id), \
- SRI_ARR(DCSURF_SURFACE_PITCH, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_PITCH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_CONFIG, HUBP, id), \
- SRI_ARR(DCSURF_FLIP_CONTROL, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION, HUBP, id), \
- SRI_ARR(DCSURF_PRI_VIEWPORT_START, HUBP, id), \
- SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION, HUBP, id), \
- SRI_ARR(DCSURF_SEC_VIEWPORT_START, HUBP, id), \
- SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION_C, HUBP, id), \
- SRI_ARR(DCSURF_PRI_VIEWPORT_START_C, HUBP, id), \
- SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION_C, HUBP, id), \
- SRI_ARR(DCSURF_SEC_VIEWPORT_START_C, HUBP, id), \
- SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_INUSE, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_INUSE_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_INUSE_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_INUSE_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_CONTROL, HUBPREQ, id), \
- SRI_ARR(DCSURF_SURFACE_FLIP_INTERRUPT, HUBPREQ, id), \
- SRI_ARR(HUBPRET_CONTROL, HUBPRET, id), \
- SRI_ARR(HUBPRET_READ_LINE_STATUS, HUBPRET, id), \
- SRI_ARR(DCN_EXPANSION_MODE, HUBPREQ, id), \
- SRI_ARR(DCHUBP_REQ_SIZE_CONFIG, HUBP, id), \
- SRI_ARR(DCHUBP_REQ_SIZE_CONFIG_C, HUBP, id), \
- SRI_ARR(BLANK_OFFSET_0, HUBPREQ, id), \
- SRI_ARR(BLANK_OFFSET_1, HUBPREQ, id), \
- SRI_ARR(DST_DIMENSIONS, HUBPREQ, id), \
- SRI_ARR(DST_AFTER_SCALER, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_0, HUBPREQ, id), \
- SRI_ARR(REF_FREQ_TO_PIX_FREQ, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_1, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_3, HUBPREQ, id), \
- SRI_ARR(NOM_PARAMETERS_4, HUBPREQ, id), \
- SRI_ARR(NOM_PARAMETERS_5, HUBPREQ, id), \
- SRI_ARR(PER_LINE_DELIVERY_PRE, HUBPREQ, id), \
- SRI_ARR(PER_LINE_DELIVERY, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_2, HUBPREQ, id), \
- SRI_ARR(VBLANK_PARAMETERS_4, HUBPREQ, id), \
- SRI_ARR(NOM_PARAMETERS_6, HUBPREQ, id), \
- SRI_ARR(NOM_PARAMETERS_7, HUBPREQ, id), \
- SRI_ARR(DCN_TTU_QOS_WM, HUBPREQ, id), \
- SRI_ARR(DCN_GLOBAL_TTU_CNTL, HUBPREQ, id), \
- SRI_ARR(DCN_SURF0_TTU_CNTL0, HUBPREQ, id), \
- SRI_ARR(DCN_SURF0_TTU_CNTL1, HUBPREQ, id), \
- SRI_ARR(DCN_SURF1_TTU_CNTL0, HUBPREQ, id), \
- SRI_ARR(DCN_SURF1_TTU_CNTL1, HUBPREQ, id), \
- SRI_ARR(DCN_CUR0_TTU_CNTL0, HUBPREQ, id), \
- SRI_ARR(DCN_CUR0_TTU_CNTL1, HUBPREQ, id), \
- SRI_ARR(HUBP_CLK_CNTL, HUBP, id), \
- SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id), \
- SRI_ARR(DCHUBP_MALL_CONFIG, HUBP, id), \
- SRI_ARR(DCHUBP_VMPG_CONFIG, HUBP, id), \
- SRI_ARR(UCLK_PSTATE_FORCE, HUBPREQ, id), \
- SRI_ARR(HUBP_3DLUT_DLG_PARAM, CURSOR0_, id), \
- HUBP_3DLUT_FL_REG_LIST_DCN401(id)
struct dcn42b_resource_pool {
struct resource_pool base;
};
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
index 8079da7c5335..012227c70600 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
@@ -3892,13 +3892,16 @@ static void si_notify_hw_of_powersource(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */
- if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
- return;
-
- /* The SMU automatically notices DC, but needs to be notified when switching to AC. */
- if (adev->pm.ac_power)
+ /*
+ * Check if the platform already manages the AC/DC switch via dedicated GPIO.
+ * Otherwise SMU automatically notices DC, but needs to be notified of AC.
+ */
+ if (adev->pm.ac_power &&
+ (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC))
amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC);
+
+ /* Recompute clocks with updated max_limits. */
+ amdgpu_legacy_dpm_compute_clocks(adev);
}
static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
@@ -7689,7 +7692,7 @@ static int si_dpm_process_interrupt(struct amdgpu_device *adev,
break;
}
- if (queue_thermal)
+ if (queue_thermal && amdgpu_dpm)
schedule_work(&adev->pm.dpm.thermal.work);
return 0;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
index 1d6e30269d56..4d553be56396 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
@@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr)
hwmgr->od_enabled = false;
switch (hwmgr->chip_id) {
case CHIP_BONAIRE:
- /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM
- * R7 260X cards with old MC ucode: MCLK DPM is unstable
- */
- if (adev->pdev->subsystem_vendor == 0x106B ||
- adev->pdev->device == 0x6658) {
+ /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */
+ if (adev->pdev->subsystem_vendor == 0x106B) {
dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC");
adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK;
hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
index 95bf187f02a5..bc82ba2e4c9b 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
@@ -5857,15 +5857,19 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr)
static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr)
{
struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
- /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */
- if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ /*
+ * Check if the platform already manages the AC/DC switch via dedicated GPIO.
+ * Otherwise SMU automatically notices DC, but needs to be notified of AC.
+ */
+ if (adev->pm.ac_power &&
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_AutomaticDCTransition))
- return;
-
- /* The SMU automatically notices DC, but needs to be notified when switching to AC. */
- if (adev->pm.ac_power)
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunningOnAC, NULL);
+
+ /* Recompute clocks with updated max_limits. */
+ pp_funcs->pm_compute_clocks(adev->powerplay.pp_handle);
}
static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 762ec3cede96..3969a7670482 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1367,6 +1367,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
}
+static int smu_set_power_dep(struct smu_context *smu, bool enable)
+{
+ if (!smu->ppt_funcs->set_power_dep)
+ return 0;
+
+ return smu->ppt_funcs->set_power_dep(smu, enable);
+}
+
static int smu_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -1428,6 +1436,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
if (!smu->ppt_funcs->get_fan_control_mode)
smu->adev->pm.no_fan = true;
+ smu_set_power_dep(smu, true);
+
return 0;
}
@@ -1450,6 +1460,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)
smu_fini_microcode(smu);
+ smu_set_power_dep(smu, false);
+
return 0;
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index d76e0b005308..e3a89e9a9df4 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -749,6 +749,9 @@ struct smu_context {
bool pm_enabled;
bool is_apu;
+ /* Power dependency link from an integrated xHCI controller to the GPU */
+ struct device_link *usb_power_link;
+
uint32_t smc_driver_if_version;
uint32_t smc_fw_if_version;
uint32_t smc_fw_version;
@@ -1648,12 +1651,19 @@ struct pptable_funcs {
int (*ras_send_msg)(struct smu_context *smu,
enum smu_message_type msg, uint32_t param, uint32_t *read_arg);
-
/**
* @get_ras_smu_drv: Get RAS smu driver interface
* Return: ras_smu_drv *
*/
int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
+
+ /**
+ * @set_power_dep: Create or destroy a power dependency link
+ * from an integrated xHCI controller to the GPU so that the GPU is
+ * resumed before the USB controller during PM resume. @enable is true
+ * to create the link and false to tear it down.
+ */
+ int (*set_power_dep)(struct smu_context *smu, bool enable);
};
typedef enum {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
index 75719c47a41e..3d73f2050bbe 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
@@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
return 0;
}
+/*
+ * Link any xHCI controller sharing the GPU's PCIe root port as a consumer
+ * of the GPU so the GPU resumes first, avoiding an xHCI resume race.
+ */
+static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct pci_dev *gpu_pdev = adev->pdev;
+ struct pci_dev *root_port, *usb_pdev = NULL;
+ struct device_link *link;
+
+ if (!enable) {
+ if (smu->usb_power_link) {
+ device_link_del(smu->usb_power_link);
+ smu->usb_power_link = NULL;
+ }
+ return 0;
+ }
+
+ root_port = pcie_find_root_port(gpu_pdev);
+ while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
+ struct pci_dev *usb_root;
+
+ usb_root = pcie_find_root_port(usb_pdev);
+ if (usb_root != root_port)
+ continue;
+
+ /* Create device link: USB (consumer) depends on GPU (supplier) */
+ link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (link) {
+ smu->usb_power_link = link;
+ drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
+ pci_name(usb_pdev), pci_name(gpu_pdev));
+ /* Only create one link for the first USB controller found */
+ break;
+ }
+ }
+
+ pci_dev_put(usb_pdev);
+
+ return 0;
+}
+
static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
.check_fw_status = smu_v14_0_check_fw_status,
.check_fw_version = smu_cmn_check_fw_version,
@@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
.set_mall_enable = smu_v14_0_common_set_mall_enable,
+ .set_power_dep = smu_v14_0_0_set_power_dep,
};
static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)
diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c
index f85cb293a3db..e417c7533053 100644
--- a/drivers/gpu/drm/drm_panel_backlight_quirks.c
+++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c
@@ -20,6 +20,15 @@ struct drm_get_panel_backlight_quirk {
};
static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = {
+ /* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "LENOVO",
+ .dmi_match_other.field = DMI_PRODUCT_VERSION,
+ .dmi_match_other.value = "Lenovo Legion 5 15ARH05",
+ .ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df),
+ .quirk = { .force_pwm = true, },
+ },
/* 13 inch matte panel */
{
.dmi_match.field = DMI_BOARD_VENDOR,
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
index 7a6c07f6aaeb..266e042e0023 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp)
int ret;
u8 bit_min, bit_max;
- /*
- * Since we only support Fully AUX Based VESA Backlight interface make sure
- * backlight enable is possible via AUX along with backlight adjustment
- */
- if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP &&
- intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
+ if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
return false;
ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min);
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index ad4bfff6903d..164b7d61c9a3 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -2126,19 +2126,6 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
return 0;
}
-
-/* Divide a U16.16 fixed-point value by 2, staying in fixed-point domain */
-static inline u32 fp_16_16_div2(u32 fp)
-{
- return fp >> 1;
-}
-
-/* Convert a U16.16 fixed-point value to integer, rounding up */
-static inline int fp_16_16_to_int_ceil(u32 fp)
-{
- return DIV_ROUND_UP(fp, 1 << 16);
-}
-
static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state);
@@ -2154,14 +2141,20 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation);
/*
- * LNL+ UV surface start/size =
- * ceiling(half of Y plane start/size). Use ceiling division
- * unconditionally; it is a no-op for even values.
+ * UV (chroma) start/size = ceiling(half of the *integer* Y plane
+ * start/size), i.e. the value the luma surface programs (src >> 16),
+ * not the raw U16.16. A bigjoiner seam mapped through the scaler can
+ * give a fractional luma src; ceiling that directly would round the
+ * chroma one column too far and read past the chroma surface.
*/
- int x = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.x1));
- int y = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.y1));
- int w = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_width(&plane_state->uapi.src)));
- int h = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_height(&plane_state->uapi.src)));
+ int luma_x = plane_state->uapi.src.x1 >> 16;
+ int luma_y = plane_state->uapi.src.y1 >> 16;
+ int luma_w = drm_rect_width(&plane_state->uapi.src) >> 16;
+ int luma_h = drm_rect_height(&plane_state->uapi.src) >> 16;
+ int x = DIV_ROUND_UP(luma_x, 2);
+ int y = DIV_ROUND_UP(luma_y, 2);
+ int w = DIV_ROUND_UP(luma_x + luma_w, 2) - x;
+ int h = DIV_ROUND_UP(luma_y + luma_h, 2) - y;
u32 offset;
/* FIXME not quite sure how/if these apply to the chroma plane */
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c
index 5a3677ea25b0..a4ce21d4c024 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.c
+++ b/drivers/gpu/drm/i915/display/skl_watermark.c
@@ -3856,7 +3856,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc,
return;
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
- skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
+ skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0);
crtc_state->wm.skl.plane_min_ddb[plane->id] = 0;
crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0;
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c b/drivers/gpu/drm/i915/gt/intel_engine_user.c
index be4bbff1a57c..d5190e11b270 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_user.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c
@@ -259,7 +259,7 @@ void intel_engines_driver_register(struct drm_i915_private *i915)
p = &prev->rb_right;
}
- if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) &&
+ if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) &&
IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) {
struct intel_engine_cs *engine;
unsigned int isolation;
diff --git a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
index 33351deeea4f..07eaf71955c4 100644
--- a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
+++ b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
@@ -16,9 +16,9 @@ static int cmp_u64(const void *A, const void *B)
{
const u64 *a = A, *b = B;
- if (a < b)
+ if (*a < *b)
return -1;
- else if (a > b)
+ else if (*a > *b)
return 1;
else
return 0;
@@ -28,9 +28,9 @@ static int cmp_u32(const void *A, const void *B)
{
const u32 *a = A, *b = B;
- if (a < b)
+ if (*a < *b)
return -1;
- else if (a > b)
+ else if (*a > *b)
return 1;
else
return 0;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 4032c6ad45bc..e384cbd0cbf7 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -3764,10 +3764,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
sor->num_settings = sor->soc->num_settings;
sor->pmc = devm_tegra_pmc_get(&pdev->dev);
- if (IS_ERR(sor->pmc)) {
- err = PTR_ERR(sor->pmc);
- goto put_aux;
- }
+ if (IS_ERR(sor->pmc))
+ return PTR_ERR(sor->pmc);
np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
if (np) {
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 4c80bac67622..7ed76349075f 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1102,6 +1102,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
xe_pm_runtime_get_noresume(xe);
}
+ /*
+ * Attach CCS BBs before submitting the copy job below so a VF
+ * migration racing the copy sees valid, up to date attach state.
+ */
+ if (IS_VF_CCS_READY(xe) &&
+ ((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
+ (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
+ handle_system_ccs) {
+ ret = xe_sriov_vf_ccs_attach_bo(bo, new_mem);
+ if (ret) {
+ xe_pm_runtime_put(xe);
+ goto out;
+ }
+ }
+
if (move_lacks_source) {
u32 flags = 0;
@@ -1139,22 +1154,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
ttm_bo_move_null(ttm_bo, new_mem);
}
- dma_fence_put(fence);
- xe_pm_runtime_put(xe);
-
/*
- * CCS meta data is migrated from TT -> SMEM. So, let us detach the
- * BBs from BO as it is no longer needed.
+ * Detach must wait for the copy above to complete: a VF migration
+ * racing an in-flight copy must still see valid CCS BBs, so don't
+ * tear them down until the copy fence has signaled.
*/
if (IS_VF_CCS_READY(xe) && old_mem_type == XE_PL_TT &&
- new_mem->mem_type == XE_PL_SYSTEM)
+ new_mem->mem_type == XE_PL_SYSTEM) {
+ dma_fence_wait(fence, false);
xe_sriov_vf_ccs_detach_bo(bo);
+ }
- if (IS_VF_CCS_READY(xe) &&
- ((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
- (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
- handle_system_ccs)
- ret = xe_sriov_vf_ccs_attach_bo(bo);
+ dma_fence_put(fence);
+ xe_pm_runtime_put(xe);
out:
if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) &&
@@ -1349,7 +1361,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)
backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
- XE_BO_FLAG_PINNED, &exec);
+ XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1490,7 +1502,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
- XE_BO_FLAG_PINNED, &exec);
+ XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1826,6 +1838,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
if (bo->ttm.base.import_attach)
drm_prime_gem_destroy(&bo->ttm.base, NULL);
+ if (bo->dma_buf)
+ dma_buf_put(bo->dma_buf);
drm_gem_object_release(&bo->ttm.base);
xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
@@ -2283,6 +2297,8 @@ void xe_bo_free(struct xe_bo *bo)
* @cpu_caching: The cpu caching used for system memory backing store.
* @type: The TTM buffer object type.
* @flags: XE_BO_FLAG_ flags.
+ * @dma_buf: The dma-buf to reference for the BO lifetime (imported BOs),
+ * or NULL.
* @exec: The drm_exec transaction to use for exhaustive eviction.
*
* Initialize or create an xe buffer object. On failure, any allocated buffer
@@ -2294,7 +2310,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
- u32 flags, struct drm_exec *exec)
+ u32 flags, struct dma_buf *dma_buf,
+ struct drm_exec *exec)
{
struct ttm_operation_ctx ctx = {
.interruptible = true,
@@ -2383,6 +2400,17 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
placement = (type == ttm_bo_type_sg ||
bo->flags & XE_BO_FLAG_DEFER_BACKING) ? &sys_placement :
&bo->placement;
+
+ /*
+ * For imported BOs, keep the exporter dma-buf alive for the BO
+ * lifetime. Taken before ttm_bo_init_reserved() to also cover a
+ * creation failure there. Released in xe_ttm_bo_destroy().
+ */
+ if (dma_buf) {
+ get_dma_buf(dma_buf);
+ bo->dma_buf = dma_buf;
+ }
+
err = ttm_bo_init_reserved(&xe->ttm, &bo->ttm, type,
placement, alignment,
&ctx, NULL, resv, xe_ttm_bo_destroy);
@@ -2500,7 +2528,7 @@ __xe_bo_create_locked(struct xe_device *xe,
vm && !xe_vm_in_fault_mode(vm) &&
flags & XE_BO_FLAG_USER ?
&vm->lru_bulk_move : NULL, size,
- cpu_caching, type, flags, exec);
+ cpu_caching, type, flags, NULL, exec);
if (IS_ERR(bo))
return bo;
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 6340317f7d2e..7ae1d9ac0574 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -118,7 +118,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
- u32 flags, struct drm_exec *exec);
+ u32 flags, struct dma_buf *dma_buf,
+ struct drm_exec *exec);
struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
enum ttm_bo_type type, u32 flags,
diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h
index fcc63ae3f455..e45f24301050 100644
--- a/drivers/gpu/drm/xe/xe_bo_types.h
+++ b/drivers/gpu/drm/xe/xe_bo_types.h
@@ -36,6 +36,8 @@ struct xe_bo {
struct xe_bo *backup_obj;
/** @parent_obj: Ref to parent bo if this a backup_obj */
struct xe_bo *parent_obj;
+ /** @dma_buf: Imported dma-buf ref to keep its resv alive. */
+ struct dma_buf *dma_buf;
/** @flags: flags for this buffer object */
u32 flags;
/** @vm: VM this BO is attached to, for extobj this will be NULL */
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index abe25aedeead..dcb48caa485d 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -426,7 +426,6 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = {
static const struct drm_driver admin_only_driver = {
.driver_features =
- XE_DISPLAY_DRIVER_FEATURES |
DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA,
.open = xe_file_open,
.postclose = xe_file_close,
@@ -438,7 +437,6 @@ static const struct drm_driver admin_only_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
- XE_DISPLAY_DRIVER_OPS,
};
/**
@@ -580,7 +578,7 @@ int xe_device_init_early(struct xe_device *xe)
WQ_MEM_RECLAIM);
xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0);
xe->unordered_wq = alloc_workqueue("xe-unordered-wq", WQ_PERCPU, 0);
- xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU, 0);
+ xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU | WQ_MEM_RECLAIM, 0);
if (!xe->ordered_wq || !xe->unordered_wq ||
!xe->preempt_fence_wq || !xe->destroy_wq) {
/*
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 32dd2ffbc796..aad10899d9ac 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -355,7 +355,7 @@ struct xe_device {
/** @unordered_wq: used to serialize unordered work */
struct workqueue_struct *unordered_wq;
- /** @destroy_wq: used to serialize user destroy work, like queue */
+ /** @destroy_wq: used to serialize SVM pagemap destroy work */
struct workqueue_struct *destroy_wq;
/** @tiles: device tiles */
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 8a920e58245c..bf0728838ead 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -302,7 +302,7 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)
bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size,
0, /* Will require 1way or 2way for vm_bind */
- ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec);
+ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec);
drm_exec_retry_on_contention(&exec);
if (IS_ERR(bo)) {
ret = PTR_ERR(bo);
diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
index e5e53b421f29..cda14d954e57 100644
--- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
@@ -10,6 +10,7 @@
#include <linux/workqueue.h>
#include "xe_gpu_scheduler_types.h"
+#include "xe_hw_fence_types.h"
struct dma_fence;
struct xe_exec_queue;
@@ -24,6 +25,10 @@ struct xe_guc_exec_queue {
struct rcu_head rcu;
/** @sched: GPU scheduler for this xe_exec_queue */
struct xe_gpu_scheduler sched;
+ /**
+ * @name: Scheduler timeline name, kept with @sched until RCU free.
+ */
+ char name[MAX_FENCE_NAME_LEN];
/** @entity: Scheduler entity for this xe_exec_queue */
struct xe_sched_entity entity;
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index f5c3d8a97ec6..1c92b96e4c32 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -10,6 +10,7 @@
#include <linux/circ_buf.h>
#include <linux/dma-fence-array.h>
+#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include "abi/guc_actions_abi.h"
@@ -37,6 +38,7 @@
#include "xe_macros.h"
#include "xe_map.h"
#include "xe_mocs.h"
+#include "xe_module.h"
#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
@@ -232,17 +234,9 @@ static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
static void guc_submit_sw_fini(struct drm_device *drm, void *arg)
{
struct xe_guc *guc = arg;
- struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
- int ret;
-
- ret = wait_event_timeout(guc->submission_state.fini_wq,
- xa_empty(&guc->submission_state.exec_queue_lookup),
- HZ * 5);
- drain_workqueue(xe->destroy_wq);
-
- xe_gt_assert(gt, ret);
+ xe_gt_assert(gt, xa_empty(&guc->submission_state.exec_queue_lookup));
xa_destroy(&guc->submission_state.exec_queue_lookup);
}
@@ -319,8 +313,6 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
xa_init(&guc->submission_state.exec_queue_lookup);
- init_waitqueue_head(&guc->submission_state.fini_wq);
-
primelockdep(guc);
guc->submission_state.initialized = true;
@@ -411,9 +403,6 @@ static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q,
xe_guc_id_mgr_release_locked(&guc->submission_state.idm,
q->guc->id, q->width);
- if (xa_empty(&guc->submission_state.exec_queue_lookup))
- wake_up(&guc->submission_state.fini_wq);
-
mutex_unlock(&guc->submission_state.lock);
}
@@ -1685,6 +1674,7 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
{
struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct drm_device *drm = &guc_to_xe(guc)->drm;
if (xe_exec_queue_is_multi_queue_secondary(q)) {
struct xe_exec_queue_group *group = q->multi_queue.group;
@@ -1703,36 +1693,52 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
* (timeline name).
*/
kfree_rcu(ge, rcu);
+
+ drm_dev_put(drm);
}
-static void __guc_exec_queue_destroy_async(struct work_struct *w)
+static void guc_exec_queue_do_destroy(struct xe_exec_queue *q)
{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, destroy_async);
- struct xe_exec_queue *q = ge->q;
+ struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+ struct drm_device *drm = &xe->drm;
+
+ /*
+ * guc_exec_queue_fini() drops the queue's drm_device ref.
+ * Keep the device alive until the PM-runtime guard unwinds.
+ */
+ drm_dev_get(drm);
+
+ scoped_guard(xe_pm_runtime, xe) {
+ trace_xe_exec_queue_destroy(q);
- guard(xe_pm_runtime)(guc_to_xe(guc));
- trace_xe_exec_queue_destroy(q);
+ /* Confirm no work left behind accessing device structures */
+ cancel_delayed_work_sync(&ge->sched.base.work_tdr);
- /* Confirm no work left behind accessing device structures */
- cancel_delayed_work_sync(&ge->sched.base.work_tdr);
+ xe_exec_queue_fini(q);
+ }
- xe_exec_queue_fini(q);
+ drm_dev_put(drm);
}
-static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
+static void __guc_exec_queue_destroy_async(struct work_struct *w)
{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
+ struct xe_guc_exec_queue *ge =
+ container_of(w, struct xe_guc_exec_queue, destroy_async);
+
+ guc_exec_queue_do_destroy(ge->q);
+}
+static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
+{
INIT_WORK(&q->guc->destroy_async, __guc_exec_queue_destroy_async);
/* We must block on kernel engines so slabs are empty on driver unload */
if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
- __guc_exec_queue_destroy_async(&q->guc->destroy_async);
+ guc_exec_queue_do_destroy(q);
else
- queue_work(xe->destroy_wq, &q->guc->destroy_async);
+ xe_destroy_wq_queue(&q->guc->destroy_async);
}
static void __guc_exec_queue_destroy(struct xe_guc *guc, struct xe_exec_queue *q)
@@ -1927,6 +1933,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct drm_device *drm = &guc_to_xe(guc)->drm;
struct workqueue_struct *submit_wq = NULL;
struct xe_guc_exec_queue *ge;
long timeout;
@@ -1938,6 +1945,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
if (!ge)
return -ENOMEM;
+ drm_dev_get(drm);
+
q->guc = ge;
ge->q = q;
init_rcu_head(&ge->rcu);
@@ -1955,6 +1964,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
xe_exec_queue_assign_name(q, q->guc->id);
+ strscpy(ge->name, q->name, sizeof(ge->name));
+
/*
* Use primary queue's submit_wq for all secondary queues of a
* multi queue group. This serialization avoids any locking around
@@ -1969,7 +1980,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
- q->name, gt_to_xe(q->gt)->drm.dev);
+ ge->name, gt_to_xe(q->gt)->drm.dev);
if (err)
goto err_release_id;
@@ -2014,6 +2025,7 @@ err_release_id:
release_guc_id(guc, q);
err_free:
kfree(ge);
+ drm_dev_put(drm);
return err;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_types.h b/drivers/gpu/drm/xe/xe_guc_types.h
index c7b9642b41ba..31a2acb63ac3 100644
--- a/drivers/gpu/drm/xe/xe_guc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_types.h
@@ -100,8 +100,6 @@ struct xe_guc {
* even initialized - before that not even the lock is valid
*/
bool initialized;
- /** @submission_state.fini_wq: submit fini wait queue */
- wait_queue_head_t fini_wq;
} submission_state;
/** @hwconfig: Hardware config state */
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 706783863d07..f05f23221c1b 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
struct platform_device *pdev;
struct fwnode_handle *fwnode;
int ret;
+ u32 id;
fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
+ id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci);
+
/*
* Not using platform_device_register_full() here because we don't have
* a handle to the platform_device before it returns. xe_i2c_notifier()
* uses that handle, but it may be called before
* platform_device_register_full() is done.
*/
- pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
+ pdev = platform_device_alloc(adapter_name, id);
if (!pdev) {
ret = -ENOMEM;
goto err_fwnode_remove;
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index 9428dd5e7760..be787f331768 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -1166,6 +1166,8 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
* @tile: Tile whose migration context to be used.
* @q : Execution to be used along with migration context.
* @src_bo: The buffer object @src is currently bound to.
+ * @new_mem: The (not yet committed) destination resource @src_bo is being
+ * moved into; src_bo->ttm.resource is still the old resource.
* @read_write : Creates BB commands for CCS read/write.
*
* Creates batch buffer instructions to copy CCS metadata from CCS pool to
@@ -1177,12 +1179,13 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
*/
int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
+ struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write)
{
bool src_is_pltt = read_write == XE_SRIOV_VF_CCS_READ_CTX;
bool dst_is_pltt = read_write == XE_SRIOV_VF_CCS_WRITE_CTX;
- struct ttm_resource *src = src_bo->ttm.resource;
+ struct ttm_resource *src = new_mem;
struct xe_migrate *m = tile->migrate;
struct xe_gt *gt = tile->primary_gt;
u32 batch_size, batch_size_allocated;
@@ -1310,6 +1313,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
* content.
* @src_bo: The buffer object @src is currently bound to.
* @read_write : Creates BB commands for CCS read/write.
+ * @bound: Device is bound
*
* Directly clearing the BB lacks atomicity and can lead to undefined
* behavior if the vCPU is halted mid-operation during the clearing
@@ -1322,7 +1326,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
* Returns: None.
*/
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
- enum xe_sriov_vf_ccs_rw_ctxs read_write)
+ enum xe_sriov_vf_ccs_rw_ctxs read_write,
+ bool bound)
{
struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write];
struct xe_device *xe = xe_bo_device(src_bo);
@@ -1336,13 +1341,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
bb_pool = ctx->mem.ccs_bb_pool;
scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) {
- xe_mem_pool_swap_shadow_locked(bb_pool);
+ if (bound) {
+ xe_mem_pool_swap_shadow_locked(bb_pool);
- cs = xe_mem_pool_node_cpu_addr(bb);
- memset(cs, MI_NOOP, bb->sa_node.size);
- xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
+ cs = xe_mem_pool_node_cpu_addr(bb);
+ memset(cs, MI_NOOP, bb->sa_node.size);
+ xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
- xe_mem_pool_sync_shadow_locked(bb);
+ xe_mem_pool_sync_shadow_locked(bb);
+ }
xe_mem_pool_free_node(bb);
src_bo->bb_ccs[read_write] = NULL;
}
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index 965c45889c72..c3a268b01768 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -138,10 +138,12 @@ struct dma_fence *xe_migrate_resolve(struct xe_migrate *m,
int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
+ struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write);
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
- enum xe_sriov_vf_ccs_rw_ctxs read_write);
+ enum xe_sriov_vf_ccs_rw_ctxs read_write,
+ bool bound);
struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index 4cb578182912..99347f216ec8 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/workqueue.h>
#include <drm/drm_module.h>
@@ -91,6 +92,50 @@ static int xe_check_nomodeset(void)
return 0;
}
+static struct workqueue_struct *xe_destroy_wq;
+
+static int __init xe_destroy_wq_module_init(void)
+{
+ xe_destroy_wq = alloc_workqueue("xe-guc-destroy-wq", WQ_UNBOUND, 0);
+ if (!xe_destroy_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+static void xe_destroy_wq_module_exit(void)
+{
+ if (xe_destroy_wq)
+ destroy_workqueue(xe_destroy_wq);
+ xe_destroy_wq = NULL;
+}
+
+/**
+ * xe_destroy_wq_queue() - Queue work on the destroy workqueue
+ * @work: work item to queue
+ *
+ * The destroy workqueue has module lifetime and is used for GuC exec queue
+ * teardown that can outlive a single xe_device. SVM pagemap destroy uses the
+ * per-device xe->destroy_wq instead.
+ *
+ * Return: %true if @work was queued, %false if it was already pending.
+ */
+bool xe_destroy_wq_queue(struct work_struct *work)
+{
+ return queue_work(xe_destroy_wq, work);
+}
+
+/**
+ * xe_destroy_wq_flush() - Flush the destroy workqueue
+ *
+ * Drains all pending destroy work. Called from PCI remove to ensure
+ * teardown ordering before the device is destroyed.
+ */
+void xe_destroy_wq_flush(void)
+{
+ if (xe_destroy_wq)
+ flush_workqueue(xe_destroy_wq);
+}
+
struct init_funcs {
int (*init)(void);
void (*exit)(void);
@@ -113,6 +158,10 @@ static const struct init_funcs init_funcs[] = {
.exit = xe_sched_job_module_exit,
},
{
+ .init = xe_destroy_wq_module_init,
+ .exit = xe_destroy_wq_module_exit,
+ },
+ {
.init = xe_register_pci_driver,
.exit = xe_unregister_pci_driver,
},
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index 79cb9639c0f3..e8e54f701cf3 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -8,6 +8,8 @@
#include <linux/types.h>
+struct work_struct;
+
/* Module modprobe variables */
struct xe_modparam {
bool force_execlist;
@@ -27,5 +29,8 @@ struct xe_modparam {
extern struct xe_modparam xe_modparam;
+bool xe_destroy_wq_queue(struct work_struct *work);
+void xe_destroy_wq_flush(void);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c
index 33487e91f366..1ea67eaeae24 100644
--- a/drivers/gpu/drm/xe/xe_nvm.c
+++ b/drivers/gpu/drm/xe/xe_nvm.c
@@ -60,35 +60,40 @@ static bool xe_nvm_writable_override(struct xe_device *xe)
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
bool writable_override;
struct xe_reg reg;
- u32 test_bit;
+ u32 test_bit, test_val;
switch (xe->info.platform) {
case XE_CRESCENTISLAND:
reg = PCODE_SCRATCH(0);
test_bit = FDO_MODE;
+ test_val = FDO_MODE;
break;
case XE_BATTLEMAGE:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_PVC:
reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_DG2:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_DG1:
reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
default:
drm_err(&xe->drm, "Unknown platform\n");
return true;
}
- writable_override = !(xe_mmio_read32(mmio, reg) & test_bit);
+ writable_override = (xe_mmio_read32(mmio, reg) & test_bit) == test_val;
if (writable_override)
drm_info(&xe->drm, "NVM access overridden by jumper\n");
return writable_override;
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 3165686e3e04..9cd873708136 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -788,7 +788,8 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
- desc->has_display;
+ desc->has_display &&
+ !xe_device_is_admin_only(xe);
xe->info.force_execlist = xe_modparam.force_execlist;
xe_assert(xe, desc->max_gt_per_tile > 0);
@@ -1048,6 +1049,12 @@ static void xe_pci_remove(struct pci_dev *pdev)
return;
xe_device_remove(xe);
+
+ /*
+ * Preserve remove-time flush after moving destroy work to module
+ * lifetime.
+ */
+ xe_destroy_wq_flush();
xe_pm_fini(xe);
}
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index e787c0c27c42..39c9c8f0ea2d 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -2365,8 +2365,11 @@ static void
xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
{
init_llist_head(&pt_update_ops->deferred);
+ pt_update_ops->current_op = 0;
pt_update_ops->start = ~0x0ull;
pt_update_ops->last = 0x0ull;
+ pt_update_ops->needs_svm_lock = false;
+ pt_update_ops->needs_invalidation = false;
xe_page_reclaim_list_init(&pt_update_ops->prl);
}
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 09b99fb2608b..a8c831fbee3b 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -3,6 +3,8 @@
* Copyright © 2025 Intel Corporation
*/
+#include <drm/drm_drv.h>
+
#include "instructions/xe_mi_commands.h"
#include "instructions/xe_gpu_commands.h"
#include "xe_bb.h"
@@ -404,6 +406,8 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
/**
* xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO.
* @bo: the &buffer object to which batch buffer commands will be added.
+ * @new_mem: the (not yet committed) destination resource @bo is being moved
+ * into; bo->ttm.resource is still the old resource at this point.
*
* This function shall be called only by VF. It inserts the PTEs and copy
* command instructions in the BO by calling xe_migrate_ccs_rw_copy()
@@ -411,7 +415,7 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
*
* Returns: 0 if successful, negative error code on failure.
*/
-int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
+int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem)
{
struct xe_device *xe = xe_bo_device(bo);
enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
@@ -430,7 +434,21 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
xe_assert(xe, !bb);
ctx = &xe->sriov.vf.ccs.contexts[ctx_id];
- err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, ctx_id);
+ err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, new_mem, ctx_id);
+ if (err)
+ goto err_unwind;
+ }
+ return 0;
+
+err_unwind:
+ /*
+ * Clean up any contexts already attached. Can't reuse
+ * xe_sriov_vf_ccs_detach_bo() here as it requires both contexts
+ * attached before cleaning up either one.
+ */
+ for_each_ccs_rw_ctx(ctx_id) {
+ if (bo->bb_ccs[ctx_id])
+ xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true);
}
return err;
}
@@ -450,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo)
struct xe_device *xe = xe_bo_device(bo);
enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
struct xe_mem_pool_node *bb;
+ bool bound;
+ int idx;
xe_assert(xe, IS_VF_CCS_READY(xe));
if (!xe_bo_has_valid_ccs_bb(bo))
return 0;
+ bound = drm_dev_enter(&xe->drm, &idx);
+
for_each_ccs_rw_ctx(ctx_id) {
bb = bo->bb_ccs[ctx_id];
if (!bb)
continue;
- xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
+ xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound);
}
+
+ if (bound)
+ drm_dev_exit(idx);
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
index 00e58b36c510..e1034d852104 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
@@ -11,11 +11,12 @@
#include "xe_sriov_vf_ccs_types.h"
struct drm_printer;
+struct ttm_resource;
struct xe_device;
struct xe_bo;
int xe_sriov_vf_ccs_init(struct xe_device *xe);
-int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo);
+int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem);
int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_register_context(struct xe_device *xe);
void xe_sriov_vf_ccs_rebase(struct xe_device *xe);
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 080c2fff0e95..67819deb45e3 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1809,10 +1809,10 @@ err_close:
return ERR_PTR(err);
err_svm_fini:
- if (flags & XE_VM_FLAG_FAULT_MODE) {
- vm->size = 0; /* close the vm */
- xe_svm_fini(vm);
- }
+ vm->size = 0; /* close the vm */
+ if (flags & XE_VM_FLAG_FAULT_MODE)
+ xe_svm_close(vm);
+ xe_svm_fini(vm);
err_no_resv:
mutex_destroy(&vm->snap_mutex);
for_each_tile(tile, xe, id)
@@ -3255,11 +3255,26 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
.request_decompress = false,
.check_purged = true,
});
- if (!err && !xe_vma_has_no_bo(vma))
- err = xe_bo_migrate(xe_vma_bo(vma),
- region_to_mem_type[region],
- NULL,
- exec);
+ if (!err && !xe_vma_has_no_bo(vma)) {
+ struct xe_bo *bo = xe_vma_bo(vma);
+ u32 mem_type;
+
+ if (region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) {
+ unsigned int i;
+
+ mem_type = XE_PL_TT;
+ for (i = 0; i < bo->placement.num_placement; i++) {
+ if (mem_type_is_vram(bo->placements[i].mem_type)) {
+ mem_type = bo->placements[i].mem_type;
+ break;
+ }
+ }
+ } else {
+ mem_type = region_to_mem_type[region];
+ }
+
+ err = xe_bo_migrate(bo, mem_type, NULL, exec);
+ }
break;
}
default:
diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c
index 246fe1843142..0474768a38aa 100644
--- a/drivers/gpu/drm/xe/xe_vm_madvise.c
+++ b/drivers/gpu/drm/xe/xe_vm_madvise.c
@@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end)
return err;
}
+/**
+ * madvise_range_needs_invalidation() - Check whether madvise needs invalidation
+ * @args: madvise ioctl arguments
+ *
+ * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are
+ * zapped only if the BO is later purged.
+ *
+ * Return: true when the update needs PTE invalidation.
+ */
+static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args)
+{
+ return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE;
+}
+
static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args)
{
if (XE_IOCTL_DBG(xe, !args))
@@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args,
&details);
- err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
- madvise_range.addr + args->range);
+ if (madvise_range_needs_invalidation(args))
+ err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
+ madvise_range.addr + args->range);
if (madvise_range.has_svm_userptr_vmas)
xe_svm_notifier_unlock(vm);
diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c
index 900daf1d1b1b..fe65ed246775 100644
--- a/drivers/gpu/drm/xe/xe_wopcm.c
+++ b/drivers/gpu/drm/xe/xe_wopcm.c
@@ -49,9 +49,9 @@
*/
/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
-/* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */
+/* FIXME: Larger size require for some platforms, do a proper probe sooner or later */
#define DGFX_WOPCM_SIZE SZ_4M
-/* FIXME: Larger size require for MTL, do a proper probe sooner or later */
+#define LNL_WOPCM_SIZE SZ_8M
#define MTL_WOPCM_SIZE SZ_4M
#define WOPCM_SIZE SZ_2M
@@ -179,9 +179,14 @@ err_out:
u32 xe_wopcm_size(struct xe_device *xe)
{
- return IS_DGFX(xe) ? DGFX_WOPCM_SIZE :
- xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE :
- WOPCM_SIZE;
+ if (xe->info.platform >= XE_LUNARLAKE)
+ return LNL_WOPCM_SIZE;
+ else if (IS_DGFX(xe))
+ return DGFX_WOPCM_SIZE;
+ else if (xe->info.platform == XE_METEORLAKE)
+ return MTL_WOPCM_SIZE;
+ else
+ return WOPCM_SIZE;
}
static u32 max_wopcm_size(struct xe_device *xe)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 28313d0fad37..d5e6e2eca3b3 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1061,11 +1061,28 @@ static inline enum imx_i2c_state i2c_imx_isr_read_continue(struct imx_i2c_struct
static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx)
{
u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ unsigned int temp;
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) {
+ /*
+ * SMBus 3.1 6.5.7: support count byte of 0.
+ * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either.
+ * So NACK it (TXAK) to not hold the bus.
+ */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ if (len == 0) {
+ i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = 0;
+ i2c_imx->msg->len = 2;
+ return;
+ }
+
i2c_imx->isr_result = -EPROTO;
i2c_imx->state = IMX_I2C_STATE_FAILED;
wake_up(&i2c_imx->queue);
+ return;
}
i2c_imx->msg->len += len;
i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len;
@@ -1415,6 +1432,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
int i, result;
unsigned int temp;
int block_data = msgs->flags & I2C_M_RECV_LEN;
+ int block_err = 0;
result = i2c_imx_prepare_read(i2c_imx, msgs, false);
if (result)
@@ -1436,8 +1454,20 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
*/
if ((!i) && block_data) {
len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
- if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX))
- return -EPROTO;
+ if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) {
+ /*
+ * SMBus 3.1 6.5.7: support count byte of 0.
+ * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either.
+ */
+ if (len > I2C_SMBUS_BLOCK_MAX)
+ block_err = -EPROTO;
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+ msgs->buf[0] = 0;
+ msgs->len = 2;
+ continue;
+ }
dev_dbg(&i2c_imx->adapter.dev,
"<%s> read length: 0x%X\n",
__func__, len);
@@ -1485,7 +1515,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
"<%s> read byte: B%d=0x%X\n",
__func__, i, msgs->buf[i]);
}
- return 0;
+ return block_err;
}
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c
index 9152cf436bea..51a0c3d80fc9 100644
--- a/drivers/i2c/busses/i2c-k1.c
+++ b/drivers/i2c/busses/i2c-k1.c
@@ -596,7 +596,7 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
status = readl(i2c->base + SPACEMIT_ISR);
if (!status)
- return IRQ_HANDLED;
+ return IRQ_NONE;
i2c->status = status;
diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c
index 6c1cfe9ec8ac..e33512b25353 100644
--- a/drivers/i2c/busses/i2c-mlxbf.c
+++ b/drivers/i2c/busses/i2c-mlxbf.c
@@ -1051,8 +1051,10 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev,
tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params);
if (IS_ERR(tmp_res->io)) {
+ int ret = PTR_ERR(tmp_res->io);
+
devm_kfree(dev, tmp_res);
- return PTR_ERR(tmp_res->io);
+ return ret;
}
tmp_res->type = type;
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 126040ca05f1..307925fb78e3 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -1258,7 +1258,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
i2c->auto_restart = i2c->dev_comp->auto_restart;
/* checking if we can skip restart and optimize using WRRD mode */
- if (i2c->auto_restart && num == 2) {
+ if (num == 2) {
if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
msgs[0].addr == msgs[1].addr) {
i2c->auto_restart = 0;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 9480d1a51c11..e88d3efb967b 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -5270,7 +5270,7 @@ static int cma_netevent_callback(struct notifier_block *self,
list_for_each_entry(current_id, &ips_node->id_list, id_list_entry) {
if (!memcmp(current_id->id.route.addr.dev_addr.dst_dev_addr,
- neigh->ha, ETH_ALEN))
+ neigh->ha, neigh->dev->addr_len))
continue;
cma_id_get(current_id);
if (!queue_work(cma_wq, &current_id->id.net_work))
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 8d19613179e3..e0b3b36b8b14 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -2031,6 +2031,24 @@ void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr)
change_mad_state(mad_send_wr, IB_MAD_STATE_EARLY_RESP);
}
+static bool is_kernel_rmpp_data_response(struct ib_mad_agent_private *agent,
+ struct ib_mad_recv_wc *mad_recv_wc)
+{
+ const struct ib_mad_hdr *mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr;
+ struct ib_rmpp_mad *rmpp_mad;
+
+ if (!ib_mad_kernel_rmpp_agent(&agent->agent) ||
+ !ib_response_mad(mad_hdr) ||
+ !ib_is_mad_class_rmpp(mad_hdr->mgmt_class))
+ return false;
+
+ rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
+
+ return (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
+ IB_MGMT_RMPP_FLAG_ACTIVE) &&
+ rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA;
+}
+
static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
struct ib_mad_recv_wc *mad_recv_wc)
{
@@ -2050,6 +2068,18 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
}
list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
+ if (is_kernel_rmpp_data_response(mad_agent_priv, mad_recv_wc)) {
+ spin_lock_irqsave(&mad_agent_priv->lock, flags);
+ mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
+ spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+ if (!mad_send_wr) {
+ ib_free_recv_mad(mad_recv_wc);
+ deref_mad_agent(mad_agent_priv);
+ return;
+ }
+ }
+
if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) {
mad_recv_wc = ib_process_rmpp_recv_wc(mad_agent_priv,
mad_recv_wc);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 3b613b57e269..86811d31092c 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -2196,13 +2196,13 @@ struct ib_cq *__ib_create_cq(struct ib_device *device,
struct ib_cq *cq;
int ret;
+ if (WARN_ON_ONCE(!cq_attr->cqe))
+ return ERR_PTR(-EINVAL);
+
cq = rdma_zalloc_drv_obj(device, ib_cq);
if (!cq)
return ERR_PTR(-ENOMEM);
- if (WARN_ON_ONCE(!cq_attr->cqe))
- return ERR_PTR(-EINVAL);
-
cq->device = device;
cq->comp_handler = comp_handler;
cq->event_handler = event_handler;
diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c
index 25f6c49aec77..e002343832f7 100644
--- a/drivers/infiniband/hw/erdma/erdma_qp.c
+++ b/drivers/infiniband/hw/erdma/erdma_qp.c
@@ -734,7 +734,7 @@ int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr,
const struct ib_recv_wr *wr = recv_wr;
struct erdma_qp *qp = to_eqp(ibqp);
unsigned long flags;
- int ret;
+ int ret = 0;
spin_lock_irqsave(&qp->lock, flags);
diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c
index 7041a8e9134b..92edec4fa61b 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.c
@@ -836,7 +836,7 @@ static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev,
mhop.bt_chunk_size;
for (i = 0; i < table->num_hem; ++i) {
- obj = i * buf_chunk_size / table->obj_size;
+ obj = (u64)i * buf_chunk_size / table->obj_size;
if (table->hem[i])
hns_roce_table_mhop_put(hr_dev, table, obj, 0);
}
diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index cb54c7c8fcd8..b7388b41ed95 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -633,17 +633,16 @@ static int irdma_setup_umode_qp(struct ib_udata *udata,
iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx;
iwqp->user_mode = 1;
- if (req.user_wqe_bufs) {
- spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
- iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs,
- &ucontext->qp_reg_mem_list);
- spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
- if (!iwqp->iwpbl) {
- ret = -ENODATA;
- ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n");
- return ret;
- }
+ spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
+ iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs,
+ &ucontext->qp_reg_mem_list);
+ spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
+
+ if (!iwqp->iwpbl) {
+ ret = -ENODATA;
+ ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n");
+ return ret;
}
if (!ucontext->use_raw_attrs) {
@@ -2802,7 +2801,7 @@ static bool irdma_check_mem_contiguous(u64 *arr, u32 npages, u32 pg_size)
u32 pg_idx;
for (pg_idx = 0; pg_idx < npages; pg_idx++) {
- if ((*arr + (pg_size * pg_idx)) != arr[pg_idx])
+ if ((*arr + ((u64)pg_size * pg_idx)) != arr[pg_idx])
return false;
}
@@ -2835,7 +2834,7 @@ static bool irdma_check_mr_contiguous(struct irdma_pble_alloc *palloc,
for (i = 0; i < lvl2->leaf_cnt; i++, leaf++) {
arr = leaf->addr;
- if ((*start_addr + (i * pg_size * PBLE_PER_PAGE)) != *arr)
+ if ((*start_addr + ((u64)i * pg_size * PBLE_PER_PAGE)) != *arr)
return false;
ret = irdma_check_mem_contiguous(arr, leaf->cnt, pg_size);
if (!ret)
@@ -3791,6 +3790,9 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags,
if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS))
return ERR_PTR(-EOPNOTSUPP);
+ if (iwmr->type != IRDMA_MEMREG_TYPE_MEM)
+ return ERR_PTR(-EINVAL);
+
ret = ib_umem_check_rereg(iwmr->region, flags, new_access);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/infiniband/hw/mana/wr.c b/drivers/infiniband/hw/mana/wr.c
index 1813567d3b16..36a1d506f08f 100644
--- a/drivers/infiniband/hw/mana/wr.c
+++ b/drivers/infiniband/hw/mana/wr.c
@@ -144,7 +144,7 @@ static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr
int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
- int err;
+ int err = 0;
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
for (; wr; wr = wr->next) {
diff --git a/drivers/infiniband/hw/mlx5/wr.c b/drivers/infiniband/hw/mlx5/wr.c
index 9947feb7fb8a..fca9e1d9d5e9 100644
--- a/drivers/infiniband/hw/mlx5/wr.c
+++ b/drivers/infiniband/hw/mlx5/wr.c
@@ -840,13 +840,15 @@ out:
return err;
}
-static int handle_reg_mr_integrity(struct mlx5_ib_dev *dev,
- struct mlx5_ib_qp *qp,
- const struct ib_send_wr *wr,
- struct mlx5_wqe_ctrl_seg **ctrl, void **seg,
- int *size, void **cur_edge,
- unsigned int *idx, int nreq, u8 fence,
- u8 next_fence)
+static noinline_for_stack int handle_reg_mr_integrity(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_qp *qp,
+ const struct ib_send_wr *wr,
+ struct mlx5_wqe_ctrl_seg **ctrl,
+ void **seg,
+ int *size, void **cur_edge,
+ unsigned int *idx, int nreq,
+ u8 fence,
+ u8 next_fence)
{
struct mlx5_ib_mr *mr;
struct mlx5_ib_mr *pi_mr;
diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c
index b74ac85c1b8b..2349c08c32ed 100644
--- a/drivers/infiniband/sw/siw/siw_verbs.c
+++ b/drivers/infiniband/sw/siw/siw_verbs.c
@@ -318,6 +318,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
struct siw_ucontext *uctx =
rdma_udata_to_drv_context(udata, struct siw_ucontext,
base_ucontext);
+ struct siw_uresp_create_qp uresp = {};
unsigned long flags;
int num_sqe, num_rqe, rv = 0;
size_t length;
@@ -371,11 +372,6 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
spin_lock_init(&qp->rq_lock);
spin_lock_init(&qp->orq_lock);
- rv = siw_qp_add(sdev, qp);
- if (rv)
- goto err_atomic;
-
-
/* All queue indices are derived from modulo operations
* on a free running 'get' (consumer) and 'put' (producer)
* unsigned counter. Having queue sizes at power of two
@@ -393,14 +389,14 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (qp->sendq == NULL) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
if (attrs->sq_sig_type != IB_SIGNAL_REQ_WR) {
if (attrs->sq_sig_type == IB_SIGNAL_ALL_WR)
qp->attrs.flags |= SIW_SIGNAL_ALL_WR;
else {
rv = -EINVAL;
- goto err_out_xa;
+ goto err_out;
}
}
qp->pd = pd;
@@ -426,7 +422,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (qp->recvq == NULL) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
qp->attrs.rq_size = num_rqe;
}
@@ -441,11 +437,8 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
qp->attrs.state = SIW_QP_STATE_IDLE;
if (udata) {
- struct siw_uresp_create_qp uresp = {};
-
uresp.num_sqe = num_sqe;
uresp.num_rqe = num_rqe;
- uresp.qp_id = qp_id(qp);
if (qp->sendq) {
length = num_sqe * sizeof(struct siw_sqe);
@@ -454,7 +447,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
length, &uresp.sq_key);
if (!qp->sq_entry) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
}
@@ -466,9 +459,23 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (!qp->rq_entry) {
uresp.sq_key = SIW_INVAL_UOBJ_KEY;
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
}
+ }
+ qp->tx_cpu = siw_get_tx_cpu(sdev);
+ if (qp->tx_cpu < 0) {
+ rv = -EINVAL;
+ goto err_out;
+ }
+ init_completion(&qp->qp_free);
+
+ rv = siw_qp_add(sdev, qp);
+ if (rv)
+ goto err_out_tx;
+
+ if (udata) {
+ uresp.qp_id = qp_id(qp);
if (udata->outlen < sizeof(uresp)) {
rv = -EINVAL;
@@ -478,22 +485,19 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (rv)
goto err_out_xa;
}
- qp->tx_cpu = siw_get_tx_cpu(sdev);
- if (qp->tx_cpu < 0) {
- rv = -EINVAL;
- goto err_out_xa;
- }
+
INIT_LIST_HEAD(&qp->devq);
spin_lock_irqsave(&sdev->lock, flags);
list_add_tail(&qp->devq, &sdev->qp_list);
spin_unlock_irqrestore(&sdev->lock, flags);
- init_completion(&qp->qp_free);
-
return 0;
err_out_xa:
xa_erase(&sdev->qp_xa, qp_id(qp));
+err_out_tx:
+ siw_put_tx_cpu(qp->tx_cpu);
+err_out:
if (uctx) {
rdma_user_mmap_entry_remove(qp->sq_entry);
rdma_user_mmap_entry_remove(qp->rq_entry);
diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c
index a01fe313558e..ce33907bfc24 100644
--- a/drivers/memstick/core/ms_block.c
+++ b/drivers/memstick/core/ms_block.c
@@ -1338,6 +1338,10 @@ static int msb_ftl_initialize(struct msb_data *msb)
return 0;
msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE;
+ if (msb->block_count > MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) {
+ pr_err("Too many blocks: %d\n", msb->block_count);
+ return -EINVAL;
+ }
msb->logical_block_count = msb->zone_count * 496 - 2;
msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 0274e8d07660..54a923ba4f1e 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2715,7 +2715,6 @@ static void mmc_blk_rpmb_device_release(struct device *dev)
{
struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
- rpmb_dev_unregister(rpmb->rdev);
mmc_blk_put(rpmb->md);
ida_free(&mmc_rpmb_ida, rpmb->id);
kfree(rpmb);
@@ -2930,8 +2929,8 @@ out_put_device:
}
static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb)
-
{
+ rpmb_dev_unregister(rpmb->rdev);
cdev_device_del(&rpmb->chrdev, &rpmb->dev);
put_device(&rpmb->dev);
}
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index ab38e4c45a8d..4dc16649e61d 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -318,9 +318,9 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem)
{
if (!mem)
return;
- while (mem->cnt--)
- __free_pages(mem->arr[mem->cnt].page,
- mem->arr[mem->cnt].order);
+ for (unsigned int i = 0; i < mem->cnt; i++)
+ __free_pages(mem->arr[i].page,
+ mem->arr[i].order);
kfree(mem);
}
@@ -341,6 +341,7 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
unsigned long page_cnt = 0;
unsigned long limit = nr_free_buffer_pages() >> 4;
struct mmc_test_mem *mem;
+ unsigned int idx = 0;
if (max_page_cnt > limit)
max_page_cnt = limit;
@@ -375,23 +376,26 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
goto out_free;
break;
}
- mem->arr[mem->cnt].page = page;
- mem->arr[mem->cnt].order = order;
- mem->cnt += 1;
+ mem->arr[idx].page = page;
+ mem->arr[idx].order = order;
+ idx += 1;
if (max_page_cnt <= (1UL << order))
break;
max_page_cnt -= 1UL << order;
page_cnt += 1UL << order;
- if (mem->cnt >= max_segs) {
+ if (idx >= mem->cnt) {
if (page_cnt < min_page_cnt)
goto out_free;
break;
}
}
+ mem->cnt = idx;
+
return mem;
out_free:
+ mem->cnt = idx;
mmc_test_free_mem(mem);
return NULL;
}
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 18ecddd6df6f..18f4905c15b9 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1326,19 +1326,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
- if (IS_ERR(imx_data->pinctrl) ||
- IS_ERR(imx_data->pins_100mhz) ||
- IS_ERR(imx_data->pins_200mhz))
+ if (IS_ERR(imx_data->pinctrl))
return -EINVAL;
switch (uhs) {
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_DDR50:
+ if (IS_ERR(imx_data->pins_100mhz))
+ return -EINVAL;
pinctrl = imx_data->pins_100mhz;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
case MMC_TIMING_MMC_HS400:
+ if (IS_ERR(imx_data->pins_200mhz))
+ return -EINVAL;
pinctrl = imx_data->pins_200mhz;
break;
default:
@@ -1349,6 +1351,23 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
}
+static void esdhc_set_dll_override(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+ u32 v;
+
+ if (!boarddata->delay_line)
+ return;
+
+ v = boarddata->delay_line << ESDHC_DLL_OVERRIDE_VAL_SHIFT |
+ (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
+ if (is_imx53_esdhc(imx_data))
+ v <<= 1;
+ writel(v, host->ioaddr + ESDHC_DLL_CTRL);
+}
+
/*
* For HS400 eMMC, there is a data_strobe line. This signal is generated
* by the device and used for data output and CRC status response output
@@ -1404,7 +1423,6 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
u32 m;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- struct esdhc_platform_data *boarddata = &imx_data->boarddata;
/* disable ddr mode and disable HS400 mode */
m = readl(host->ioaddr + ESDHC_MIX_CTRL);
@@ -1425,15 +1443,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
m |= ESDHC_MIX_CTRL_DDREN;
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
imx_data->is_ddr = 1;
- if (boarddata->delay_line) {
- u32 v;
- v = boarddata->delay_line <<
- ESDHC_DLL_OVERRIDE_VAL_SHIFT |
- (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
- if (is_imx53_esdhc(imx_data))
- v <<= 1;
- writel(v, host->ioaddr + ESDHC_DLL_CTRL);
- }
+ esdhc_set_dll_override(host);
break;
case MMC_TIMING_MMC_HS400:
m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
@@ -2051,7 +2061,9 @@ static int sdhci_esdhc_suspend(struct device *dev)
* 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really
* invoke its ->runtime_resume callback (needs_force_resume = 1).
*/
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
@@ -2064,15 +2076,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
* to save the tuning delay value just in case the usdhc
* lost power during system PM.
*/
- if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
- esdhc_is_usdhc(imx_data))
+ if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data))
sdhc_esdhc_tuning_save(host);
+ /* The irqs of imx are not shared. It is safe to disable */
+ disable_irq(host->irq);
+
if (device_may_wakeup(dev)) {
- /* The irqs of imx are not shared. It is safe to disable */
- disable_irq(host->irq);
- ret = sdhci_enable_irq_wakeups(host);
- if (!ret)
+ if (!sdhci_enable_irq_wakeups(host))
dev_warn(dev, "Failed to enable irq wakeup\n");
} else {
/*
@@ -2083,12 +2094,12 @@ static int sdhci_esdhc_suspend(struct device *dev)
* other function like GPIO function to save power in PM,
* which finally block the SDIO wakeup function.
*/
- ret = pinctrl_pm_select_sleep_state(dev);
- if (ret)
- return ret;
+ if (pinctrl_pm_select_sleep_state(dev))
+ dev_warn(dev, "Failed to select sleep pinctrl state\n");
}
- ret = mmc_gpio_set_cd_wake(host->mmc, true);
+ if (mmc_gpio_set_cd_wake(host->mmc, true))
+ dev_warn(dev, "Failed to enable cd wake\n");
/*
* Make sure invoke runtime_suspend to gate off clock.
@@ -2096,7 +2107,7 @@ static int sdhci_esdhc_suspend(struct device *dev)
*/
pm_runtime_force_suspend(dev);
- return ret;
+ return 0;
}
static int sdhci_esdhc_resume(struct device *dev)
@@ -2106,31 +2117,45 @@ static int sdhci_esdhc_resume(struct device *dev)
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int ret;
- pm_runtime_force_resume(dev);
+ if (!device_may_wakeup(dev)) {
+ ret = esdhc_change_pinstate(host, host->timing);
+ if (ret)
+ dev_warn(dev, "Failed to restore pinctrl state\n");
+ }
- ret = mmc_gpio_set_cd_wake(host->mmc, false);
+ ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
+ mmc_gpio_set_cd_wake(host->mmc, false);
+
/* re-initialize hw state in case it's lost in low power mode */
sdhci_esdhc_imx_hwinit(host);
- if (host->irq_wake_enabled) {
+ if (host->irq_wake_enabled)
sdhci_disable_irq_wakeups(host);
- enable_irq(host->irq);
- }
+
+ enable_irq(host->irq);
/*
* restore the saved tuning delay value for the device which keep
* power during system PM.
*/
- if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
- esdhc_is_usdhc(imx_data))
+ if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) {
sdhc_esdhc_tuning_restore(host);
+ /*
+ * Restore DLL override for DDR modes. hwinit unconditionally
+ * clears ESDHC_DLL_CTRL, but the card is still in DDR mode.
+ */
+ if (host->timing == MMC_TIMING_UHS_DDR50 ||
+ host->timing == MMC_TIMING_MMC_DDR52)
+ esdhc_set_dll_override(host);
+ }
+
pm_runtime_put_autosuspend(dev);
- return ret;
+ return 0;
}
static int sdhci_esdhc_runtime_suspend(struct device *dev)
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index eef53455b8ee..c688f3eaf468 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -2433,13 +2433,16 @@ static int dwcmshc_probe(struct platform_device *pdev)
return err;
priv->bus_clk = devm_clk_get(dev, "bus");
- if (!IS_ERR(priv->bus_clk))
- clk_prepare_enable(priv->bus_clk);
+ if (!IS_ERR(priv->bus_clk)) {
+ err = clk_prepare_enable(priv->bus_clk);
+ if (err)
+ goto err_clk;
+ }
}
err = mmc_of_parse(host->mmc);
if (err)
- goto err_clk;
+ goto err_bus_clk;
sdhci_get_of_property(pdev);
@@ -2453,7 +2456,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
if (pltfm_data->init) {
err = pltfm_data->init(&pdev->dev, host, priv);
if (err)
- goto err_clk;
+ goto err_bus_clk;
}
#ifdef CONFIG_ACPI
@@ -2499,9 +2502,10 @@ err_setup_host:
err_rpm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
+err_bus_clk:
+ clk_disable_unprepare(priv->bus_clk);
err_clk:
clk_disable_unprepare(pltfm_host->clk);
- clk_disable_unprepare(priv->bus_clk);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
return err;
}
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 6c3cb2f1c9d3..2dae474dcd06 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -1586,7 +1586,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300,
return linear_length;
}
-static void __vub300_command_response(struct vub300_mmc_host *vub300,
+static bool __vub300_command_response(struct vub300_mmc_host *vub300,
struct mmc_command *cmd,
struct mmc_data *data, int data_length)
{
@@ -1598,17 +1598,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
msecs_to_jiffies(msec_timeout));
if (respretval == 0) { /* TIMED OUT */
/* we don't know which of "out" and "res" if any failed */
- int result;
vub300->usb_timed_out = 1;
usb_kill_urb(vub300->command_out_urb);
usb_kill_urb(vub300->command_res_urb);
cmd->error = -ETIMEDOUT;
- result = usb_lock_device_for_reset(vub300->udev,
- vub300->interface);
- if (result == 0) {
- result = usb_reset_device(vub300->udev);
- usb_unlock_device(vub300->udev);
- }
+ return true;
} else if (respretval < 0) {
/* we don't know which of "out" and "res" if any failed */
usb_kill_urb(vub300->command_out_urb);
@@ -1704,6 +1698,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
} else {
cmd->error = -EINVAL;
}
+
+ return false;
}
static void construct_request_response(struct vub300_mmc_host *vub300,
@@ -1749,6 +1745,7 @@ static void vub300_cmndwork_thread(struct work_struct *work)
struct mmc_request *req = vub300->req;
struct mmc_command *cmd = vub300->cmd;
struct mmc_data *data = vub300->data;
+ bool reset_device;
int data_length;
mutex_lock(&vub300->cmd_mutex);
init_completion(&vub300->command_complete);
@@ -1771,7 +1768,8 @@ static void vub300_cmndwork_thread(struct work_struct *work)
data_length = __command_read_data(vub300, cmd, data);
else
data_length = __command_write_data(vub300, cmd, data);
- __vub300_command_response(vub300, cmd, data, data_length);
+ reset_device = __vub300_command_response(vub300, cmd,
+ data, data_length);
vub300->req = NULL;
vub300->cmd = NULL;
vub300->data = NULL;
@@ -1779,6 +1777,16 @@ static void vub300_cmndwork_thread(struct work_struct *work)
if (cmd->error == -ENOMEDIUM)
check_vub300_port_status(vub300);
mutex_unlock(&vub300->cmd_mutex);
+ if (reset_device) {
+ int result;
+
+ result = usb_lock_device_for_reset(vub300->udev,
+ vub300->interface);
+ if (result == 0) {
+ result = usb_reset_device(vub300->udev);
+ usb_unlock_device(vub300->udev);
+ }
+ }
mmc_request_done(vub300->mmc, req);
kref_put(&vub300->kref, vub300_delete);
return;
@@ -2336,12 +2344,16 @@ static int vub300_probe(struct usb_interface *interface,
interface_to_InterfaceNumber(interface));
retval = mmc_add_host(mmc);
if (retval)
- goto err_delete_timer;
+ goto err_stop_io;
return 0;
-err_delete_timer:
- timer_delete_sync(&vub300->inactivity_timer);
+err_stop_io:
+ vub300->interface = NULL;
+ kref_put(&vub300->kref, vub300_delete);
+
+ return retval;
+
err_free_host:
mmc_free_host(mmc);
/*
diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c
index cef5f9677d39..66e058df4c32 100644
--- a/drivers/mtd/devices/mchp23k256.c
+++ b/drivers/mtd/devices/mchp23k256.c
@@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi)
data = dev_get_platdata(&spi->dev);
- flash->caps = of_device_get_match_data(&spi->dev);
+ flash->caps = spi_get_device_match_data(spi);
if (!flash->caps)
flash->caps = &mchp23k256_caps;
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index dce5e67ce3c2..f447902d707e 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -277,6 +277,12 @@ config MTD_PCMCIA_ANONYMOUS
If unsure, say N.
+config MTD_UCLINUX
+ bool "Generic uClinux RAM/ROM filesystem support"
+ depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
+ help
+ Map driver to support image based filesystems for uClinux.
+
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
select MTD_RAM
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index fbed278157f6..01745eca1f73 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
+obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
new file mode 100644
index 000000000000..de4c46318abb
--- /dev/null
+++ b/drivers/mtd/maps/uclinux.c
@@ -0,0 +1,118 @@
+/****************************************************************************/
+
+/*
+ * uclinux.c -- generic memory mapped MTD driver for uclinux
+ *
+ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+ * License: GPL
+ */
+
+/****************************************************************************/
+
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+/****************************************************************************/
+
+#ifdef CONFIG_MTD_ROM
+#define MAP_NAME "rom"
+#else
+#define MAP_NAME "ram"
+#endif
+
+static struct map_info uclinux_ram_map = {
+ .name = MAP_NAME,
+ .size = 0,
+};
+
+static unsigned long physaddr = -1;
+module_param(physaddr, ulong, S_IRUGO);
+
+static struct mtd_info *uclinux_ram_mtdinfo;
+
+/****************************************************************************/
+
+static const struct mtd_partition uclinux_romfs[] = {
+ { .name = "ROMfs" }
+};
+
+#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs)
+
+/****************************************************************************/
+
+static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void **virt, resource_size_t *phys)
+{
+ struct map_info *map = mtd->priv;
+ *virt = map->virt + from;
+ if (phys)
+ *phys = map->phys + from;
+ *retlen = len;
+ return(0);
+}
+
+/****************************************************************************/
+
+static int __init uclinux_mtd_init(void)
+{
+ struct mtd_info *mtd;
+ struct map_info *mapp;
+
+ mapp = &uclinux_ram_map;
+
+ if (physaddr == -1)
+ mapp->phys = (resource_size_t)__bss_stop;
+ else
+ mapp->phys = physaddr;
+
+ if (!mapp->size)
+ mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
+ mapp->bankwidth = 4;
+
+ printk("uclinux[mtd]: probe address=0x%x size=0x%x\n",
+ (int) mapp->phys, (int) mapp->size);
+
+ /*
+ * The filesystem is guaranteed to be in direct mapped memory. It is
+ * directly following the kernels own bss region. Following the same
+ * mechanism used by architectures setting up traditional initrds we
+ * use phys_to_virt to get the virtual address of its start.
+ */
+ mapp->virt = phys_to_virt(mapp->phys);
+
+ if (mapp->virt == 0) {
+ printk("uclinux[mtd]: no virtual mapping?\n");
+ return(-EIO);
+ }
+
+ simple_map_init(mapp);
+
+ mtd = do_map_probe("map_" MAP_NAME, mapp);
+ if (!mtd) {
+ printk("uclinux[mtd]: failed to find a mapping?\n");
+ return(-ENXIO);
+ }
+
+ mtd->owner = THIS_MODULE;
+ mtd->_point = uclinux_point;
+ mtd->priv = mapp;
+
+ uclinux_ram_mtdinfo = mtd;
+ mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS);
+
+ return(0);
+}
+device_initcall(uclinux_mtd_init);
+
+/****************************************************************************/
diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c
index 37075ead0f33..da4277ced4d6 100644
--- a/drivers/mtd/mtd_virt_concat.c
+++ b/drivers/mtd/mtd_virt_concat.c
@@ -75,8 +75,8 @@ void mtd_virt_concat_destroy_joins(void)
if (item->concat) {
mtd_device_unregister(mtd);
kfree(mtd->name);
- mtd_concat_destroy(mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
+ mtd_concat_destroy(mtd);
}
}
}
@@ -126,8 +126,8 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd)
if (concat->mtd.name) {
del_mtd_device(&concat->mtd);
kfree(concat->mtd.name);
- mtd_concat_destroy(&concat->mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
+ mtd_concat_destroy(&concat->mtd);
}
for (idx = 0; idx < item->count; idx++)
@@ -321,8 +321,10 @@ int mtd_virt_concat_create_join(void)
if (concat->mtd.name) {
ret = memcmp(concat->mtd.name, name, name_sz);
- if (ret == 0)
+ if (ret == 0) {
+ kfree(name);
continue;
+ }
}
mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name);
if (!mtd) {
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 576537774628..16629382a787 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -105,6 +105,15 @@ static void mtd_release(struct device *dev)
device_destroy(&mtd_class, index + 1);
}
+/*
+ * No-op device release used in add_mtd_device() error paths.
+ * Prevents mtd_release() from being called via device_release(),
+ * which would free the mtd_info that the caller still manages.
+ */
+static void mtd_dev_release_nop(struct device *dev)
+{
+}
+
static void mtd_device_release(struct kref *kref)
{
struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
@@ -799,10 +808,8 @@ int add_mtd_device(struct mtd_info *mtd)
mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev);
- if (error) {
- put_device(&mtd->dev);
+ if (error)
goto fail_added;
- }
/* Add the nvmem provider */
error = mtd_nvmem_add(mtd);
@@ -840,8 +847,16 @@ int add_mtd_device(struct mtd_info *mtd)
return 0;
fail_nvmem_add:
- device_unregister(&mtd->dev);
+ device_del(&mtd->dev);
fail_added:
+ /*
+ * Clear type and set nop release to prevent mtd_release() ->
+ * release_mtd_partition() -> free_partition() from freeing mtd.
+ * The caller handles cleanup on failure.
+ */
+ mtd->dev.type = NULL;
+ mtd->dev.release = mtd_dev_release_nop;
+ put_device(&mtd->dev);
of_node_put(mtd_get_of_node(mtd));
fail_devname:
idr_remove(&mtd_idr, i);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 795a94e6b482..4b41550fd374 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -118,6 +118,9 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
part->name, parent_size - child->part.offset,
child->part.size);
/* register to preserve ordering */
+ child->part.offset = 0;
+ child->part.size = 0;
+ child->erasesize = parent->erasesize;
goto out_register;
}
}
@@ -264,6 +267,11 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
if (length <= 0)
return -EINVAL;
+ if (offset < 0 || offset >= (long long)parent_size)
+ return -EINVAL;
+
+ if ((u64)offset + (u64)length > parent_size)
+ return -EINVAL;
memset(&part, 0, sizeof(part));
part.name = name;
part.size = length;
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index 866933fc8426..f33f753f0a9f 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -125,6 +125,7 @@ struct mtdswap_dev {
char *page_buf;
char *oob_buf;
+ struct dentry *debugfs_stats;
};
struct mtdswap_oobdata {
@@ -1262,7 +1263,8 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d)
if (IS_ERR_OR_NULL(root))
return -1;
- debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops);
+ d->debugfs_stats = debugfs_create_file("mtdswap_stats", 0400, root,
+ d, &mtdswap_fops);
return 0;
}
@@ -1463,6 +1465,7 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
{
struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+ debugfs_remove(d->debugfs_stats);
del_mtd_blktrans_dev(dev);
mtdswap_cleanup(d);
kfree(d);
diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c
index c75bb8b80cc1..66f0985ef7cd 100644
--- a/drivers/mtd/nand/ecc-mtk.c
+++ b/drivers/mtd/nand/ecc-mtk.c
@@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = {
[ECC_DECIRQ_STA] = 0x144,
};
-static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
- enum mtk_ecc_operation op)
+static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+ enum mtk_ecc_operation op)
{
struct device *dev = ecc->dev;
u32 val;
@@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
if (ret)
dev_warn(dev, "%s NOT idle\n",
op == ECC_ENCODE ? "encoder" : "decoder");
+
+ return ret;
}
static irqreturn_t mtk_ecc_irq(int irq, void *id)
@@ -265,6 +267,7 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
{
struct platform_device *pdev;
struct mtk_ecc *ecc;
+ int ret;
pdev = of_find_device_by_node(np);
if (!pdev)
@@ -276,7 +279,12 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
return ERR_PTR(-EPROBE_DEFER);
}
- clk_prepare_enable(ecc->clk);
+ ret = clk_prepare_enable(ecc->clk);
+ if (ret) {
+ put_device(&pdev->dev);
+ return ERR_PTR(ret);
+ }
+
mtk_ecc_hw_init(ecc);
return ecc;
@@ -312,7 +320,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
return ret;
}
- mtk_ecc_wait_idle(ecc, op);
+ ret = mtk_ecc_wait_idle(ecc, op);
+ if (ret) {
+ mutex_unlock(&ecc->lock);
+ return ret;
+ }
ret = mtk_ecc_config(ecc, config);
if (ret) {
@@ -412,7 +424,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
if (ret)
goto timeout;
- mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+ ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+ if (ret)
+ goto timeout;
/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c
index 6d6aa709a21f..b7b7758ce4d8 100644
--- a/drivers/mtd/nand/onenand/onenand_samsung.c
+++ b/drivers/mtd/nand/onenand/onenand_samsung.c
@@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
time_before(jiffies, timeout));
+ if (!(status & S5PC110_DMA_TRANS_STATUS_TD))
+ return -ETIMEDOUT;
+
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
return 0;
@@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
- wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
+ if (!wait_for_completion_timeout(&onenand->complete,
+ msecs_to_jiffies(20)))
+ return -ETIMEDOUT;
return 0;
}
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 64b8b99a3a68..1f4053e531fd 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -72,6 +72,7 @@ config MTD_NAND_AU1550
config MTD_NAND_NDFC
tristate "IBM/MCC 4xx NAND controller"
depends on 44x || COMPILE_TEST
+ depends on OF
select MTD_NAND_ECC_SW_HAMMING
select MTD_NAND_ECC_SW_HAMMING_SMC
help
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index fad0334f759d..a88ac2cfaccd 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -684,8 +684,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf,
return check_erased_page(chip, buf);
}
- if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
+ if (!ctrl->nand_stat) {
mtd->ecc_stats.failed++;
+ return -ETIMEDOUT;
+ }
+
+ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) {
+ mtd->ecc_stats.failed++;
+ return -EIO;
+ }
return nctrl->max_bitflips;
}
diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
index 525c34c281b6..beb033705cf3 100644
--- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
+++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
@@ -67,6 +67,7 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
{
struct platform_device *pdev;
struct ingenic_ecc *ecc;
+ int ret;
pdev = of_find_device_by_node(np);
if (!pdev)
@@ -78,7 +79,11 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
}
ecc = platform_get_drvdata(pdev);
- clk_prepare_enable(ecc->clk);
+ ret = clk_prepare_enable(ecc->clk);
+ if (ret) {
+ put_device(&pdev->dev);
+ return ERR_PTR(ret);
+ }
return ecc;
}
diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index 19b13ae536d4..8f6a89d9ba83 100644
--- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
@@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ unsigned long time_left;
int res;
sg_init_one(&host->sgl, mem, len);
@@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
flags);
if (!desc) {
dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
+ res = -ENXIO;
goto out1;
}
@@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
- wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
+ time_left = wait_for_completion_timeout(&host->comp_dma,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dmaengine_terminate_sync(host->dma_chan);
+ res = -ETIMEDOUT;
+ goto out1;
+ }
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
@@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
- return -ENXIO;
+ return res;
}
static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 3ca30e7dce33..10c8080207f4 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
+ unsigned long time_left;
host->dma_slave_config.direction = dir;
host->dma_slave_config.src_addr = dma;
@@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
- wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000));
+ time_left = wait_for_completion_timeout(&host->comp,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dmaengine_terminate_sync(host->dma_chan);
+ res = -ETIMEDOUT;
+ } else {
+ res = 0;
+ }
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
- return 0;
+ return res;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index a937ca3eeff5..a48274297d3b 100644
--- a/drivers/mtd/nand/raw/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
@@ -188,7 +188,7 @@ static int ndfc_probe(struct platform_device *ofdev)
const __be32 *reg;
u32 ccr;
u32 cs;
- int err, len;
+ int err, len = 0;
/* Read the reg property to get the chip select */
reg = of_get_property(ofdev->dev.of_node, "reg", &len);
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index f86786344d52..74bb5ee83b31 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -822,7 +822,7 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
bool disable_ecc = false;
bool ecc_failed = false;
unsigned int retry_mode = 0;
- int ret;
+ int ret = 0;
old_stats = mtd->ecc_stats;
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index e4058708ae68..a8fad6fe5302 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -40,11 +40,8 @@ config CAN_VXCAN
When one end receives the packet it appears on its pair and vice
versa. The vxcan can be used for cross namespace communication.
- In opposite to vcan loopback devices the vxcan only forwards CAN
- frames to its pair and does *not* provide a local echo of sent
- CAN frames. To disable a potential echo in af_can.c the vxcan driver
- announces IFF_ECHO in the interface flags. To have a clean start
- in each namespace the CAN GW hop counter is set to zero.
+ To have a clean start in each namespace the CAN GW hop counter is
+ set to zero.
This driver can also be built as a module. If so, the module
will be called vxcan.
diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c
index 06cb2629f66a..4fd1aefb780f 100644
--- a/drivers/net/can/peak_canfd/peak_canfd.c
+++ b/drivers/net/can/peak_canfd/peak_canfd.c
@@ -2,7 +2,7 @@
/* Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com>
*
* Copyright (C) 2016-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/can.h>
diff --git a/drivers/net/can/peak_canfd/peak_canfd_user.h b/drivers/net/can/peak_canfd/peak_canfd_user.h
index 60c6542028cf..dc0ecb566a85 100644
--- a/drivers/net/can/peak_canfd/peak_canfd_user.h
+++ b/drivers/net/can/peak_canfd/peak_canfd_user.h
@@ -2,7 +2,7 @@
/* CAN driver for PEAK System micro-CAN based adapters
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#ifndef PEAK_CANFD_USER_H
#define PEAK_CANFD_USER_H
diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c
index 93558e33bc02..7c749301ea84 100644
--- a/drivers/net/can/peak_canfd/peak_pciefd_main.c
+++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_pci.c:
*
* Copyright (C) 2001-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/kernel.h>
@@ -19,7 +19,7 @@
#include "peak_canfd_user.h"
-MODULE_AUTHOR("Stéphane Grosjean <stephane.grosjean@hms-networks.com>");
+MODULE_AUTHOR("Stéphane Grosjean <s.grosjean@peak-system.fr>");
MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe/M.2 FD family cards");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
index 4cc4a1581dd1..69c61ccf621d 100644
--- a/drivers/net/can/sja1000/peak_pci.c
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -5,7 +5,7 @@
* Derived from the PCAN project file driver/src/pcan_pci.c:
*
* Copyright (C) 2001-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/kernel.h>
@@ -22,7 +22,7 @@
#include "sja1000.h"
-MODULE_AUTHOR("Stéphane Grosjean <stephane.grosjean@hms-networks.com>");
+MODULE_AUTHOR("Stéphane Grosjean <s.grosjean@peak-system.fr>");
MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c
index 42a77d435b39..c3c2aa21da47 100644
--- a/drivers/net/can/sja1000/peak_pcmcia.c
+++ b/drivers/net/can/sja1000/peak_pcmcia.c
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_pccard.c
*
* Copyright (C) 2006-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/kernel.h>
#include <linux/module.h>
@@ -19,7 +19,7 @@
#include <linux/can/dev.h>
#include "sja1000.h"
-MODULE_AUTHOR("Stéphane Grosjean <stephane.grosjean@hms-networks.com>");
+MODULE_AUTHOR("Stéphane Grosjean <s.grosjean@peak-system.fr>");
MODULE_DESCRIPTION("CAN driver for PEAK-System PCAN-PC Cards");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index d257440fa01f..f41d4a0d140f 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -1390,10 +1390,13 @@ static void esd_usb_disconnect(struct usb_interface *intf)
netdev = dev->nets[i]->netdev;
netdev_info(netdev, "unregister\n");
unregister_netdev(netdev);
- free_candev(netdev);
}
}
unlink_all_urbs(dev);
+ for (i = 0; i < dev->net_count; i++) {
+ if (dev->nets[i])
+ free_candev(dev->nets[i]->netdev);
+ }
kfree(dev);
}
}
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 9278a1522aae..8fd058c32856 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_usb.c
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*
* Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
*/
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index cf48bb26d46d..c7933d1acc99 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_usb_core.c
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*
* Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
*/
@@ -24,7 +24,7 @@
#include "pcan_usb_core.h"
-MODULE_AUTHOR("Stéphane Grosjean <stephane.grosjean@hms-networks.com>");
+MODULE_AUTHOR("Stéphane Grosjean <s.grosjean@peak-system.fr>");
MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
index d1c1897d47b9..65999f04f4b7 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_usb_core.c
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*
* Many thanks to Klaus Hitschler <klaus.hitschler@gmx.de>
*/
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
index eb4f5884ad73..ef9fd693e9bd 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -3,7 +3,7 @@
* CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter
*
* Copyright (C) 2013-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/ethtool.h>
#include <linux/module.h>
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index 4bfa8d0fbb32..aefcded8e12a 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_usbpro.c
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#include <linux/ethtool.h>
#include <linux/module.h>
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
index 162c7546d3a8..d669c9e610c7 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
@@ -4,7 +4,7 @@
* Derived from the PCAN project file driver/src/pcan_usbpro_fw.h
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#ifndef PCAN_USB_PRO_H
#define PCAN_USB_PRO_H
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index 5c751933da6a..a515c368bac0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -566,6 +566,18 @@ void bnxt_aux_devices_init(struct bnxt *bp)
if (!aux_priv)
goto next_auxdev;
+ edev = kzalloc_obj(*edev);
+ if (!edev)
+ goto aux_priv_free;
+ aux_priv->edev = edev;
+ bnxt_set_edev_info(edev, bp);
+
+ ulp = kzalloc_obj(*ulp);
+ if (!ulp)
+ goto edev_free;
+ edev->ulp_tbl = ulp;
+ aux_priv->id = idx;
+
aux_dev = &aux_priv->aux_dev;
aux_dev->id = bp->auxdev_id;
aux_dev->name = bnxt_aux_devices[idx].name;
@@ -573,37 +585,26 @@ void bnxt_aux_devices_init(struct bnxt *bp)
aux_dev->dev.release = bnxt_aux_dev_release;
rc = auxiliary_device_init(aux_dev);
- if (rc) {
- kfree(aux_priv);
- goto next_auxdev;
- }
+ if (rc)
+ goto ulp_free;
bp->aux_priv[idx] = aux_priv;
/* From this point, all cleanup will happen via the .release
* callback & any error unwinding will need to include a call
* to auxiliary_device_uninit.
*/
- edev = kzalloc_obj(*edev);
- if (!edev)
- goto aux_dev_uninit;
-
- aux_priv->edev = edev;
- bnxt_set_edev_info(edev, bp);
-
- ulp = kzalloc_obj(*ulp);
- if (!ulp)
- goto aux_dev_uninit;
-
- edev->ulp_tbl = ulp;
bp->edev[idx] = edev;
if (idx == BNXT_AUXDEV_RDMA)
bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp);
- aux_priv->id = idx;
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT);
continue;
-aux_dev_uninit:
- auxiliary_device_uninit(aux_dev);
+ulp_free:
+ kfree(ulp);
+edev_free:
+ kfree(edev);
+aux_priv_free:
+ kfree(aux_priv);
next_auxdev:
if (idx == BNXT_AUXDEV_RDMA)
bp->flags &= ~BNXT_FLAG_ROCE_CAP;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
index 997be91f0a13..7cedc348790d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
@@ -175,6 +175,7 @@ int mlx5_st_dealloc_index(struct mlx5_core_dev *dev, u16 st_index)
if (refcount_dec_and_test(&idx_data->usecount)) {
xa_erase(&st->idx_xa, st_index);
+ kfree(idx_data);
/* We leave PCI config space as was before, no mkey will refer to it */
}
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index dd89282f0179..ee0e2eb7dbc6 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -3615,19 +3615,22 @@ static int macsec_dev_open(struct net_device *dev)
ops = macsec_get_ops(netdev_priv(dev), &ctx);
if (!ops) {
err = -EOPNOTSUPP;
- goto clear_allmulti;
+ goto clear_promisc;
}
ctx.secy = &macsec->secy;
err = macsec_offload(ops->mdo_dev_open, &ctx);
if (err)
- goto clear_allmulti;
+ goto clear_promisc;
}
if (netif_carrier_ok(real_dev))
netif_carrier_on(dev);
return 0;
+clear_promisc:
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(real_dev, -1);
clear_allmulti:
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(real_dev, -1);
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 57c68efa5ff8..717c1d3aa953 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -184,6 +184,7 @@ struct channel {
struct list_head clist; /* link in list of channels per unit */
spinlock_t upl; /* protects `ppp' and 'bridge' */
struct channel __rcu *bridge; /* "bridged" ppp channel */
+ struct rcu_head rcu; /* for RCU-deferred free of the channel */
#ifdef CONFIG_PPP_MULTILINK
u8 avail; /* flag used in multilink stuff */
u8 had_frag; /* >= 1 fragments have been sent */
@@ -3562,6 +3563,18 @@ ppp_disconnect_channel(struct channel *pch)
return err;
}
+/* Purge after the grace period: a late ppp_input() may still queue an
+ * skb on pch->file.rq before the last RCU reader drains.
+ */
+static void ppp_release_channel_free(struct rcu_head *rcu)
+{
+ struct channel *pch = container_of(rcu, struct channel, rcu);
+
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ kfree(pch);
+}
+
/*
* Drop a reference to a ppp channel and free its memory if the refcount reaches
* zero.
@@ -3581,9 +3594,7 @@ static void ppp_release_channel(struct channel *pch)
pr_err("ppp: destroying undead channel %p !\n", pch);
return;
}
- skb_queue_purge(&pch->file.xq);
- skb_queue_purge(&pch->file.rq);
- kfree(pch);
+ call_rcu(&pch->rcu, ppp_release_channel_free);
}
static void __exit ppp_cleanup(void)
@@ -3596,6 +3607,7 @@ static void __exit ppp_cleanup(void)
device_destroy(&ppp_class, MKDEV(PPP_MAJOR, 0));
class_unregister(&ppp_class);
unregister_pernet_device(&ppp_net_ops);
+ rcu_barrier(); /* wait for RCU callbacks before module unload */
}
/*
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
index ce09d44fa73c..873754be5174 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
@@ -293,6 +293,12 @@ brcmf_notify_auth_frame_rx(struct brcmf_if *ifp,
return -EINVAL;
}
+ if (mgmt_frame_len < offsetof(struct ieee80211_mgmt, u)) {
+ bphy_err(drvr, "Event %s (%d) frame too small. Ignore\n",
+ brcmf_fweh_event_name(e->event_code), e->event_code);
+ return -EINVAL;
+ }
+
wdev = &ifp->vif->wdev;
WARN_ON(!wdev);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 8fb595733b9c..b725c64e5b5c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -4465,6 +4465,7 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->sdiodev = sdiodev;
sdiodev->bus = bus;
skb_queue_head_init(&bus->glom);
+ INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->txbound = BRCMF_TXBOUND;
bus->rxbound = BRCMF_RXBOUND;
bus->txminmax = BRCMF_TXMINMAX;
@@ -4479,7 +4480,6 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
goto fail;
}
brcmf_sdiod_freezer_count(sdiodev);
- INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->brcmf_wq = wq;
/* attempt to attach to the dongle */
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index c11428485dcc..2b8a23865bfb 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -6157,6 +6157,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_enable_device.\n");
+ free_libipw(dev, 0);
+ pci_iounmap(pci_dev, ioaddr);
return err;
}
@@ -6169,16 +6171,14 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_set_dma_mask.\n");
- pci_disable_device(pci_dev);
- return err;
+ goto fail;
}
err = pci_request_regions(pci_dev, DRV_NAME);
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_request_regions.\n");
- pci_disable_device(pci_dev);
- return err;
+ goto fail;
}
/* We disable the RETRY_TIMEOUT register (0x41) to keep
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
index b7bc94f7abd8..c8841f9b9ad9 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
@@ -414,7 +414,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
ieee->host_mc_decrypt : ieee->host_decrypt;
if (can_be_decrypted) {
- if (skb->len >= hdrlen + 3) {
+ if (skb->len >= hdrlen + 4) {
/* Top two-bits of byte 3 are the key index */
keyidx = skb->data[hdrlen + 3] >> 6;
}
@@ -660,7 +660,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
int trimlen = 0;
/* Top two-bits of byte 3 are the key index */
- if (skb->len >= hdrlen + 3)
+ if (skb->len >= hdrlen + 4)
keyidx = skb->data[hdrlen + 3] >> 6;
/* To strip off any security data which appears before the
diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c
index 1294a1d6528e..9f491334c8d0 100644
--- a/drivers/net/wireless/intersil/p54/txrx.c
+++ b/drivers/net/wireless/intersil/p54/txrx.c
@@ -499,11 +499,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv,
if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size)
return;
+ if (eeprom->v2.data + priv->eeprom_slice_size >
+ skb_tail_pointer(skb))
+ return;
+
memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size);
} else {
if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size)
return;
+ if (eeprom->v1.data + priv->eeprom_slice_size >
+ skb_tail_pointer(skb))
+ return;
+
memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size);
}
diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c
index f124110944b7..9bf7d4c207b9 100644
--- a/drivers/net/wireless/marvell/libertas/firmware.c
+++ b/drivers/net/wireless/marvell/libertas/firmware.c
@@ -78,6 +78,7 @@ static void helper_firmware_cb(const struct firmware *firmware, void *context)
} else {
/* No main firmware needed for this helper --> success! */
lbs_fw_loaded(priv, 0, firmware, NULL);
+ release_firmware(firmware);
}
}
diff --git a/drivers/net/wireless/marvell/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c
index 27304a98787d..13d08022e414 100644
--- a/drivers/net/wireless/marvell/libertas/tx.c
+++ b/drivers/net/wireless/marvell/libertas/tx.c
@@ -117,6 +117,13 @@ netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data;
+ if (skb->len < sizeof(*rtap_hdr) + 4 + ETH_ALEN) {
+ lbs_deb_tx("tx err: short monitor frame %u\n", skb->len);
+ dev->stats.tx_dropped++;
+ dev->stats.tx_errors++;
+ goto free;
+ }
+
/* set txpd fields from the radiotap header */
txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate));
diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c
index fb20fe31cd36..42be6fa22f9c 100644
--- a/drivers/net/wireless/marvell/libertas_tf/main.c
+++ b/drivers/net/wireless/marvell/libertas_tf/main.c
@@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv)
{
lbtf_deb_enter(LBTF_DEB_MAIN);
lbtf_free_cmd_buffer(priv);
- timer_delete(&priv->command_timer);
+ timer_delete_sync(&priv->command_timer);
lbtf_deb_leave(LBTF_DEB_MAIN);
}
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index c9daf893472f..abc703441c5d 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -4334,7 +4334,7 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy,
return -EOPNOTSUPP;
}
- if (!priv->auth_flag) {
+ if (!(priv->auth_flag & HOST_MLME_AUTH_PENDING)) {
ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET,
req->bss->channel,
AUTH_TX_DEFAULT_WAIT_TIME);
diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c
index 5a1a0287c1d5..b48f7febaf03 100644
--- a/drivers/net/wireless/marvell/mwifiex/join.c
+++ b/drivers/net/wireless/marvell/mwifiex/join.c
@@ -736,7 +736,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
/* Send a Media Connected event, according to the Spec */
priv->media_connected = true;
- priv->adapter->ps_state = PS_STATE_AWAKE;
priv->adapter->pps_uapsd_mode = false;
priv->adapter->tx_lock_flag = false;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
index cac191304bf5..b846fe589c2b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
@@ -1429,7 +1429,7 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
*/
static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
{
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
u32 reg;
u16 word;
u8 *mac;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
index fc35b60e422c..be9df35acc33 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
@@ -1555,7 +1555,7 @@ static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance)
*/
static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
{
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
u32 reg;
u16 word;
u8 *mac;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
index 4fa14bb573ad..2596b9fcc7dd 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
@@ -108,7 +108,7 @@ static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
{
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
u32 reg;
reg = rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 82fb230a73bb..4d94b7062f44 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -1388,7 +1388,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
GFP_KERNEL);
if (!rt2x00dev->drv_data) {
retval = -ENOMEM;
- goto exit;
+ return retval;
}
}
@@ -1422,7 +1422,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy));
if (!rt2x00dev->workqueue) {
retval = -ENOMEM;
- goto exit;
+ goto exit_free_drv_data;
}
INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
@@ -1495,6 +1495,14 @@ exit:
rt2x00lib_remove_dev(rt2x00dev);
return retval;
+
+exit_free_drv_data:
+ clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+
+ kfree(rt2x00dev->drv_data);
+ rt2x00dev->drv_data = NULL;
+
+ return retval;
}
EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
index 79e1fd0a1fbd..d4783658b2c5 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
@@ -2298,7 +2298,7 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)
*/
static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
{
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
u32 reg;
u16 word;
u8 *mac;
diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c
index a0c36144eb0b..501071104a9f 100644
--- a/drivers/net/wireless/rsi/rsi_91x_hal.c
+++ b/drivers/net/wireless/rsi/rsi_91x_hal.c
@@ -431,6 +431,7 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb)
struct ieee80211_vif *vif;
struct sk_buff *mac_bcn;
u8 vap_id = 0, i;
+ unsigned int tailroom;
u16 tim_offset = 0;
for (i = 0; i < RSI_MAX_VIFS; i++) {
@@ -480,6 +481,13 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb)
if (mac_bcn->data[tim_offset + 2] == 0)
bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON);
+ tailroom = skb_tailroom(skb);
+ if (tailroom < FRAME_DESC_SZ ||
+ mac_bcn->len > tailroom - FRAME_DESC_SZ) {
+ dev_kfree_skb(mac_bcn);
+ return -EMSGSIZE;
+ }
+
memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len);
skb_put(skb, mac_bcn->len + FRAME_DESC_SZ);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 7f2c1608f2ce..bb167f03367b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -848,8 +848,10 @@ int rsi_hal_load_key(struct rsi_common *common,
} else {
memcpy(&set_key->key[0][0], data, key_len);
}
- memcpy(set_key->tx_mic_key, &data[16], 8);
- memcpy(set_key->rx_mic_key, &data[24], 8);
+ if (cipher == WLAN_CIPHER_SUITE_TKIP) {
+ memcpy(set_key->tx_mic_key, &data[16], 8);
+ memcpy(set_key->rx_mic_key, &data[24], 8);
+ }
} else {
memset(&set_key[FRAME_DESC_SZ], 0, frame_len - FRAME_DESC_SZ);
}
@@ -1911,6 +1913,12 @@ int rsi_send_bgscan_probe_req(struct rsi_common *common,
return -ENOMEM;
}
+ if (probereq_skb->len > MAX_BGSCAN_PROBE_REQ_LEN) {
+ dev_kfree_skb(probereq_skb);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
memcpy(&skb->data[frame_len], probereq_skb->data, probereq_skb->len);
bgscan->probe_req_length = cpu_to_le16(probereq_skb->len);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 0dd8a6c85953..956ff9b94526 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1324,6 +1324,17 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
}
}
+static struct ieee80211_rate *
+mac80211_hwsim_get_tx_rate(struct ieee80211_hw *hw,
+ struct ieee80211_tx_info *info)
+{
+ if (info->control.rates[0].flags &
+ (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
+ return NULL;
+
+ return ieee80211_get_tx_rate(hw, info);
+}
+
static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct sk_buff *tx_skb,
struct ieee80211_channel *chan)
@@ -1333,7 +1344,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct hwsim_radiotap_hdr *hdr;
u16 flags, bitrate;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb);
- struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
+ struct ieee80211_rate *txrate = mac80211_hwsim_get_tx_rate(hw, info);
if (!txrate)
bitrate = 0;
@@ -1603,7 +1614,7 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data,
spin_lock_bh(&data->tsf_offset_lock);
- txrate = ieee80211_get_tx_rate(data->hw, info);
+ txrate = mac80211_hwsim_get_tx_rate(data->hw, info);
if (txrate)
bitrate = txrate->bitrate;
@@ -7289,6 +7300,7 @@ static void hwsim_virtio_rx_work(struct work_struct *work)
skb->data = skb->head;
skb_reset_tail_pointer(skb);
+ len = min(len, skb_end_offset(skb));
skb_put(skb, len);
hwsim_virtio_handle_cmd(skb);
diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
index 19e992d2ee3b..99d100e1d923 100644
--- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
@@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data {
* register.
*/
u32 mipi_phy_rst_mask;
+
+ /*
+ * VC8000E reset de-assertion edge and AXI clock may have a timing issue.
+ * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off
+ * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to
+ * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is
+ * de-asserted by HW)
+ */
+ bool is_errata_err050531;
};
#define DOMAIN_MAX_CLKS 4
@@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
dev_err(bc->dev, "failed to enable clocks\n");
goto bus_put;
}
- regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
+ if (data->is_errata_err050531)
+ regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+ else
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
/* power up upstream GPC domain */
ret = pm_runtime_get_sync(domain->power_dev);
@@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
goto clk_disable;
}
+ if (data->is_errata_err050531)
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
/* wait for reset to propagate */
udelay(5);
@@ -511,12 +527,38 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[]
.clk_mask = BIT(2),
.path_names = (const char *[]){"vc8000e"},
.num_paths = 1,
+ .is_errata_err050531 = true,
},
};
+static int imx8mp_vpu_power_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
+ power_nb);
+
+ if (action == GENPD_NOTIFY_ON) {
+ /*
+ * On power up we have no software backchannel to the GPC to
+ * wait for the ADB handshake to happen, so we just delay for a
+ * bit. On power down the GPC driver waits for the handshake.
+ */
+
+ udelay(5);
+
+ /* set "fuse" bits to enable the VPUs */
+ regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
+ }
+
+ return NOTIFY_OK;
+}
+
static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = {
.max_reg = 0x18,
- .power_notifier_fn = imx8mm_vpu_power_notifier,
+ .power_notifier_fn = imx8mp_vpu_power_notifier,
.domains = imx8mp_vpu_blk_ctl_domain_data,
.num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data),
};
diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c
index 1afc78b034fa..243ce939ba68 100644
--- a/drivers/pmdomain/imx/imx93-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c
@@ -48,6 +48,8 @@
#define PRIO(X) (X)
+#define BLK_CTRL_NO_PARENT UINT_MAX
+
struct imx93_blk_ctrl_domain;
struct imx93_blk_ctrl {
@@ -68,12 +70,18 @@ struct imx93_blk_ctrl_qos {
u32 cfg_prio;
};
+struct imx93_blk_ctrl_subdomain_link {
+ struct generic_pm_domain *parent;
+ struct generic_pm_domain *subdomain;
+};
+
struct imx93_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
int num_clks;
u32 rst_mask;
u32 clk_mask;
+ u32 parent;
int num_qos;
struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS];
};
@@ -203,6 +211,13 @@ static void imx93_release_pm_genpd(void *data)
pm_genpd_remove(genpd);
}
+static void imx93_release_subdomain(void *data)
+{
+ struct imx93_blk_ctrl_subdomain_link *link = data;
+
+ pm_genpd_remove_subdomain(link->parent, link->subdomain);
+}
+
static struct lock_class_key blk_ctrl_genpd_lock_class;
static int imx93_blk_ctrl_probe(struct platform_device *pdev)
@@ -302,6 +317,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev)
bc->onecell_data.domains[i] = &domain->genpd;
}
+ for (i = 0; i < bc_data->num_domains; i++) {
+ struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
+ const struct imx93_blk_ctrl_domain_data *data = domain->data;
+ struct imx93_blk_ctrl_subdomain_link *link;
+
+ if (bc_data->skip_mask & BIT(i) ||
+ data->parent == BLK_CTRL_NO_PARENT)
+ continue;
+
+ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+
+ link->parent = &bc->domains[data->parent].genpd;
+ link->subdomain = &domain->genpd;
+
+ ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd,
+ &domain->genpd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add subdomain %s\n",
+ domain->genpd.name);
+
+ ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to add subdomain release callback\n");
+ }
+
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "failed to enable pm-runtime\n");
@@ -326,8 +369,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.name = "mediablk-mipi-dsi",
.clk_names = (const char *[]){ "dsi" },
.num_clks = 1,
- .rst_mask = BIT(11) | BIT(12),
- .clk_mask = BIT(11) | BIT(12),
+ .rst_mask = BIT(11),
+ .clk_mask = BIT(11),
+ .parent = IMX93_MEDIABLK_PD_MIPI_PHY,
},
[IMX93_MEDIABLK_PD_MIPI_CSI] = {
.name = "mediablk-mipi-csi",
@@ -335,6 +379,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 2,
.rst_mask = BIT(9) | BIT(10),
.clk_mask = BIT(9) | BIT(10),
+ .parent = IMX93_MEDIABLK_PD_MIPI_PHY,
},
[IMX93_MEDIABLK_PD_PXP] = {
.name = "mediablk-pxp",
@@ -342,6 +387,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 1,
.rst_mask = BIT(7) | BIT(8),
.clk_mask = BIT(7) | BIT(8),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 2,
.qos = {
{
@@ -363,6 +409,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 2,
.rst_mask = BIT(4) | BIT(5) | BIT(6),
.clk_mask = BIT(4) | BIT(5) | BIT(6),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 1,
.qos = {
{
@@ -379,6 +426,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 1,
.rst_mask = BIT(2) | BIT(3),
.clk_mask = BIT(2) | BIT(3),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 4,
.qos = {
{
@@ -404,6 +452,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
}
}
},
+ [IMX93_MEDIABLK_PD_MIPI_PHY] = {
+ .name = "mediablk-mipi-phy",
+ .clk_names = NULL,
+ .num_clks = 0,
+ .rst_mask = BIT(12),
+ .clk_mask = BIT(12),
+ .parent = BLK_CTRL_NO_PARENT,
+ },
};
static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = {
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index e1cfd4223473..f0a6339affd7 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -393,9 +393,8 @@ err_infra:
return ret;
};
-static int scpsys_hwv_power_off(struct generic_pm_domain *genpd)
+static int scpsys_hwv_power_off_internal(struct scpsys_domain *pd)
{
- struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
struct scpsys *scpsys = pd->scpsys;
u32 val;
@@ -464,6 +463,13 @@ err_infra:
return ret;
};
+static int scpsys_hwv_power_off(struct generic_pm_domain *genpd)
+{
+ struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
+
+ return scpsys_hwv_power_off_internal(pd);
+}
+
static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd)
{
struct scpsys *scpsys = pd->scpsys;
@@ -694,9 +700,8 @@ err_reg:
return ret;
}
-static int scpsys_power_off(struct generic_pm_domain *genpd)
+static int scpsys_power_off_internal(struct scpsys_domain *pd)
{
- struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
struct scpsys *scpsys = pd->scpsys;
bool tmp;
int ret;
@@ -737,6 +742,13 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
return 0;
}
+static int scpsys_power_off(struct generic_pm_domain *genpd)
+{
+ struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
+
+ return scpsys_power_off_internal(pd);
+}
+
static struct
generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node)
{
@@ -884,7 +896,14 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
* late_init time.
*/
if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) {
- if (scpsys_domain_is_on(pd))
+ bool domain_is_on;
+
+ if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER)
+ domain_is_on = scpsys_hwv_domain_is_enable_done(pd);
+ else
+ domain_is_on = scpsys_domain_is_on(pd);
+
+ if (domain_is_on)
dev_warn(scpsys->dev,
"%pOF: A default off power domain has been ON\n", node);
} else {
@@ -973,6 +992,7 @@ err_put_node:
static void scpsys_remove_one_domain(struct scpsys_domain *pd)
{
+ struct scpsys *scpsys = pd->scpsys;
int ret;
/*
@@ -984,8 +1004,14 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd)
dev_err(pd->scpsys->dev,
"failed to remove domain '%s' : %d - state may be inconsistent\n",
pd->genpd.name, ret);
- if (scpsys_domain_is_on(pd))
- scpsys_power_off(&pd->genpd);
+
+ if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER) {
+ if (scpsys_hwv_domain_is_enable_done(pd))
+ scpsys_hwv_power_off_internal(pd);
+ } else {
+ if (scpsys_domain_is_on(pd))
+ scpsys_power_off_internal(pd);
+ }
clk_bulk_put(pd->num_clks, pd->clks);
clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks);
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
index 73d511eb1c1d..768cdce0d4ec 100644
--- a/drivers/regulator/ltc3676.c
+++ b/drivers/regulator/ltc3676.c
@@ -45,11 +45,11 @@
#define LTC3676_DVBxA_REF_SELECT BIT(5)
#define LTC3676_DVBxB_PGOOD_MASK BIT(5)
-#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3)
-#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4)
-#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5)
-#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6)
-#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7)
+#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(2)
+#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(3)
+#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(4)
+#define LTC3676_IRQSTAT_THERMAL_WARN BIT(5)
+#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(6)
enum ltc3676_reg {
LTC3676_SW1,
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
index dd01fe11c5cb..a3cb8244d76a 100644
--- a/drivers/reset/reset-imx7.c
+++ b/drivers/reset/reset-imx7.c
@@ -236,6 +236,12 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev,
case IMX8MQ_RESET_PCIE_CTRL_APPS_EN:
case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN:
+ case IMX8MQ_RESET_MIPI_CSI1_CORE_RESET:
+ case IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET:
+ case IMX8MQ_RESET_MIPI_CSI1_ESC_RESET:
+ case IMX8MQ_RESET_MIPI_CSI2_CORE_RESET:
+ case IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET:
+ case IMX8MQ_RESET_MIPI_CSI2_ESC_RESET:
case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N:
case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N:
case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N:
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
index 2544de6576e4..2f6df7707cad 100644
--- a/drivers/reset/reset-sunxi.c
+++ b/drivers/reset/reset-sunxi.c
@@ -44,7 +44,7 @@ static int sunxi_reset_init(struct device_node *np)
data->membase = ioremap(res.start, size);
if (!data->membase) {
ret = -ENOMEM;
- goto err_alloc;
+ goto err_mem_region;
}
spin_lock_init(&data->lock);
@@ -57,6 +57,8 @@ static int sunxi_reset_init(struct device_node *np)
return reset_controller_register(&data->rcdev);
+err_mem_region:
+ release_mem_region(res.start, size);
err_alloc:
kfree(data);
return ret;
diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c
index 9841f5e057b2..2e87f320cf11 100644
--- a/drivers/reset/spacemit/reset-spacemit-k3.c
+++ b/drivers/reset/spacemit/reset-spacemit-k3.c
@@ -112,7 +112,7 @@ static const struct ccu_reset_data k3_apmu_resets[] = {
[RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
[RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
[RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
- [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)),
[RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)),
[RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)),
[RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)),
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index 2df399c537c1..8c9d423129c0 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -2627,7 +2627,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
}
-static void
+static noinline_for_stack void
bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr)
{
diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
index 1838032f6486..b79c6a7ea791 100644
--- a/drivers/scsi/elx/efct/efct_hw.c
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -1997,6 +1997,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort);
if (!wqcb) {
efc_log_err(hw->os, "can't allocate request tag\n");
+ io_to_abort->abort_in_progress = false;
+ kref_put(&io_to_abort->ref, io_to_abort->release);
return -ENOSPC;
}
diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c
index e6addab66a60..6a871a59c909 100644
--- a/drivers/scsi/elx/efct/efct_unsol.c
+++ b/drivers/scsi/elx/efct/efct_unsol.c
@@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
efc_log_err(efct, "Additional CDB not supported\n");
+ efct_scsi_io_free(io);
return -EIO;
}
/*
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e047747d4ecf..46cc8e3c79a2 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -357,6 +357,7 @@ static void scsi_host_dev_release(struct device *dev)
/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
rcu_barrier();
+ cancel_work_sync(&shost->eh_work);
if (shost->tmf_work_q)
destroy_workqueue(shost->tmf_work_q);
if (shost->ehandler)
@@ -422,6 +423,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait);
mutex_init(&shost->scan_mutex);
+ INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup);
index = ida_alloc(&host_index_ida, GFP_KERNEL);
if (index < 0) {
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index a1b116cd4723..8edad1830abe 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -5017,6 +5017,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
if (phys_disk->in_reset) {
cmd->result = DID_RESET << 16;
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ scsi_dma_unmap(cmd);
+ if (use_sg > h->ioaccel_maxsg)
+ hpsa_unmap_ioaccel2_sg_chain_block(h, cp);
return -1;
}
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 82af59c913e9..23355f12fbff 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -8189,6 +8189,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
mempool_free(mboxq, phba->mbox_mem_pool);
goto out_free_bsmbx;
}
+ mempool_free(mboxq, phba->mbox_mem_pool);
/*
* 1 for cmd, 1 for rsp, NVME adds an extra one
@@ -8311,8 +8312,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_sg_dma_buf;
}
- mempool_free(mboxq, phba->mbox_mem_pool);
-
/* Verify OAS is supported */
lpfc_sli4_oas_verify(phba);
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 147127fb4db9..453a2232452d 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy)
}
}
+void scsi_rcu_eh_wakeup(struct work_struct *work)
+{
+ struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work);
+ unsigned long flags;
+ unsigned int busy;
+
+ /*
+ * Ensure any running scsi_dec_host_busy has completed its rcu section
+ * so changes to host state and host_eh_scheduled are visible to all
+ * future calls of scsi_dec_host_busy
+ */
+ synchronize_rcu();
+
+ busy = scsi_host_busy(shost);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ scsi_eh_wakeup(shost, busy);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
/**
* scsi_schedule_eh - schedule EH for SCSI host
* @shost: SCSI host to invoke error handling on.
@@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
shost->host_eh_scheduled++;
- scsi_eh_wakeup(shost, scsi_host_busy(shost));
+ queue_work(shost->tmf_work_q, &shost->eh_work);
}
spin_unlock_irqrestore(shost->host_lock, flags);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 22e2e3223440..daeb3693fe55 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2224,14 +2224,6 @@ struct scsi_device *scsi_device_from_queue(struct request_queue *q)
return sdev;
}
-/*
- * pktcdvd should have been integrated into the SCSI layers, but for historical
- * reasons like the old IDE driver it isn't. This export allows it to safely
- * probe if a given device is a SCSI one and only attach to that.
- */
-#ifdef CONFIG_CDROM_PKTCDVD_MODULE
-EXPORT_SYMBOL_GPL(scsi_device_from_queue);
-#endif
/**
* scsi_block_requests - Utility function used by low-level drivers to prevent
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 7a193cc04e5b..3dbf2ca59536 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req);
extern int scsi_error_handler(void *host);
extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd);
extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy);
+extern void scsi_rcu_eh_wakeup(struct work_struct *work);
extern void scsi_eh_scmd_add(struct scsi_cmnd *);
void scsi_eh_ready_devs(struct Scsi_Host *shost,
struct list_head *work_q,
@@ -102,6 +103,7 @@ void scsi_eh_done(struct scsi_cmnd *scmd);
/* scsi_lib.c */
extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd);
+extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern void scsi_queue_insert(struct scsi_cmnd *cmd,
enum scsi_qc_status reason);
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 74cd4e8a61c2..5408f002e6c0 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -863,10 +863,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
if (val >= SG_MAX_QUEUE)
break;
rinfo[val].req_state = srp->done + 1;
- rinfo[val].problem =
- srp->header.masked_status &
- srp->header.host_status &
- srp->header.driver_status;
+ rinfo[val].problem = srp->header.masked_status ||
+ srp->header.host_status ||
+ srp->header.driver_status;
if (srp->done)
rinfo[val].duration =
srp->header.duration;
diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c
index 5b0e9dae231f..6af69ccaa295 100644
--- a/drivers/soc/tegra/fuse/tegra-apbmisc.c
+++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c
@@ -116,7 +116,7 @@ bool tegra_is_silicon(void)
u32 tegra_read_straps(void)
{
- WARN(!chipid, "Tegra ABP MISC not yet available\n");
+ WARN(!apbmisc_base, "Tegra ABP MISC not yet available\n");
return strapping;
}
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index aaba1a3ad577..ecb0be394696 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -382,12 +382,16 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
/* Clear interrupt */
writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS);
- if (cqspi->use_dma_read && ddata && ddata->get_dma_status)
- irq_status = ddata->get_dma_status(cqspi);
- else if (cqspi->slow_sram)
+ if (cqspi->use_dma_read && ddata && ddata->get_dma_status) {
+ if (ddata->get_dma_status(cqspi)) {
+ complete(&cqspi->transfer_complete);
+ return IRQ_HANDLED;
+ }
+ } else if (cqspi->slow_sram) {
irq_status &= CQSPI_IRQ_MASK_RD_SLOW_SRAM | CQSPI_IRQ_MASK_WR;
- else
+ } else {
irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR;
+ }
if (irq_status)
complete(&cqspi->transfer_complete);
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index bd70a7ed8067..f7d848fec9ab 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -282,7 +282,8 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed)
static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws)
{
- return !(dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_TF_EMPT);
+ return (dw_readl(dws, DW_SPI_SR) &
+ (DW_SPI_SR_BUSY | DW_SPI_SR_TF_EMPT)) != DW_SPI_SR_TF_EMPT;
}
static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index 87c5d26a5089..2853b95b2c59 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str)
static bool iscsi_parse_pr_out_transport_id(
struct se_portal_group *se_tpg,
char *buf,
+ u32 buf_len,
u32 *out_tid_len,
char **port_nexus_ptr,
char *i_str)
{
char *p;
+ u32 tid_len;
int i;
- u8 format_code = (buf[0] & 0xc0);
+ u8 format_code;
+
+ /*
+ * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL
+ * LENGTH) must be present before any of it can be parsed.
+ */
+ if (buf_len < 4)
+ return false;
+
+ format_code = buf[0] & 0xc0;
/*
* Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:
*
@@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id(
return false;
}
/*
- * If the caller wants the TransportID Length, we set that value for the
- * entire iSCSI Tarnsport ID now.
+ * Reconstruct the self-described TransportID length from the ADDITIONAL
+ * LENGTH field plus the 4-byte header. Reject it if it is below the
+ * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least
+ * 20) or if it runs past the bytes actually received, so that every
+ * access below stays inside the TransportID.
*/
- if (out_tid_len) {
- /* The shift works thanks to integer promotion rules */
- *out_tid_len = get_unaligned_be16(&buf[2]);
- /* Add four bytes for iSCSI Transport ID header */
- *out_tid_len += 4;
- }
+ tid_len = get_unaligned_be16(&buf[2]) + 4;
+ if (tid_len < 24 || tid_len > buf_len)
+ return false;
+ if (out_tid_len)
+ *out_tid_len = tid_len;
/*
* Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
@@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id(
* format.
*/
if (format_code == 0x40) {
- p = strstr(&buf[4], ",i,0x");
+ p = strnstr(&buf[4], ",i,0x", tid_len - 4);
if (!p) {
- pr_err("Unable to locate \",i,0x\" separator"
- " for Initiator port identifier: %s\n",
- &buf[4]);
+ pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n");
+ return false;
+ }
+ /*
+ * The iSCSI name runs from &buf[4] up to the separator; reject it
+ * if it cannot fit in i_str[TRANSPORT_IQN_LEN].
+ */
+ if (p - &buf[4] >= TRANSPORT_IQN_LEN) {
+ pr_err("iSCSI Initiator port name too long in TransportID\n");
return false;
}
*p = '\0'; /* Terminate iSCSI Name */
p += 5; /* Skip over ",i,0x" separator */
+ /*
+ * The ISID must follow the separator. A ",i,0x" sitting at the
+ * very end of the TransportID leaves no ISID and would point the
+ * port nexus at buf + tid_len, i.e. past the descriptor, which
+ * the registration code then reads as the ISID string.
+ */
+ if (p >= buf + tid_len) {
+ pr_err("Missing ISID in iSCSI Initiator port TransportID\n");
+ return false;
+ }
*port_nexus_ptr = p;
/*
* Go ahead and do the lower case conversion of the received
@@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id(
* for comparison against the running iSCSI session's ISID from
* iscsi_target.c:lio_sess_get_initiator_sid()
*/
- for (i = 0; i < 12; i++) {
+ for (i = 0; i < 12 && p < buf + tid_len; i++) {
/*
* The first ISCSI INITIATOR SESSION ID field byte
* containing an ASCII null character terminates the
@@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id(
*p = tolower(*p);
p++;
}
- } else
+ strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
+ } else {
*port_nexus_ptr = NULL;
-
- strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
+ /*
+ * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The
+ * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN].
+ * (For 01b the same tid_len bound would be over-restrictive: the
+ * descriptor also carries the separator and ISID, so a legal
+ * <=223-byte name gives tid_len up to 244.)
+ */
+ if (tid_len - 4 >= TRANSPORT_IQN_LEN) {
+ pr_err("iSCSI Initiator port name too long in TransportID\n");
+ return false;
+ }
+ strscpy(i_str, &buf[4], tid_len - 4);
+ }
return true;
}
@@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
}
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
- char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str)
+ char *buf, u32 buf_len, u32 *out_tid_len,
+ char **port_nexus_ptr, char *i_str)
{
+ /*
+ * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI
+ * format is variable and bounds itself against buf_len below.
+ */
+ if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24)
+ return false;
+
switch (tpg->proto_id) {
case SCSI_PROTOCOL_SAS:
/*
@@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
sbp_parse_pr_out_transport_id(buf, i_str);
break;
case SCSI_PROTOCOL_ISCSI:
- return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len,
- port_nexus_ptr, i_str);
+ return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len,
+ out_tid_len, port_nexus_ptr, i_str);
default:
pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id);
return false;
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 763e6d26e187..f0886ea29034 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg, int *format_code,
unsigned char *buf);
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
- char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str);
+ char *buf, u32 buf_len, u32 *out_tid_len,
+ char **port_nexus_ptr, char *i_str);
/* target_core_hba.c */
struct se_hba *core_alloc_hba(const char *, u32, u32);
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 11790f2c5d80..1a77b4bb62b0 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port(
iport_ptr = NULL;
tid_found = target_parse_pr_out_transport_id(tmp_tpg,
- ptr, &tid_len, &iport_ptr, i_str);
+ ptr, tpdl, &tid_len, &iport_ptr, i_str);
if (!tid_found)
continue;
/*
@@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
tid_found = target_parse_pr_out_transport_id(dest_se_tpg,
- &buf[24], &tmp_tid_len, &iport_ptr, initiator_str);
+ &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str);
if (!tid_found) {
pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
" initiator_str from Transport ID\n");
@@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
- transport_kunmap_data_sg(cmd);
- buf = NULL;
-
pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s"
" %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ?
"port" : "device", initiator_str, (iport_ptr != NULL) ?
@@ -3532,6 +3529,11 @@ after_iport_check:
core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
core_scsi3_put_pr_reg(dest_pr_reg);
+ /*
+ * iport_ptr aliases the PR-OUT parameter list mapped above, so the
+ * buffer is unmapped only here on success (and at out: on error).
+ */
+ transport_kunmap_data_sg(cmd);
return 0;
out:
if (buf)
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index e33f95c91b09..c7036e0e41bd 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -611,6 +611,25 @@ static void scsiback_disconnect(struct vscsibk_info *info)
xenbus_unmap_ring_vfree(info->dev, info->ring.sring);
}
+/*
+ * Send the error response for a request that did not reach the target core
+ * and return its tag. Free the tag before the response drops the v2p
+ * reference that keeps the session alive, and snapshot what the response
+ * needs since returning the tag can let the slot be reused.
+ */
+static void scsiback_resp_and_free(struct vscsibk_pend *pending_req,
+ int32_t result)
+{
+ struct vscsibk_info *info = pending_req->info;
+ struct v2p_entry *v2p = pending_req->v2p;
+ struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess;
+ u16 rqid = pending_req->rqid;
+
+ target_free_tag(se_sess, &pending_req->se_cmd);
+ scsiback_send_response(info, NULL, result, 0, rqid);
+ kref_put(&v2p->kref, scsiback_free_translation_entry);
+}
+
static void scsiback_device_action(struct vscsibk_pend *pending_req,
enum tcm_tmreq_table act, int tag)
{
@@ -639,7 +658,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req,
return;
err:
- scsiback_do_resp_with_sense(NULL, err, 0, pending_req);
+ scsiback_resp_and_free(pending_req, err);
}
/*
@@ -792,9 +811,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
case VSCSIIF_ACT_SCSI_CDB:
if (scsiback_gnttab_data_map(&ring_req, pending_req)) {
scsiback_fast_flush_area(pending_req);
- scsiback_do_resp_with_sense(NULL,
- DID_ERROR << 16, 0, pending_req);
- transport_generic_free_cmd(&pending_req->se_cmd, 0);
+ scsiback_resp_and_free(pending_req,
+ DID_ERROR << 16);
} else {
scsiback_cmd_exec(pending_req);
}
@@ -808,9 +826,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
break;
default:
pr_err_ratelimited("invalid request\n");
- scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0,
- pending_req);
- transport_generic_free_cmd(&pending_req->se_cmd, 0);
+ scsiback_resp_and_free(pending_req, DID_ERROR << 16);
break;
}
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 9f6454e9db81..cf50fd623f41 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -358,6 +358,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
int ret = 0;
u32 bio_offset = 0;
+ bool using_commit_root = false;
if ((inode->flags & BTRFS_INODE_NODATASUM) ||
test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state))
@@ -431,6 +432,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* from across transactions.
*/
if (bbio->csum_search_commit_root) {
+ using_commit_root = true;
path->search_commit_root = true;
path->skip_locking = true;
down_read(&fs_info->commit_root_sem);
@@ -463,6 +465,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* assume this is the case.
*/
if (count == 0) {
+ /*
+ * If an extent is relocated in the current transaction
+ * then relocation writes a new csum without updating
+ * the extent map generation. Until the next commit, we
+ * will see a hole in that case, so we need to fallback
+ * to searching the transaction csum root.
+ *
+ * Note that a commit root lookup of a referenced extent can
+ * only miss, not return a stale csum. A freed extent's csum
+ * is deleted in the same transaction and its bytenr is not
+ * reusable until that transaction has committed and the
+ * extent is unpinned.
+ */
+ if (using_commit_root) {
+ up_read(&fs_info->commit_root_sem);
+ using_commit_root = false;
+ path->search_commit_root = false;
+ path->skip_locking = false;
+ btrfs_release_path(path);
+ continue;
+ }
+
memset(csum_dst, 0, csum_size);
count = 1;
@@ -481,7 +505,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
bio_offset += count * sectorsize;
}
- if (bbio->csum_search_commit_root)
+ if (using_commit_root)
up_read(&fs_info->commit_root_sem);
return ret;
}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 6009b1477232..e2af75a205ea 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -551,6 +551,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index)
u32 crc = ~(u32)0;
unsigned offset = 0;
+ if (index >= io_ctl->num_pages)
+ return -EIO;
+
if (index == 0)
offset = sizeof(u32) * io_ctl->num_pages;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9d47d16394fc..81e87bc39828 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -289,6 +289,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
int ret;
const char *comp = NULL;
u32 inode_flags;
+ bool prop_set = false;
if (btrfs_root_readonly(root))
return -EROFS;
@@ -401,16 +402,15 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
if (comp) {
ret = btrfs_set_prop(trans, inode, "btrfs.compression",
comp, strlen(comp), 0);
- if (unlikely(ret)) {
- btrfs_abort_transaction(trans, ret);
+ if (ret)
goto out_end_trans;
- }
+ prop_set = true;
} else {
ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL, 0, 0);
- if (unlikely(ret && ret != -ENODATA)) {
- btrfs_abort_transaction(trans, ret);
+ prop_set = (ret == 0);
+ /* If ret == -ENODATA ignore and proceed to update inode item. */
+ if (ret && ret != -ENODATA)
goto out_end_trans;
- }
}
update_flags:
@@ -420,6 +420,12 @@ update_flags:
inode_inc_iversion(&inode->vfs_inode);
inode_set_ctime_current(&inode->vfs_inode);
ret = btrfs_update_inode(trans, inode);
+ /*
+ * If we set a property or deleted one, we must abort if we fail to
+ * update the inode, to avoid persisting an inconsistent state.
+ */
+ if (unlikely(ret && prop_set))
+ btrfs_abort_transaction(trans, ret);
out_end_trans:
btrfs_end_transaction(trans);
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 6e4aa22853ab..1531adb117d1 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -552,17 +552,26 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
size_t max_segment_len = workspace_buf_length(fs_info);
int ret;
- if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2))
+ if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
+ srclen, LZO_LEN, max_segment_len + LZO_LEN * 2);
return -EUCLEAN;
+ }
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen))
+ if (unlikely(in_len != srclen)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu",
+ in_len, srclen);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen - LZO_LEN * 2))
+ if (unlikely(in_len != srclen - LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu",
+ in_len, srclen - LZO_LEN * 2);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
out_len = sectorsize;
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index 87e60a2d4bd8..53e726119ca7 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -449,9 +449,9 @@ void btrfs_print_leaf(const struct extent_buffer *l)
nr = btrfs_header_nritems(l);
btrfs_info(fs_info,
- "leaf %llu gen %llu total ptrs %d free space %d owner %llu",
+ "leaf %llu gen %llu total ptrs %d free space %d owner %lld",
btrfs_header_bytenr(l), btrfs_header_generation(l), nr,
- btrfs_leaf_free_space(l), btrfs_header_owner(l));
+ btrfs_leaf_free_space(l), (s64)btrfs_header_owner(l));
print_eb_refs_lock(l);
for (i = 0 ; i < nr ; i++) {
char key_buf[KEY_TYPE_BUF_SIZE];
@@ -600,10 +600,10 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow)
return;
}
btrfs_info(fs_info,
- "node %llu level %d gen %llu total ptrs %d free spc %u owner %llu",
+ "node %llu level %d gen %llu total ptrs %d free spc %u owner %lld",
btrfs_header_bytenr(c), level, btrfs_header_generation(c),
nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr,
- btrfs_header_owner(c));
+ (s64)btrfs_header_owner(c));
print_eb_refs_lock(c);
for (i = 0; i < nr; i++) {
btrfs_node_key_to_cpu(c, &key, i);
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
index adc956432d2f..bb77d46376d4 100644
--- a/fs/btrfs/props.c
+++ b/fs/btrfs/props.c
@@ -127,14 +127,24 @@ int btrfs_set_prop(struct btrfs_trans_handle *trans, struct btrfs_inode *inode,
return ret;
}
+ ret = handler->validate(inode, value, value_len);
+ if (ret)
+ return ret;
ret = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, value,
value_len, flags);
if (ret)
return ret;
ret = handler->apply(inode, value, value_len);
- if (ret) {
- btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, NULL,
- 0, flags);
+ /* We validated before, so it should not fail here. */
+ ASSERT(ret == 0);
+ if (unlikely(ret)) {
+ int ret2;
+
+ /* Try to delete xattr, if not possible abort transaction. */
+ ret2 = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name,
+ NULL, 0, flags);
+ if (unlikely(ret2))
+ btrfs_abort_transaction(trans, ret2);
return ret;
}
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index fb85bc8b345c..0d63d117db59 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -719,21 +719,19 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
ret = btrfs_insert_root(trans, fs_info->tree_root,
&root_key, root_item);
- if (ret)
- goto abort;
+ if (unlikely(ret)) {
+ btrfs_abort_transaction(trans, ret);
+ return ERR_PTR(ret);
+ }
reloc_root = btrfs_read_tree_root(fs_info->tree_root, &root_key);
if (IS_ERR(reloc_root)) {
- ret = PTR_ERR(reloc_root);
- goto abort;
+ btrfs_abort_transaction(trans, PTR_ERR(reloc_root));
+ return ERR_CAST(reloc_root);
}
set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state);
btrfs_set_root_last_trans(reloc_root, trans->transid);
return reloc_root;
-
-abort:
- btrfs_abort_transaction(trans, ret);
- return ERR_PTR(ret);
}
/*
@@ -1912,6 +1910,7 @@ again:
* corruption, e.g. bad reloc tree key offset.
*/
ret = -EINVAL;
+ btrfs_put_root(root);
goto out;
}
ret = merge_reloc_root(rc, root);
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index cb3e676a81cc..0ce91396b517 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1923,6 +1923,12 @@ static int check_inode_ref(struct extent_buffer *leaf,
iref = (struct btrfs_inode_ref *)ptr;
namelen = btrfs_inode_ref_name_len(leaf, iref);
+ if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) {
+ inode_ref_err(leaf, slot,
+ "invalid inode ref name length, has %u expect [1, %u]",
+ namelen, BTRFS_NAME_LEN);
+ return -EUCLEAN;
+ }
if (unlikely(ptr + sizeof(*iref) + namelen > end)) {
inode_ref_err(leaf, slot,
"inode ref overflow, ptr %lu end %lu namelen %u",
diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
index 0868c12fc15b..a1a20a5ba548 100644
--- a/fs/erofs/ishare.c
+++ b/fs/erofs/ishare.c
@@ -12,12 +12,6 @@
static struct vfsmount *erofs_ishare_mnt;
-static inline bool erofs_is_ishare_inode(struct inode *inode)
-{
- /* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */
- return inode->i_sb->s_type == &erofs_anon_fs_type;
-}
-
static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
{
struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint;
@@ -179,7 +173,7 @@ struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
struct inode *realinode;
*need_iput = false;
- if (!erofs_is_ishare_inode(inode))
+ if (inode->i_sb != erofs_ishare_mnt->mnt_sb)
return inode;
vi_share = EROFS_I(inode);
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 86fa5c6a0c70..9d8f862f309f 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -595,17 +595,6 @@ static const struct export_operations erofs_export_ops = {
.get_parent = erofs_get_parent,
};
-static void erofs_set_sysfs_name(struct super_block *sb)
-{
- struct erofs_sb_info *sbi = EROFS_SB(sb);
-
- if (erofs_is_fileio_mode(sbi))
- super_set_sysfs_name_generic(sb, "%s",
- bdi_dev_name(sb->s_bdi));
- else
- super_set_sysfs_name_id(sb);
-}
-
static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct inode *inode;
@@ -657,12 +646,14 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
err = super_setup_bdi(sb);
if (err)
return err;
+
+ snprintf(sb->s_id, sizeof(sb->s_id),
+ "%u:%u", MAJOR(sb->s_dev), MINOR(sb->s_dev));
} else {
if (!sb_set_blocksize(sb, PAGE_SIZE)) {
errorfc(fc, "failed to set initial blksize");
return -EINVAL;
}
-
sbi->dif0.dax_dev = fs_dax_get_by_bdev(sb->s_bdev,
&sbi->dif0.dax_part_off, NULL, NULL);
}
@@ -740,7 +731,7 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
if (err)
return err;
- erofs_set_sysfs_name(sb);
+ super_set_sysfs_name_id(sb);
err = erofs_register_sysfs(sb);
if (err)
return err;
@@ -1038,7 +1029,7 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root)
",user_xattr" : ",nouser_xattr");
if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))
seq_puts(seq, test_opt(opt, POSIX_ACL) ? ",acl" : ",noacl");
- if (IS_ENABLED(CONFIG_EROFS_FS_ZIP))
+ if (IS_ENABLED(CONFIG_EROFS_FS_ZIP) && sbi->available_compr_algs)
seq_printf(seq, ",cache_strategy=%s",
erofs_param_cache_strategy[opt->cache_strategy].name);
if (test_opt(opt, DAX_ALWAYS))
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index bab521613552..5811556a7b71 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -732,7 +732,8 @@ static int z_erofs_map_sanity_check(struct inode *inode,
map->m_algorithmformat, EROFS_I(inode)->nid);
return -EFSCORRUPTED;
}
- if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen) {
+ if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen &&
+ map->m_la + map->m_llen < inode->i_size) {
erofs_err(inode->i_sb, "too much compressed data @ la %llu of nid %llu",
map->m_la, EROFS_I(inode)->nid);
return -EFSCORRUPTED;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index d0486f4a47ba..fa92e31d19d6 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2340,14 +2340,15 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb,
if (error)
return error;
- nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
- if (nn->nfsd_serv)
+ if (nn->nfsd_serv) {
+ nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
nfsd4_revoke_states(nn, path.dentry->d_sb);
- else
+ } else {
error = -EINVAL;
+ }
mutex_unlock(&nfsd_mutex);
path_put(&path);
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 4df6ca03a8de..66b9104e7ca2 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1490,9 +1490,13 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
}
}
- /* force revalidate of size and timestamps of target file now
- that target is updated on the server */
- CIFS_I(target_inode)->time = 0;
+ /*
+ * On success, duplicate_extents already updated the target inode attrs
+ * or marked them stale if the refresh failed. On failure, mark attrs
+ * stale because EOF may have changed before the clone failed.
+ */
+ if (rc)
+ CIFS_I(target_inode)->time = 0;
unlock:
/* although unlocking in the reverse order from locking is not
strictly necessary here it is a little cleaner to be consistent */
diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c
index 74530088d17d..ab3d09613c91 100644
--- a/fs/smb/client/smb1maperror.c
+++ b/fs/smb/client/smb1maperror.c
@@ -10,6 +10,9 @@
*/
#include <linux/bsearch.h>
+
+#include <kunit/visibility.h>
+
#include "cifsproto.h"
#include "smb1proto.h"
#include "smberr.h"
@@ -239,48 +242,45 @@ int __init smb1_init_maperror(void)
}
#if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
-#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \
- EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test")
-
const struct ntstatus_to_dos_err *
search_ntstatus_to_dos_map_test(__u32 ntstatus)
{
return search_ntstatus_to_dos_map(ntstatus);
}
-EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test);
+EXPORT_SYMBOL_IF_KUNIT(search_ntstatus_to_dos_map_test);
const struct ntstatus_to_dos_err *
ntstatus_to_dos_map_test = ntstatus_to_dos_map;
-EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test);
+EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_map_test);
unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map);
-EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num);
+EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_num);
const struct smb_to_posix_error *
search_mapping_table_ERRDOS_test(__u16 smb_err)
{
return search_mapping_table_ERRDOS(smb_err);
}
-EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test);
+EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRDOS_test);
const struct smb_to_posix_error *
mapping_table_ERRDOS_test = mapping_table_ERRDOS;
-EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_test);
unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS);
-EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_num);
const struct smb_to_posix_error *
search_mapping_table_ERRSRV_test(__u16 smb_err)
{
return search_mapping_table_ERRSRV(smb_err);
}
-EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test);
+EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRSRV_test);
const struct smb_to_posix_error *
mapping_table_ERRSRV_test = mapping_table_ERRSRV;
-EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_test);
unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV);
-EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_num);
#endif
diff --git a/fs/smb/client/smb1maperror_test.c b/fs/smb/client/smb1maperror_test.c
index 2caaf11228ef..903c46f71291 100644
--- a/fs/smb/client/smb1maperror_test.c
+++ b/fs/smb/client/smb1maperror_test.c
@@ -75,3 +75,4 @@ kunit_test_suite(maperror_suite);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KUnit tests of SMB1 maperror");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c
index 9ed21f7b618c..d86f2460d0e5 100644
--- a/fs/smb/client/smb2maperror.c
+++ b/fs/smb/client/smb2maperror.c
@@ -8,6 +8,9 @@
*
*/
#include <linux/errno.h>
+
+#include <kunit/visibility.h>
+
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2proto.h"
@@ -109,18 +112,15 @@ int __init smb2_init_maperror(void)
}
#if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
-#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \
- EXPORT_SYMBOL_FOR_MODULES(sym, "smb2maperror_test")
-
const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status)
{
return smb2_get_err_map(smb2_status);
}
-EXPORT_SYMBOL_FOR_SMB_TEST(smb2_get_err_map_test);
+EXPORT_SYMBOL_IF_KUNIT(smb2_get_err_map_test);
const struct status_to_posix_error *smb2_error_map_table_test = smb2_error_map_table;
-EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_table_test);
+EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_table_test);
unsigned int smb2_error_map_num = ARRAY_SIZE(smb2_error_map_table);
-EXPORT_SYMBOL_FOR_SMB_TEST(smb2_error_map_num);
+EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_num);
#endif
diff --git a/fs/smb/client/smb2maperror_test.c b/fs/smb/client/smb2maperror_test.c
index 0f8a44a5ed3c..44dc5e899cad 100644
--- a/fs/smb/client/smb2maperror_test.c
+++ b/fs/smb/client/smb2maperror_test.c
@@ -47,3 +47,4 @@ kunit_test_suite(maperror_suite);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KUnit tests of SMB2 maperror");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index 6270b33147d2..9068175e57cd 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -19,7 +19,8 @@
#include "nterr.h"
#include "cached_dir.h"
-static unsigned int __smb2_calc_size(void *buf, bool *have_data);
+static unsigned int __smb2_calc_size(void *buf, bool *have_data,
+ bool *data_area_overlap);
static int
check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
@@ -148,6 +149,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
__u32 calc_len; /* calculated length */
__u64 mid;
bool have_data;
+ bool data_area_overlap;
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
@@ -232,7 +234,12 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
}
have_data = false;
- calc_len = __smb2_calc_size(buf, &have_data);
+ data_area_overlap = false;
+ calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap);
+
+ /* Reject responses whose data area overlaps the fixed area. */
+ if (data_area_overlap)
+ return 1;
/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
* be 0, and not a real miscalculation */
@@ -416,14 +423,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)
}
/*
- * Calculate the size of the SMB message based on the fixed header
- * portion, the number of word parameters and the data portion of the message.
- * If have_data is non-NULL, it is set to true when a non-empty data area was
- * found (data_length > 0), allowing callers to distinguish the implied bcc[0]
- * case (no data area) from an overreported data length.
+ * Calculate the size of the SMB message based on the fixed header, fixed
+ * parameter area, and variable data area.
+ *
+ * If have_data is not NULL, it is set when a non-empty data area is found.
+ * If data_area_overlap is not NULL, it is set when the data area overlaps
+ * the fixed area.
*/
static unsigned int
-__smb2_calc_size(void *buf, bool *have_data)
+__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap)
{
struct smb2_pdu *pdu = buf;
struct smb2_hdr *shdr = &pdu->hdr;
@@ -432,6 +440,11 @@ __smb2_calc_size(void *buf, bool *have_data)
/* Structure Size has already been checked to make sure it is 64 */
int len = le16_to_cpu(shdr->StructureSize);
+ if (have_data)
+ *have_data = false;
+ if (data_area_overlap)
+ *data_area_overlap = false;
+
/*
* StructureSize2, ie length of fixed parameter area has already
* been checked to make sure it is the correct length.
@@ -454,7 +467,10 @@ __smb2_calc_size(void *buf, bool *have_data)
if (offset + 1 < len) {
cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n",
offset + 1, len);
+ if (data_area_overlap)
+ *data_area_overlap = true;
data_length = 0;
+ goto calc_size_exit;
} else {
len = offset + data_length;
}
@@ -469,7 +485,7 @@ calc_size_exit:
unsigned int
smb2_calc_size(void *buf)
{
- return __smb2_calc_size(buf, NULL);
+ return __smb2_calc_size(buf, NULL, NULL);
}
/* Note: caller must free return buffer */
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 6bce44b171e4..cbd51a08e97e 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2193,10 +2193,14 @@ smb2_duplicate_extents(const unsigned int xid,
u64 len, u64 dest_off)
{
int rc;
+ int qrc;
unsigned int ret_data_len;
struct inode *inode;
+ struct smb2_file_all_info file_inf;
struct duplicate_extents_to_file dup_ext_buf;
+ struct timespec64 ts;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+ u64 asize;
/* server fileays advertise duplicate extent support with this flag */
if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
@@ -2232,6 +2236,32 @@ smb2_duplicate_extents(const unsigned int xid,
if (ret_data_len > 0)
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
+ if (rc == 0) {
+ qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
+ trgtfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ CIFS_I(inode)->time = jiffies;
+ if (file_inf.LastWriteTime) {
+ ts = cifs_NTtimeToUnix(file_inf.LastWriteTime);
+ inode_set_mtime_to_ts(inode, ts);
+ }
+ if (file_inf.ChangeTime) {
+ ts = cifs_NTtimeToUnix(file_inf.ChangeTime);
+ inode_set_ctime_to_ts(inode, ts);
+ }
+ if (file_inf.LastAccessTime) {
+ ts = cifs_NTtimeToUnix(file_inf.LastAccessTime);
+ inode_set_atime_to_ts(inode, ts);
+ }
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ CIFS_I(inode)->time = 0; /* force reval */
+ }
+ spin_unlock(&inode->i_lock);
+ }
+
duplicate_extents_out:
if (rc)
trace_smb3_clone_err(xid, srcfile->fid.volatile_fid,
@@ -3490,6 +3520,15 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
filemap_invalidate_lock(inode->i_mapping);
/*
+ * Flush dirty data first, otherwise a dirty folio spanning the punched
+ * range may be written back after the ioctl and refill the hole.
+ */
+ rc = filemap_write_and_wait_range(inode->i_mapping, offset,
+ offset + len - 1);
+ if (rc < 0)
+ goto unlock;
+
+ /*
* We implement the punch hole through ioctl, so we need remove the page
* caches first, otherwise the data may be inconsistent with the server.
*/
@@ -3543,7 +3582,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
char *buf)
{
struct cifs_io_parms io_parms = {0};
- int nbytes;
+ unsigned int nbytes;
int rc = 0;
struct kvec iov[2];
@@ -3564,9 +3603,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
if (rc)
break;
+ if (!nbytes)
+ return -EIO;
if (nbytes > len)
return -EINVAL;
- buf += nbytes;
off += nbytes;
len -= nbytes;
}
@@ -3579,11 +3619,25 @@ static int smb3_simple_fallocate_range(unsigned int xid,
loff_t off, loff_t len)
{
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
+ struct inode *inode = d_inode(cfile->dentry);
u32 out_data_len;
char *buf = NULL;
+ u64 range_start, range_len, range_end;
loff_t l;
int rc;
+ buf = kvzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (off >= i_size_read(inode)) {
+ rc = smb3_simple_fallocate_write_range(xid, tcon, cfile,
+ off, len, buf);
+ goto out;
+ }
+
in_data.file_offset = cpu_to_le64(off);
in_data.length = cpu_to_le64(len);
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
@@ -3595,12 +3649,6 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (rc)
goto out;
- buf = kzalloc(1024 * 1024, GFP_KERNEL);
- if (buf == NULL) {
- rc = -ENOMEM;
- goto out;
- }
-
tmp_data = out_data;
while (len) {
/*
@@ -3617,13 +3665,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
goto out;
}
- if (off < le64_to_cpu(tmp_data->file_offset)) {
+ range_start = le64_to_cpu(tmp_data->file_offset);
+ range_len = le64_to_cpu(tmp_data->length);
+ if (check_add_overflow(range_start, range_len, &range_end) ||
+ range_end > S64_MAX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (off < range_start) {
/*
* We are at a hole. Write until the end of the region
* or until the next allocated data,
* whichever comes next.
*/
- l = le64_to_cpu(tmp_data->file_offset) - off;
+ l = range_start - off;
if (len < l)
l = len;
rc = smb3_simple_fallocate_write_range(xid, tcon,
@@ -3640,11 +3696,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
* until the end of the data or the end of the region
* we are supposed to fallocate, whichever comes first.
*/
- l = le64_to_cpu(tmp_data->length);
- if (len < l)
- l = len;
- off += l;
- len -= l;
+ if (off < range_end) {
+ l = range_end - off;
+ if (len < l)
+ l = len;
+ off += l;
+ len -= l;
+ }
tmp_data = &tmp_data[1];
out_data_len -= sizeof(struct file_allocated_range_buffer);
@@ -3652,7 +3710,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
out:
kfree(out_data);
- kfree(buf);
+ kvfree(buf);
return rc;
}
@@ -3665,18 +3723,22 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
long rc = -EOPNOTSUPP;
unsigned int xid;
- loff_t new_eof;
+ loff_t old_eof, new_eof;
+ struct smb2_file_all_info file_inf;
+ u64 asize;
+ int qrc;
xid = get_xid();
inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
+ old_eof = i_size_read(inode);
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
tcon->ses->Suid, off, len);
/* if file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi))
- if (keep_size == false) {
+ if (!keep_size) {
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid);
@@ -3686,21 +3748,98 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
/*
* Extending the file
*/
- if ((keep_size == false) && i_size_read(inode) < off + len) {
+ if (!keep_size && old_eof < off + len) {
rc = inode_newsize_ok(inode, off + len);
if (rc)
goto out;
+ /*
+ * A small range at or beyond EOF can be allocated by writing
+ * zeroes. For off > old_eof, this preserves the intervening
+ * hole instead of allocating from offset 0.
+ */
+ if (off > old_eof ||
+ (off == old_eof && old_eof != 0 &&
+ (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
+ if (len > 1024 * 1024) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+ off, len);
+ if (rc) {
+ spin_lock(&inode->i_lock);
+ cifsi->time = 0;
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
+ new_eof = off + len;
+ netfs_resize_file(&cifsi->netfs, new_eof, true);
+ cifs_setsize(inode, new_eof);
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ cifsi->time = 0;
+ }
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
smb2_set_sparse(xid, tcon, cfile, inode, false);
new_eof = off + len;
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ if (qrc == 0)
+ asize = le64_to_cpu(file_inf.AllocationSize);
+
+ /*
+ * FILE_ALLOCATION_INFORMATION can only describe allocation up to
+ * new_eof. Some servers may accept it without allocating blocks,
+ * so refresh AllocationSize before updating i_blocks.
+ */
+ if (off == 0 || off == old_eof) {
+ if (qrc || asize < new_eof) {
+ rc = SMB2_set_allocation(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ cfile->pid, new_eof);
+ if (rc)
+ goto out;
+ }
+ }
+
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
- if (rc == 0) {
- netfs_resize_file(&cifsi->netfs, new_eof, true);
- cifs_setsize(inode, new_eof);
+ if (rc)
+ goto out;
+
+ netfs_resize_file(&cifsi->netfs, new_eof, true);
+ cifs_setsize(inode, new_eof);
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize >= new_eof)
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ cifsi->time = 0;
}
+ spin_unlock(&inode->i_lock);
goto out;
}
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 8f83ab377db1..4ce165e40657 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -5950,6 +5950,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
}
int
+SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size)
+{
+ struct smb2_file_alloc_info info;
+ void *data;
+ unsigned int size;
+
+ info.AllocationSize = cpu_to_le64(allocation_size);
+
+ data = &info;
+ size = sizeof(struct smb2_file_alloc_info);
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+}
+
+int
SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb_ntsd *pnntsd, int pacllen, int aclflag)
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index e01effe45ae2..2e9f70096825 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -204,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst);
int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 pid,
loff_t new_eof);
+int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size);
int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
struct smb_rqst *rqst, u64 persistent_fid,
u64 volatile_fid, u32 pid, u8 info_class, u8 info_type,
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
index 859849a42fec..941db5a95564 100644
--- a/fs/smb/common/fscc.h
+++ b/fs/smb/common/fscc.h
@@ -283,6 +283,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
__le64 EndOfFile; /* new end of file value */
} __packed; /* level 20 Set */
+/* See MS-FSCC 2.4.4 */
+struct smb2_file_alloc_info { /* encoding of request for level 19 */
+ __le64 AllocationSize;
+} __packed;
+
/* See MS-FSCC 2.4.15 */
typedef struct {
__le32 NextEntryOffset;
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index d6331184ebfc..f99c86284ba3 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -457,22 +457,19 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
{
struct ksmbd_session *sess;
unsigned long id;
+ struct hlist_node *tmp;
+ int bkt;
down_write(&sessions_table_lock);
- if (conn->binding) {
- int bkt;
- struct hlist_node *tmp;
-
- hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) {
- if (!ksmbd_chann_del(conn, sess) &&
- xa_empty(&sess->ksmbd_chann_list)) {
- hash_del(&sess->hlist);
- down_write(&conn->session_lock);
- xa_erase(&conn->sessions, sess->id);
- up_write(&conn->session_lock);
- if (atomic_dec_and_test(&sess->refcnt))
- ksmbd_session_destroy(sess);
- }
+ hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) {
+ if (!ksmbd_chann_del(conn, sess) &&
+ xa_empty(&sess->ksmbd_chann_list)) {
+ hash_del(&sess->hlist);
+ down_write(&conn->session_lock);
+ xa_erase(&conn->sessions, sess->id);
+ up_write(&conn->session_lock);
+ if (atomic_dec_and_test(&sess->refcnt))
+ ksmbd_session_destroy(sess);
}
}
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index 8893a9aaede7..4637a8c8436d 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -19,7 +19,7 @@
struct ksmbd_file_table;
struct channel {
- char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
+ char sess_key[CIFS_KEY_SIZE];
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct ksmbd_conn *conn;
};
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 3c55ae5d6a11..79787099afdc 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -875,6 +875,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
ksmbd_conn_r_count_dec(conn);
+ ksmbd_conn_put(conn);
}
/**
@@ -910,7 +911,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
br_info->open_trunc = opinfo->open_trunc;
work->request_buf = (char *)br_info;
- work->conn = conn;
+ work->conn = ksmbd_conn_get(conn);
work->sess = opinfo->sess;
ksmbd_conn_r_count_inc(conn);
@@ -985,6 +986,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
ksmbd_conn_r_count_dec(conn);
+ ksmbd_conn_put(conn);
}
/**
@@ -1034,7 +1036,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack,
memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE);
work->request_buf = (char *)br_info;
- work->conn = conn;
+ work->conn = ksmbd_conn_get(conn);
work->sess = opinfo->sess;
ksmbd_conn_r_count_inc(conn);
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index c0c4edd092c2..9f3629c86291 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -407,6 +407,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
return 1;
}
+ if (len < __SMB2_HEADER_STRUCTURE_SIZE + sizeof(__le16)) {
+ ksmbd_debug(SMB, "Message is too small for StructureSize2\n");
+ return 1;
+ }
+
if (smb2_req_struct_sizes[command] != pdu->StructureSize2) {
if (!(command == SMB2_OPLOCK_BREAK_HE &&
(le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_20 ||
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b73167785e87..bec692bca1ca 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1689,7 +1689,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
struct ksmbd_user *user;
- char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {};
+ char channel_key[CIFS_KEY_SIZE] = {};
char *auth_key = conn->binding ? channel_key : sess->sess_key;
u64 prev_id;
bool binding = conn->binding;
@@ -1826,7 +1826,7 @@ static int krb5_authenticate(struct ksmbd_work *work,
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
char *in_blob, *out_blob;
- char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {};
+ char channel_key[CIFS_KEY_SIZE] = {};
char *auth_key = conn->binding ? channel_key : sess->sess_key;
u64 prev_sess_id;
bool binding = conn->binding;
@@ -6597,9 +6597,8 @@ static int smb2_rename(struct ksmbd_work *work,
pr_err("failed to store stream name in xattr: %d\n",
rc);
rc = -EINVAL;
- goto out;
}
-
+ kfree(xattr_stream_name);
goto out;
}
@@ -6781,6 +6780,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
*/
loff_t alloc_blks;
+ u64 alloc_size;
struct inode *inode;
struct kstat stat;
int rc;
@@ -6796,7 +6796,19 @@ static int set_file_allocation_info(struct ksmbd_work *work,
if (rc)
return rc;
- alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
+ /*
+ * AllocationSize is fully client-controlled (the caller only
+ * validates the fixed 8-byte buffer length). Reject values that
+ * would overflow the "round up to 512-byte blocks" conversion
+ * below instead of silently wrapping it to a tiny block count,
+ * which would truncate the file to a size the client never
+ * asked for.
+ */
+ alloc_size = le64_to_cpu(file_alloc_info->AllocationSize);
+ if (alloc_size > MAX_LFS_FILESIZE - 511)
+ return -EINVAL;
+
+ alloc_blks = (alloc_size + 511) >> 9;
inode = file_inode(fp->filp);
if (alloc_blks > stat.blocks) {
@@ -7433,6 +7445,15 @@ int smb2_read(struct ksmbd_work *work)
goto out;
}
+ /*
+ * ksmbd_vfs_read() fills only nbytes; the [nbytes, ALIGN(nbytes, 8))
+ * tail of the un-zeroed buffer is transmitted as compound-response
+ * alignment padding, leaking uninitialized kernel memory to the
+ * client. Zero just that tail.
+ */
+ if (nbytes & 7)
+ memset(aux_payload_buf + nbytes, 0, ALIGN(nbytes, 8) - nbytes);
+
if ((nbytes == 0 && length != 0) || nbytes < mincount) {
kvfree(aux_payload_buf);
rsp->hdr.Status = STATUS_END_OF_FILE;
@@ -9787,22 +9808,21 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work)
}
if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) {
- __u8 *hash_value;
+ ksmbd_conn_lock(conn);
if (conn->binding) {
struct preauth_session *preauth_sess;
preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id);
- if (!preauth_sess)
- return;
- hash_value = preauth_sess->Preauth_HashValue;
- } else {
- hash_value = sess->Preauth_HashValue;
- if (!hash_value)
- return;
+ if (preauth_sess)
+ ksmbd_gen_preauth_integrity_hash(conn,
+ work->response_buf,
+ preauth_sess->Preauth_HashValue);
+ } else if (sess->Preauth_HashValue) {
+ ksmbd_gen_preauth_integrity_hash(conn, work->response_buf,
+ sess->Preauth_HashValue);
}
- ksmbd_gen_preauth_integrity_hash(conn, work->response_buf,
- hash_value);
+ ksmbd_conn_unlock(conn);
}
}
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index c2512dbcdec8..aa06c8c905f1 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -212,10 +212,6 @@ struct smb2_file_ea_info {
__le32 EASize;
} __packed;
-struct smb2_file_alloc_info {
- __le64 AllocationSize;
-} __packed;
-
struct smb2_file_disposition_info {
__u8 DeletePending;
} __packed;
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index c6909716b041..89501e8bd2f8 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -878,7 +878,7 @@ xfs_defer_add_barrier(
if (dfp)
return;
- xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type);
+ dfp = xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type);
trace_xfs_defer_add_item(tp->t_mountp, dfp, NULL);
}
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index 0075b6d5a1b5..8dd9c0266e21 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -29,6 +29,7 @@
#include "xfs_rtalloc.h"
#include "xfs_rtbitmap.h"
#include "xfs_rtgroup.h"
+#include "xfs_bmap_util.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -80,12 +81,6 @@ struct xrep_cow {
unsigned int next_bno;
};
-/* CoW staging extent. */
-struct xrep_cow_extent {
- xfs_fsblock_t fsbno;
- xfs_extlen_t len;
-};
-
/*
* Mark the part of the file range that corresponds to the given physical
* space. Caller must ensure that the physical range is within xc->irec.
@@ -231,6 +226,29 @@ xrep_cow_mark_missing_staging_rmap(
}
/*
+ * Trim the start and end of the current mapping by up to 1/4 of the length
+ * and mark that as "bad" to test the cow fork repair mechanism.
+ */
+static inline int
+xrep_cow_debug_replacement(
+ struct xrep_cow *xc)
+{
+ xfs_fsblock_t fsbno = xc->irec.br_startblock;
+ xfs_extlen_t len = xc->irec.br_blockcount;
+ uint32_t trim;
+
+ /* get_random_u32_below requires a nonzero argument */
+ trim = len > 4 ? get_random_u32_below(len / 4) : 0;
+ len -= trim;
+
+ trim = len > 4 ? get_random_u32_below(len / 4) : 0;
+ fsbno += trim;
+ len -= trim;
+
+ return xrep_cow_mark_file_range(xc, fsbno, len);
+}
+
+/*
* Find any part of the CoW fork mapping that isn't a single-owner CoW staging
* extent and mark the corresponding part of the file range in the bitmap.
*/
@@ -299,8 +317,9 @@ xrep_cow_find_bad(
* If userspace is forcing us to rebuild the CoW fork or someone turned
* on the debugging knob, replace everything in the CoW fork.
*/
- if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
- XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ error = xrep_cow_debug_replacement(xc);
+ else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
xc->irec.br_blockcount);
@@ -381,8 +400,9 @@ xrep_cow_find_bad_rt(
* turned on the debugging knob, replace everything in the
* CoW fork and then scan for staging extents in the refcountbt.
*/
- if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
- XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ error = xrep_cow_debug_replacement(xc);
+ else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
xc->irec.br_blockcount);
@@ -401,22 +421,21 @@ out_rtg:
STATIC int
xrep_cow_alloc(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
struct xfs_alloc_arg args = {
.tp = sc->tp,
.mp = sc->mp,
.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
.minlen = 1,
- .maxlen = maxlen,
+ .maxlen = del->br_blockcount,
.prod = 1,
.resv = XFS_AG_RESV_NONE,
.datatype = XFS_ALLOC_USERDATA,
};
int error;
- error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
+ error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0);
if (error)
return error;
@@ -428,8 +447,8 @@ xrep_cow_alloc(
xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len);
- repl->fsbno = args.fsbno;
- repl->len = args.len;
+ del->br_startblock = args.fsbno;
+ del->br_blockcount = args.len;
return 0;
}
@@ -440,10 +459,12 @@ xrep_cow_alloc(
STATIC int
xrep_cow_alloc_rt(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
- xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen);
+ xfs_fsblock_t fsbno;
+ xfs_rtxlen_t maxrtx =
+ min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount));
+ xfs_extlen_t len;
int error;
error = xfs_trans_reserve_more(sc->tp, 0, maxrtx);
@@ -451,11 +472,14 @@ xrep_cow_alloc_rt(
return error;
error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false,
- false, &repl->fsbno, &repl->len);
+ false, &fsbno, &len);
if (error)
return error;
- xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len);
+ xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len);
+
+ del->br_startblock = fsbno;
+ del->br_blockcount = len;
return 0;
}
@@ -469,19 +493,19 @@ static inline int
xrep_cow_find_mapping(
struct xrep_cow *xc,
struct xfs_iext_cursor *icur,
- xfs_fileoff_t startoff,
- struct xfs_bmbt_irec *got)
+ xfs_fileoff_t badoff,
+ xfs_extlen_t badlen,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
{
struct xfs_inode *ip = xc->sc->ip;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
- if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
+ if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got))
goto bad;
+ memcpy(rep, got, sizeof(*rep));
- if (got->br_startoff > startoff)
- goto bad;
-
- if (got->br_blockcount == 0)
+ if (got->br_startoff > badoff)
goto bad;
if (isnullstartblock(got->br_startblock))
@@ -490,56 +514,28 @@ xrep_cow_find_mapping(
if (xfs_bmap_is_written_extent(got))
goto bad;
- return 0;
-bad:
- ASSERT(0);
- return -EFSCORRUPTED;
-}
-
-#define REPLACE_LEFT_SIDE (1U << 0)
-#define REPLACE_RIGHT_SIDE (1U << 1)
+ if (got->br_startoff < badoff) {
+ const int64_t delta = badoff - got->br_startoff;
-/*
- * Given a CoW fork mapping @got and a replacement mapping @repl, remap the
- * beginning of @got with the space described by @rep.
- */
-static inline void
-xrep_cow_replace_mapping(
- struct xfs_inode *ip,
- struct xfs_iext_cursor *icur,
- const struct xfs_bmbt_irec *got,
- const struct xrep_cow_extent *repl)
-{
- struct xfs_bmbt_irec new = *got; /* struct copy */
-
- ASSERT(repl->len > 0);
- ASSERT(!isnullstartblock(got->br_startblock));
+ rep->br_blockcount -= delta;
+ rep->br_startoff += delta;
+ rep->br_startblock += delta;
+ }
- trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
+ if (got->br_startoff + got->br_blockcount > badoff + badlen) {
+ const int64_t delta = (got->br_startoff + got->br_blockcount) -
+ (badoff + badlen);
- if (got->br_blockcount == repl->len) {
- /*
- * The new extent is a complete replacement for the existing
- * extent. Update the COW fork record.
- */
- new.br_startblock = repl->fsbno;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
- return;
+ rep->br_blockcount -= delta;
}
- /*
- * The new extent can replace the beginning of the COW fork record.
- * Move the left side of @got upwards, then insert the new record.
- */
- new.br_startoff += repl->len;
- new.br_startblock += repl->len;
- new.br_blockcount -= repl->len;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
+ if (got->br_blockcount == 0)
+ goto bad;
- new.br_startoff = got->br_startoff;
- new.br_startblock = repl->fsbno;
- new.br_blockcount = repl->len;
- xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
+ return 0;
+bad:
+ ASSERT(0);
+ return -EFSCORRUPTED;
}
/*
@@ -553,33 +549,30 @@ xrep_cow_replace_range(
xfs_extlen_t *blockcount)
{
struct xfs_iext_cursor icur;
- struct xrep_cow_extent repl;
- struct xfs_bmbt_irec got;
+ struct xfs_bmbt_irec got, rep;
struct xfs_scrub *sc = xc->sc;
- xfs_fileoff_t nextoff;
- xfs_extlen_t alloc_len;
+ xfs_fsblock_t old_fsbno;
int error;
/*
- * Put the existing CoW fork mapping in @got. If @got ends before
- * @rep, truncate @rep so we only replace one extent mapping at a time.
+ * Put the existing CoW fork mapping in @got, and put in @rep the
+ * contents of @got trimmed to @startoff/@blockcount. We only want
+ * to replace the bad region, and only one mapping at a time.
*/
- error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
+ error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got,
+ &rep);
if (error)
return error;
- nextoff = min(startoff + *blockcount,
- got.br_startoff + got.br_blockcount);
+ old_fsbno = rep.br_startblock;
/*
* Allocate a replacement extent. If we don't fill all the blocks,
* shorten the quantity that will be deleted in this step.
*/
- alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN,
- nextoff - startoff);
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrep_cow_alloc_rt(sc, alloc_len, &repl);
+ error = xrep_cow_alloc_rt(sc, &rep);
else
- error = xrep_cow_alloc(sc, alloc_len, &repl);
+ error = xrep_cow_alloc(sc, &rep);
if (error)
return error;
@@ -587,7 +580,7 @@ xrep_cow_replace_range(
* Replace the old mapping with the new one, and commit the metadata
* changes made so far.
*/
- xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
+ xfs_bmap_replace_cow_mapping(sc->ip, &icur, &got, &rep);
xfs_inode_set_cowblocks_tag(sc->ip);
error = xfs_defer_finish(&sc->tp);
@@ -596,15 +589,15 @@ xrep_cow_replace_range(
/* Note the old CoW staging extents; we'll reap them all later. */
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks,
- got.br_startblock, repl.len);
+ error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno,
+ rep.br_blockcount);
else
- error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks,
- got.br_startblock, repl.len);
+ error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno,
+ rep.br_blockcount);
if (error)
return error;
- *blockcount = repl.len;
+ *blockcount = rep.br_blockcount;
return 0;
}
diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c
index c6a210f7508f..b2cf6e5439d9 100644
--- a/fs/xfs/scrub/dirtree.c
+++ b/fs/xfs/scrub/dirtree.c
@@ -383,6 +383,14 @@ xchk_dirpath_step_up(
goto out_scanlock;
}
+ /* The handle encoded in the parent pointer must match. */
+ if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) {
+ trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr,
+ path->nr_steps, &dl->xname, &dl->pptr_rec);
+ error = -EFSCORRUPTED;
+ goto out_scanlock;
+ }
+
/* We've reached the root directory; the path is ok. */
if (parent_ino == dl->root_ino) {
xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK);
@@ -411,14 +419,6 @@ xchk_dirpath_step_up(
goto out_scanlock;
}
- /* The handle encoded in the parent pointer must match. */
- if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) {
- trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr,
- path->nr_steps, &dl->xname, &dl->pptr_rec);
- error = -EFSCORRUPTED;
- goto out_scanlock;
- }
-
/* Parent pointer must point up to a directory. */
if (!S_ISDIR(VFS_I(dp)->i_mode)) {
trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr,
diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c
index 10950e4bd4c3..079dc4e691a0 100644
--- a/fs/xfs/scrub/dqiterate.c
+++ b/fs/xfs/scrub/dqiterate.c
@@ -205,7 +205,7 @@ xchk_dquot_iter(
if (error)
return error;
- cursor->id = dq->q_id + 1;
+ cursor->id = (uint64_t)dq->q_id + 1;
*dqpp = dq;
return 1;
}
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 493dcf5cc6c1..3ec41c198351 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -921,7 +921,7 @@ xrep_dinode_bad_bmbt_fork(
if (nrecs == 0 || xfs_bmdr_space_calc(nrecs) > dfork_size)
return true;
- if (level == 0 || level >= XFS_BM_MAXLEVELS(sc->mp, whichfork))
+ if (level == 0 || level > XFS_BM_MAXLEVELS(sc->mp, whichfork))
return true;
dmxr = xfs_bmdr_maxrecs(dfork_size, 0);
@@ -1757,7 +1757,7 @@ xrep_clamp_timestamp(
struct xfs_inode *ip,
struct timespec64 *ts)
{
- ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC);
+ ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC - 1);
*ts = timestamp_truncate(*ts, VFS_I(ip));
}
diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c
index 482f899a518a..2bd2c0351b35 100644
--- a/fs/xfs/scrub/rgsuper.c
+++ b/fs/xfs/scrub/rgsuper.c
@@ -23,6 +23,8 @@ int
xchk_setup_rgsuperblock(
struct xfs_scrub *sc)
{
+ if (xchk_need_intent_drain(sc))
+ xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
return xchk_trans_alloc(sc, 0);
}
@@ -43,6 +45,7 @@ xchk_rgsuperblock(
struct xfs_scrub *sc)
{
xfs_rgnumber_t rgno = sc->sm->sm_agno;
+ unsigned int flags;
int error;
/*
@@ -63,7 +66,12 @@ xchk_rgsuperblock(
if (!xchk_xref_process_error(sc, 0, 0, &error))
return error;
- error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED);
+ if (xfs_has_rtrmapbt(sc->mp))
+ flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP;
+ else
+ flags = XFS_RTGLOCK_BITMAP_SHARED;
+
+ error = xchk_rtgroup_lock(sc, &sc->sr, flags);
if (error)
return error;
@@ -80,9 +88,13 @@ int
xrep_rgsuperblock(
struct xfs_scrub *sc)
{
+ struct xfs_buf *sb_bp;
+
ASSERT(rtg_rgno(sc->sr.rtg) == 0);
+ sb_bp = xfs_trans_getsb(sc->tp);
xfs_log_sb(sc->tp);
+ xfs_log_rtsb(sc->tp, sb_bp);
return 0;
}
#endif /* CONFIG_XFS_ONLINE_REPAIR */
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index de3f22f310f7..52c24d3d4be6 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -258,7 +258,7 @@ xchk_rtbitmap(
* the last free extent we saw and the last possible extent in the rt
* group.
*/
- last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1;
+ last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize;
if (rtb->next_free_rgbno < last_rgbno)
xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno,
last_rgbno - rtb->next_free_rgbno);
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
index 0d10ce2910c2..4e7c540c8d23 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -607,7 +607,7 @@ xchk_xref_is_rt_cow_staging(
/* CoW lookup returned a shared extent record? */
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
- xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+ xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
/* Must be at least as long as what was passed in */
if (rc.rc_blockcount < len)
diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c
index 043be93c7148..564d19a97a2f 100644
--- a/fs/xfs/scrub/rtrmap.c
+++ b/fs/xfs/scrub/rtrmap.c
@@ -87,6 +87,9 @@ xchk_rtrmapbt_is_shareable(
return false;
if (irec->rm_flags & XFS_RMAP_UNWRITTEN)
return false;
+ if (irec->rm_owner == XFS_RMAP_OWN_COW ||
+ irec->rm_owner == XFS_RMAP_OWN_FS)
+ return false;
return true;
}
@@ -146,6 +149,9 @@ xchk_rtrmap_mergeable(
return false;
if (r1->rm_flags != r2->rm_flags)
return false;
+ if (r1->rm_owner == XFS_RMAP_OWN_COW ||
+ r1->rm_owner == XFS_RMAP_OWN_FS)
+ return true;
return r1->rm_offset + r1->rm_blockcount == r2->rm_offset;
}
@@ -209,7 +215,7 @@ xchk_rtrmapbt_xref(
xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock),
irec->rm_blockcount);
if (irec->rm_owner == XFS_RMAP_OWN_COW)
- xchk_xref_is_cow_staging(sc, irec->rm_startblock,
+ xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock,
irec->rm_blockcount);
else
xchk_rtrmapbt_xref_rtrefc(sc, irec);
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index a3f1abc91390..6d7d3523b71f 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -11,7 +11,7 @@ struct xfs_scrub;
struct xchk_relax {
unsigned long next_resched;
unsigned int resched_nr;
- bool interruptible;
+ bool killable;
};
/* Yield to the scheduler at most 10x per second. */
@@ -21,7 +21,7 @@ struct xchk_relax {
(struct xchk_relax){ \
.next_resched = XCHK_RELAX_NEXT, \
.resched_nr = 0, \
- .interruptible = true, \
+ .killable = true, \
}
/*
@@ -45,7 +45,7 @@ static inline int xchk_maybe_relax(struct xchk_relax *widget)
widget->next_resched = XCHK_RELAX_NEXT;
}
- if (widget->interruptible && fatal_signal_pending(current))
+ if (widget->killable && fatal_signal_pending(current))
return -EINTR;
return 0;
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 1b7d9e07a27d..d5d39d82749e 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2671,41 +2671,6 @@ TRACE_EVENT(xrep_cow_mark_file_range,
__entry->blockcount)
);
-TRACE_EVENT(xrep_cow_replace_mapping,
- TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
- xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
- TP_ARGS(ip, irec, new_startblock, new_blockcount),
- TP_STRUCT__entry(
- __field(dev_t, dev)
- __field(xfs_ino_t, ino)
- __field(xfs_fsblock_t, startblock)
- __field(xfs_fileoff_t, startoff)
- __field(xfs_filblks_t, blockcount)
- __field(xfs_exntst_t, state)
- __field(xfs_fsblock_t, new_startblock)
- __field(xfs_extlen_t, new_blockcount)
- ),
- TP_fast_assign(
- __entry->dev = ip->i_mount->m_super->s_dev;
- __entry->ino = I_INO(ip);
- __entry->startoff = irec->br_startoff;
- __entry->startblock = irec->br_startblock;
- __entry->blockcount = irec->br_blockcount;
- __entry->state = irec->br_state;
- __entry->new_startblock = new_startblock;
- __entry->new_blockcount = new_blockcount;
- ),
- TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
- MAJOR(__entry->dev), MINOR(__entry->dev),
- __entry->ino,
- __entry->startoff,
- __entry->startblock,
- __entry->blockcount,
- __entry->state,
- __entry->new_startblock,
- __entry->new_blockcount)
-);
-
TRACE_EVENT(xrep_cow_free_staging,
TP_PROTO(const struct xfs_perag *pag, xfs_agblock_t agbno,
xfs_extlen_t blockcount),
diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c
index c7c4a71b6fa7..2ce24bfe4c0f 100644
--- a/fs/xfs/scrub/xfarray.c
+++ b/fs/xfs/scrub/xfarray.c
@@ -487,8 +487,7 @@ xfarray_sortinfo_alloc(
xfarray_sortinfo_lo(si)[0] = 0;
xfarray_sortinfo_hi(si)[0] = array->nr - 1;
si->relax = INIT_XCHK_RELAX;
- if (flags & XFARRAY_SORT_KILLABLE)
- si->relax.interruptible = false;
+ si->relax.killable = !!(flags & XFARRAY_SORT_KILLABLE);
trace_xfarray_sort(si, nr_bytes);
*infop = si;
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 3b9f262f8e91..c88b9ade7389 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1744,3 +1744,92 @@ out_trans_cancel:
xfs_trans_cancel(tp);
goto out_unlock_ilock;
}
+
+/*
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary. @icur
+ * must point to iext tree leaf containing @got.
+ */
+void
+xfs_bmap_replace_cow_mapping(
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
+{
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ xfs_fileoff_t rep_endoff =
+ rep->br_startoff + rep->br_blockcount;
+ xfs_fileoff_t got_endoff =
+ got->br_startoff + got->br_blockcount;
+ uint32_t state = BMAP_COWFORK;
+
+ ASSERT(rep->br_blockcount > 0);
+ ASSERT(!isnullstartblock(got->br_startblock));
+ ASSERT(got->br_startoff <= rep->br_startoff);
+ ASSERT(got_endoff >= rep_endoff);
+
+ trace_xfs_bmap_replace_cow_mapping(ip, got, rep);
+
+ if (got->br_startoff == rep->br_startoff)
+ state |= BMAP_LEFT_FILLING;
+ if (got_endoff == rep_endoff)
+ state |= BMAP_RIGHT_FILLING;
+
+ switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+ case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
+ /*
+ * Replacement matches the whole mapping, update the record.
+ */
+ xfs_iext_update_extent(ip, state, icur, rep);
+ break;
+ case BMAP_LEFT_FILLING:
+ /*
+ * Replace the first part of the mapping: Update the cursor
+ * position with the new mapping, then add a record with the
+ * tail of the old mapping.
+ */
+ got->br_startoff = rep_endoff;
+ got->br_blockcount -= rep->br_blockcount;
+ got->br_startblock += rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, rep);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, got, state);
+ break;
+ case BMAP_RIGHT_FILLING:
+ /*
+ * Replacing the last part of the mapping. Shorten the current
+ * mapping then add a record with the new mapping.
+ */
+ got->br_blockcount -= rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ break;
+ case 0:
+ /*
+ * Replacing the middle of the extent. Shorten the current
+ * mapping, add a new record with the new mapping, and add a
+ * second new record with the tail of the old mapping.
+ */
+ got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+ struct xfs_bmbt_irec new = {
+ .br_startoff = rep_endoff,
+ .br_blockcount = got_endoff - rep_endoff,
+ .br_state = got->br_state,
+ .br_startblock = got->br_startblock +
+ rep->br_blockcount +
+ got->br_blockcount,
+ };
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, &new, state);
+ break;
+ }
+}
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index c477b3361630..eaaf094154b9 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -81,4 +81,8 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
+void xfs_bmap_replace_cow_mapping(struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur, struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep);
+
#endif /* __XFS_BMAP_UTIL_H__ */
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
index edc368938f30..639f875a8fb2 100644
--- a/fs/xfs/xfs_log_cil.c
+++ b/fs/xfs/xfs_log_cil.c
@@ -1710,7 +1710,7 @@ xlog_cil_push_background(
static void
xlog_cil_push_now(
struct xlog *log,
- xfs_lsn_t push_seq,
+ xfs_csn_t push_seq,
bool async)
{
struct xfs_cil *cil = log->l_cilp;
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 5f984bf5698a..fdb011e6ef60 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1907,18 +1907,20 @@ xlog_recover_reorder_trans(
list_for_each_entry_safe(item, n, &sort_list, ri_list) {
enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST;
+ /* a committed item with no regions has a NULL ri_buf[0] */
+ if (!item->ri_cnt || !item->ri_buf) {
+ xfs_warn(log->l_mp,
+ "%s: committed log item has no regions",
+ __func__);
+ error = -EFSCORRUPTED;
+ break;
+ }
+
item->ri_ops = xlog_find_item_ops(item);
if (!item->ri_ops) {
xfs_warn(log->l_mp,
"%s: unrecognized type of log operation (%d)",
__func__, ITEM_TYPE(item));
- ASSERT(0);
- /*
- * return the remaining items back to the transaction
- * item list so they can be freed in caller.
- */
- if (!list_empty(&sort_list))
- list_splice_init(&sort_list, &trans->r_itemq);
error = -EFSCORRUPTED;
break;
}
@@ -1946,7 +1948,15 @@ xlog_recover_reorder_trans(
}
}
- ASSERT(list_empty(&sort_list));
+ /*
+ * Return the remaining items back to the transaction item list so they
+ * can be freed in caller. This should only happen when we encounter
+ * an error.
+ */
+ if (!list_empty(&sort_list)) {
+ ASSERT(error);
+ list_splice_init(&sort_list, &trans->r_itemq);
+ }
if (!list_empty(&buffer_list))
list_splice(&buffer_list, &trans->r_itemq);
if (!list_empty(&item_list))
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index a5c188b78138..480136136635 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -440,6 +440,7 @@ xfs_reflink_fill_cow_hole(
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
xfs_filblks_t resaligned;
+ unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
unsigned int dblocks = 0, rblocks = 0;
int nimaps;
int error;
@@ -465,6 +466,22 @@ xfs_reflink_fill_cow_hole(
*lockmode = XFS_ILOCK_EXCL;
+ /*
+ * The data fork mapping may have changed while we dropped the ILOCK
+ * (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
+ * CoW cycle including xfs_reflink_end_cow(), which remaps this offset
+ * and drops the refcount of the old shared block). Re-read it so the
+ * shared-status recheck below and the caller's in-place iomap both
+ * operate on the current mapping rather than a stale physical block.
+ */
+ if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
+ nimaps = 1;
+ error = xfs_bmapi_read(ip, imap->br_startoff,
+ imap->br_blockcount, imap, &nimaps, 0);
+ if (error)
+ goto out_trans_cancel;
+ }
+
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found);
if (error || !*shared)
goto out_trans_cancel;
@@ -511,6 +528,8 @@ xfs_reflink_fill_delalloc(
bool found;
do {
+ unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
+
xfs_iunlock(ip, *lockmode);
*lockmode = 0;
@@ -521,6 +540,23 @@ xfs_reflink_fill_delalloc(
*lockmode = XFS_ILOCK_EXCL;
+ /*
+ * The data fork mapping may have changed while we dropped the
+ * ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can
+ * complete a full CoW cycle including xfs_reflink_end_cow(),
+ * which remaps this offset and drops the refcount of the old
+ * shared block). Re-read it so the shared-status recheck
+ * below and the caller's in-place iomap both operate on the
+ * current mapping rather than a stale physical block.
+ */
+ if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
+ nimaps = 1;
+ error = xfs_bmapi_read(ip, imap->br_startoff,
+ imap->br_blockcount, imap, &nimaps, 0);
+ if (error)
+ goto out_trans_cancel;
+ }
+
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared,
&found);
if (error || !*shared)
diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c
index 676777064c2d..b62712187324 100644
--- a/fs/xfs/xfs_sysfs.c
+++ b/fs/xfs/xfs_sysfs.c
@@ -781,6 +781,23 @@ static const struct kobj_type xfs_zoned_ktype = {
};
int
+xfs_zoned_sysfs_init(struct xfs_mount *mp)
+{
+ if (!IS_ENABLED(CONFIG_XFS_RT) || !xfs_has_zoned(mp))
+ return 0;
+
+ return xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype,
+ &mp->m_kobj, "zoned");
+}
+
+void
+xfs_zoned_sysfs_del(struct xfs_mount *mp)
+{
+ if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp))
+ xfs_sysfs_del(&mp->m_zoned_kobj);
+}
+
+int
xfs_mount_sysfs_init(
struct xfs_mount *mp)
{
@@ -820,14 +837,6 @@ xfs_mount_sysfs_init(
if (error)
goto out_remove_error_dir;
- if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp)) {
- /* .../xfs/<dev>/zoned/ */
- error = xfs_sysfs_init(&mp->m_zoned_kobj, &xfs_zoned_ktype,
- &mp->m_kobj, "zoned");
- if (error)
- goto out_remove_error_dir;
- }
-
return 0;
out_remove_error_dir:
@@ -846,9 +855,6 @@ xfs_mount_sysfs_del(
struct xfs_error_cfg *cfg;
int i, j;
- if (IS_ENABLED(CONFIG_XFS_RT) && xfs_has_zoned(mp))
- xfs_sysfs_del(&mp->m_zoned_kobj);
-
for (i = 0; i < XFS_ERR_CLASS_MAX; i++) {
for (j = 0; j < XFS_ERR_ERRNO_MAX; j++) {
cfg = &mp->m_error_cfg[i][j];
diff --git a/fs/xfs/xfs_sysfs.h b/fs/xfs/xfs_sysfs.h
index 1622fe80ad3e..25e5f8fae2f3 100644
--- a/fs/xfs/xfs_sysfs.h
+++ b/fs/xfs/xfs_sysfs.h
@@ -53,6 +53,8 @@ xfs_sysfs_del(
}
int xfs_mount_sysfs_init(struct xfs_mount *mp);
+int xfs_zoned_sysfs_init(struct xfs_mount *mp);
+void xfs_zoned_sysfs_del(struct xfs_mount *mp);
void xfs_mount_sysfs_del(struct xfs_mount *mp);
#endif /* __XFS_SYSFS_H__ */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index d478693674f9..aeb89ac53bf1 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -6439,6 +6439,47 @@ TRACE_EVENT(xfs_verify_media_error,
__entry->error)
);
+TRACE_EVENT(xfs_bmap_replace_cow_mapping,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
+ const struct xfs_bmbt_irec *rep),
+ TP_ARGS(ip, got, rep),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(xfs_fsblock_t, startblock)
+ __field(xfs_fileoff_t, startoff)
+ __field(xfs_filblks_t, blockcount)
+ __field(xfs_exntst_t, state)
+ __field(xfs_fileoff_t, new_startoff)
+ __field(xfs_fsblock_t, new_startblock)
+ __field(xfs_extlen_t, new_blockcount)
+ __field(xfs_exntst_t, new_state)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = I_INO(ip);
+ __entry->startoff = got->br_startoff;
+ __entry->startblock = got->br_startblock;
+ __entry->blockcount = got->br_blockcount;
+ __entry->state = got->br_state;
+ __entry->new_startoff = rep->br_startoff;
+ __entry->new_startblock = rep->br_startblock;
+ __entry->new_blockcount = rep->br_blockcount;
+ __entry->new_state = rep->br_state;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->startoff,
+ __entry->startblock,
+ __entry->blockcount,
+ __entry->state,
+ __entry->new_startoff,
+ __entry->new_startblock,
+ __entry->new_blockcount,
+ __entry->new_state)
+);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index 08d8b34f467e..7d13fa7ab30a 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -21,6 +21,7 @@
#include "xfs_rtbitmap.h"
#include "xfs_rtrmap_btree.h"
#include "xfs_zone_alloc.h"
+#include "xfs_sysfs.h"
#include "xfs_zone_priv.h"
#include "xfs_zones.h"
#include "xfs_trace.h"
@@ -1420,11 +1421,17 @@ xfs_mount_zones(
if (error)
goto out_free_zone_info;
+ error = xfs_zoned_sysfs_init(mp);
+ if (error)
+ goto out_zone_gc_unmount;
+
xfs_info(mp, "%u zones of %u blocks (%u max open zones)",
mp->m_sb.sb_rgcount, iz.zone_capacity, mp->m_max_open_zones);
trace_xfs_zones_mount(mp);
return 0;
+out_zone_gc_unmount:
+ xfs_zone_gc_unmount(mp);
out_free_zone_info:
xfs_free_zone_info(mp->m_zone_info);
return error;
@@ -1434,6 +1441,7 @@ void
xfs_unmount_zones(
struct xfs_mount *mp)
{
+ xfs_zoned_sysfs_del(mp);
xfs_zone_gc_unmount(mp);
xfs_free_zone_info(mp->m_zone_info);
}
diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h
index 6a46f755daba..7e077484c5bb 100644
--- a/include/drm/drm_utils.h
+++ b/include/drm/drm_utils.h
@@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height);
struct drm_panel_backlight_quirk {
u16 min_brightness;
u32 brightness_mask;
+ bool force_pwm;
};
const struct drm_panel_backlight_quirk *
diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h
index 17f9f015bf7d..071221fe5c57 100644
--- a/include/dt-bindings/power/fsl,imx93-power.h
+++ b/include/dt-bindings/power/fsl,imx93-power.h
@@ -11,5 +11,6 @@
#define IMX93_MEDIABLK_PD_PXP 2
#define IMX93_MEDIABLK_PD_LCDIF 3
#define IMX93_MEDIABLK_PD_ISI 4
+#define IMX93_MEDIABLK_PD_MIPI_PHY 5
#endif
diff --git a/include/dt-bindings/reset/altr,rst-mgr-s10.h b/include/dt-bindings/reset/altr,rst-mgr-s10.h
index 04c4d0c6fd34..c2505b9eb63e 100644
--- a/include/dt-bindings/reset/altr,rst-mgr-s10.h
+++ b/include/dt-bindings/reset/altr,rst-mgr-s10.h
@@ -22,7 +22,7 @@
#define USB0_RESET 35
#define USB1_RESET 36
#define NAND_RESET 37
-/* 38 is empty */
+#define COMBOPHY_RESET 38
#define SDMMC_RESET 39
#define EMAC0_OCP_RESET 40
#define EMAC1_OCP_RESET 41
diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index 17eca3dfc59e..e71d83ee0aef 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -421,6 +421,13 @@ struct ffa_mem_region {
#define FFA_EMAD_HAS_IMPDEF_FIELD(version) ((version) >= FFA_VERSION_1_2)
#define FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version) ((version) > FFA_VERSION_1_0)
+/* The layout changed from FFA_VERSION_1_0 and the region includes an
+ * ep_mem_offset.
+ */
+#define FFA_MEM_REGION_SZ(version) (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET((version)) ?\
+ offsetof(struct ffa_mem_region, ep_mem_offset) :\
+ sizeof(struct ffa_mem_region))
+
static inline u32 ffa_emad_size_get(u32 ffa_version)
{
u32 sz;
@@ -445,7 +452,7 @@ ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version)
if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version))
offset += offsetof(struct ffa_mem_region, ep_mem_offset);
else
- offset += sizeof(struct ffa_mem_region);
+ offset += buf->ep_mem_offset;
return offset;
}
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 240401d9b25b..c09b7994de4e 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -578,6 +578,7 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset);
struct btf *btf_parse_vmlinux(void);
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
+int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag);
bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog);
diff --git a/include/linux/can/dev/peak_canfd.h b/include/linux/can/dev/peak_canfd.h
index d3788a3d0942..056e0efa649f 100644
--- a/include/linux/can/dev/peak_canfd.h
+++ b/include/linux/can/dev/peak_canfd.h
@@ -3,7 +3,7 @@
* CAN driver for PEAK System micro-CAN based adapters
*
* Copyright (C) 2003-2025 PEAK System-Technik GmbH
- * Author: Stéphane Grosjean <stephane.grosjean@hms-networks.com>
+ * Author: Stéphane Grosjean <s.grosjean@peak-system.fr>
*/
#ifndef PUCAN_H
#define PUCAN_H
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index 18f9c662cf4c..c109722b1969 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -857,7 +857,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
const struct ieee80211_multi_link_elem *mle = (const void *)data;
u8 fixed = sizeof(*mle);
u8 common = 0;
- bool check_common_len = false;
+ u8 common_len;
u16 control;
if (!data || len < fixed)
@@ -868,7 +868,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) {
case IEEE80211_ML_CONTROL_TYPE_BASIC:
common += sizeof(struct ieee80211_mle_basic_common_info);
- check_common_len = true;
if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID)
common += 1;
if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT)
@@ -888,9 +887,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
common += sizeof(struct ieee80211_mle_preq_common_info);
if (control & IEEE80211_MLC_PREQ_PRES_MLD_ID)
common += 1;
- check_common_len = true;
break;
case IEEE80211_ML_CONTROL_TYPE_RECONF:
+ common += 1;
if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR)
common += ETH_ALEN;
if (control & IEEE80211_MLC_RECONF_PRES_EML_CAPA)
@@ -902,7 +901,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
break;
case IEEE80211_ML_CONTROL_TYPE_TDLS:
common += sizeof(struct ieee80211_mle_tdls_common_info);
- check_common_len = true;
break;
case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS:
common = ETH_ALEN + 1;
@@ -915,11 +913,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
if (len < fixed + common)
return false;
- if (!check_common_len)
- return true;
+ common_len = mle->variable[0];
- /* if present, common length is the first octet there */
- return mle->variable[0] >= common;
+ return common_len >= common && common_len <= len - fixed;
}
/**
diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h
index a8553401b1c9..d5e35f24738d 100644
--- a/include/linux/skmsg.h
+++ b/include/linux/skmsg.h
@@ -551,20 +551,6 @@ static inline void psock_progs_drop(struct sk_psock_progs *progs)
psock_set_prog(&progs->skb_verdict, NULL);
}
-/* for tcp only, sk is locked */
-static inline ssize_t sk_psock_msg_inq(struct sock *sk)
-{
- struct sk_psock *psock;
- ssize_t inq = 0;
-
- psock = sk_psock_get(sk);
- if (likely(psock)) {
- inq = sk_psock_get_msg_len_nolock(psock);
- sk_psock_put(sk, psock);
- }
- return inq;
-}
-
/* for udp only, sk is not locked */
static inline ssize_t sk_msg_first_len(struct sock *sk)
{
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4ca09298e11a..e7133ff87fbf 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -2430,6 +2430,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 *name, u8 name_len);
+u8 hci_to_mgmt_reason(u8 err);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 8188ad200de5..f5abf1db7558 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -7228,7 +7228,7 @@ struct wireless_dev {
enum ieee80211_bss_type conn_bss_type;
u32 conn_owner_nlportid;
- struct work_struct disconnect_wk;
+ struct wiphy_work disconnect_wk;
u8 disconnect_bssid[ETH_ALEN];
struct list_head event_list;
@@ -7265,7 +7265,7 @@ struct wireless_dev {
struct list_head pmsr_list;
spinlock_t pmsr_lock;
- struct work_struct pmsr_free_wk;
+ struct wiphy_work pmsr_free_wk;
unsigned long unprot_beacon_reported;
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index affee44bd38e..cccc662561aa 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -312,7 +312,8 @@ struct sctp_cookie {
__u8 auth_random[sizeof(struct sctp_paramhdr) +
SCTP_AUTH_RANDOM_LENGTH];
- __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2];
+ __u8 auth_hmacs[sizeof(struct sctp_paramhdr) +
+ SCTP_AUTH_NUM_HMACS * sizeof(__u16)];
__u8 auth_chunks[sizeof(struct sctp_paramhdr) + SCTP_AUTH_MAX_CHUNKS];
/* This is a shim for my peer's INIT packet, followed by
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 519a0156a05c..a6d69aaa6cd2 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -162,6 +162,8 @@ struct xfrm_dev_offload {
*/
struct net_device *real_dev;
unsigned long offload_handle;
+ /* Snapshot the attached device index for dump paths. */
+ int ifindex;
u8 dir : 2;
u8 type : 2;
u8 flags : 2;
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 029f5115b2ea..8694eeadd753 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -408,7 +408,6 @@ void scsi_attach_vpd(struct scsi_device *sdev);
void scsi_cdl_check(struct scsi_device *sdev);
int scsi_cdl_enable(struct scsi_device *sdev, bool enable);
-extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern int __must_check scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);
extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 7e2011830ba4..f6b286fa59f2 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -750,6 +750,9 @@ struct Scsi_Host {
*/
struct device *dma_dev;
+ /* Used for an rcu-synchronizing eh wakeup */
+ struct work_struct eh_work;
+
/* Delay for runtime autosuspend */
int rpm_autosuspend_delay;
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
index 4bcbf19d75ac..53f6d02889db 100644
--- a/include/soc/tegra/pmc.h
+++ b/include/soc/tegra/pmc.h
@@ -210,7 +210,6 @@ tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id)
bool tegra_pmc_cpu_is_powered(unsigned int cpuid);
int tegra_pmc_cpu_power_on(unsigned int cpuid);
int tegra_pmc_cpu_remove_clamping(unsigned int cpuid);
-bool tegra_pmc_core_domain_state_synced(void);
#if defined(CONFIG_SOC_TEGRA_PMC) && defined(CONFIG_PM_SLEEP)
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
@@ -230,6 +229,10 @@ static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
{
}
#endif
+#endif
+
+#if defined(CONFIG_ARM) && defined(CONFIG_SOC_TEGRA_PMC)
+bool tegra_pmc_core_domain_state_synced(void);
#else
/* needed for COMPILE_TEST */
static inline bool tegra_pmc_core_domain_state_synced(void)
diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c
index 5a50f0675fe5..cf2bd068e331 100644
--- a/io_uring/bpf-ops.c
+++ b/io_uring/bpf-ops.c
@@ -168,6 +168,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops)
if (ctx->bpf_ops)
return -EBUSY;
+ if (ops->priv)
+ return -EBUSY;
if (WARN_ON_ONCE(!ops->loop_step))
return -EINVAL;
diff --git a/io_uring/fs.c b/io_uring/fs.c
index d0580c754bf8..26ea841a22e7 100644
--- a/io_uring/fs.c
+++ b/io_uring/fs.c
@@ -110,7 +110,8 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
const char __user *fname;
int err;
- if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in)
+ if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in ||
+ sqe->addr3 || sqe->__pad2[0])
return -EINVAL;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index b6b969b55e12..de0129bceaba 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -328,8 +328,8 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
buf = io_ring_head_to_buf(br, ++head, bl->mask);
} while (--nr_iovs);
- if (arg->mode & KBUF_MODE_FREE)
- kfree(arg->iovs);
+ if (arg->iovs != org_iovs && (arg->mode & KBUF_MODE_FREE))
+ kfree(org_iovs);
if (head == tail)
req->flags |= REQ_F_BL_EMPTY;
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 64572f85edc8..c4673a54c4ba 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -9114,6 +9114,35 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
}
+/*
+ * Check a single KF_* @flag on a kfunc across all of its hook sets.
+ * Returns:
+ * * 1 if @flag is set
+ * * 0 if @flag is not set
+ * * -EINVAL if @flag is set inconsistently across the sets
+ * * -ENOENT if kfunc_btf_id is not a registered kfunc
+ */
+int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag)
+{
+ enum btf_kfunc_hook hook;
+ int res = -ENOENT;
+ bool is_set;
+ u32 *flags;
+
+ for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
+ flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
+ if (!flags)
+ continue;
+ is_set = *flags & flag;
+ if (res < 0)
+ res = is_set;
+ else if (res != is_set)
+ return -EINVAL;
+ }
+
+ return res;
+}
+
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog)
{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6515d4d3c003..99444eae917e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2584,24 +2584,25 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
#define KF_IMPL_SUFFIX "_impl"
-static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
+static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_log *log,
struct btf *btf,
const char *func_name)
{
- char *buf = env->tmp_str_buf;
const struct btf_type *func;
+ char buf[KSYM_NAME_LEN];
s32 impl_id;
int len;
- len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX);
- if (len < 0 || len >= TMP_STR_BUF_LEN) {
- verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
+ len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX);
+ if (len < 0 || len >= sizeof(buf)) {
+ bpf_log(log, "function name %s%s is too long\n",
+ func_name, KF_IMPL_SUFFIX);
return NULL;
}
impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC);
if (impl_id <= 0) {
- verbose(env, "cannot find function %s in BTF\n", buf);
+ bpf_log(log, "cannot find function %s in BTF\n", buf);
return NULL;
}
@@ -2653,7 +2654,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
* can be found through the counterpart _impl kfunc.
*/
if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS))
- func_proto = find_kfunc_impl_proto(env, btf, func_name);
+ func_proto = find_kfunc_impl_proto(&env->log, btf, func_name);
else
func_proto = btf_type_by_id(btf, func->type);
@@ -5326,14 +5327,11 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
static int __check_buffer_access(struct bpf_verifier_env *env,
const char *buf_info,
const struct bpf_reg_state *reg,
- argno_t argno, int off, int size)
+ argno_t argno, int off, int size,
+ u32 *access_end)
{
- if (off < 0) {
- verbose(env,
- "%s invalid %s buffer access: off=%d, size=%d\n",
- reg_arg_name(env, argno), buf_info, off, size);
- return -EACCES;
- }
+ s64 start;
+
if (!tnum_is_const(reg->var_off)) {
char tn_buf[48];
@@ -5344,6 +5342,15 @@ static int __check_buffer_access(struct bpf_verifier_env *env,
return -EACCES;
}
+ start = (s64)reg->var_off.value + off;
+ if (start < 0) {
+ verbose(env,
+ "%s invalid negative %s buffer offset: off=%d, var_off=%lld\n",
+ reg_arg_name(env, argno), buf_info, off, (s64)reg->var_off.value);
+ return -EACCES;
+ }
+
+ *access_end = start + size;
return 0;
}
@@ -5351,14 +5358,14 @@ static int check_tp_buffer_access(struct bpf_verifier_env *env,
const struct bpf_reg_state *reg,
argno_t argno, int off, int size)
{
+ u32 access_end;
int err;
- err = __check_buffer_access(env, "tracepoint", reg, argno, off, size);
+ err = __check_buffer_access(env, "tracepoint", reg, argno, off, size, &access_end);
if (err)
return err;
- env->prog->aux->max_tp_access = max(reg->var_off.value + off + size,
- env->prog->aux->max_tp_access);
+ env->prog->aux->max_tp_access = max(access_end, env->prog->aux->max_tp_access);
return 0;
}
@@ -5370,13 +5377,14 @@ static int check_buffer_access(struct bpf_verifier_env *env,
u32 *max_access)
{
const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr";
+ u32 access_end;
int err;
- err = __check_buffer_access(env, buf_info, reg, argno, off, size);
+ err = __check_buffer_access(env, buf_info, reg, argno, off, size, &access_end);
if (err)
return err;
- *max_access = max(reg->var_off.value + off + size, *max_access);
+ *max_access = max(access_end, *max_access);
return 0;
}
@@ -18873,6 +18881,47 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b
return -EINVAL;
}
+/*
+ * Resolve the prototype describing a trace target's real ABI. A
+ * KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public
+ * prototype, so use the _impl prototype; other targets use their own.
+ */
+static const struct btf_type *
+btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id)
+{
+ const struct btf_type *func;
+ struct module *mod = NULL;
+ const char *name;
+ int implicit;
+
+ func = btf_type_by_id(btf, func_id);
+ if (!func || !btf_type_is_func(func))
+ return NULL;
+ name = btf_name_by_offset(btf, func->name_off);
+
+ /*
+ * btf_kfunc_check_flag() reads kfunc_set_tab, which for a module is
+ * stable only once it is live; hold a module ref across the read to
+ * exclude a concurrent module load.
+ */
+ if (btf_is_module(btf)) {
+ mod = btf_try_get_module(btf);
+ if (!mod)
+ return NULL;
+ }
+ implicit = btf_kfunc_check_flag(btf, func_id, KF_IMPLICIT_ARGS);
+ module_put(mod);
+
+ if (implicit == -EINVAL) {
+ bpf_log(log, "kfunc %s has inconsistent KF_IMPLICIT_ARGS\n", name);
+ return NULL;
+ }
+ if (implicit > 0)
+ return find_kfunc_impl_proto(log, btf, name);
+
+ return btf_type_by_id(btf, func->type);
+}
+
int bpf_check_attach_target(struct bpf_verifier_log *log,
const struct bpf_prog *prog,
const struct bpf_prog *tgt_prog,
@@ -19121,8 +19170,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
if (prog_extension &&
btf_check_type_match(log, prog, btf, t))
return -EINVAL;
- t = btf_type_by_id(btf, t->type);
- if (!btf_type_is_func_proto(t))
+ t = btf_attach_func_proto(log, btf, btf_id);
+ if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) &&
@@ -19405,10 +19454,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
tname = btf_name_by_offset(btf, t->name_off);
if (!tname)
return -EINVAL;
- if (!btf_type_is_func(t))
- return -EINVAL;
- t = btf_type_by_id(btf, t->type);
- if (!btf_type_is_func_proto(t))
+ t = btf_attach_func_proto(NULL, btf, btf_id);
+ if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel);
if (err < 0)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 591e3aa487fc..45944b3e31ca 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2653,7 +2653,12 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs)
migrate = is_memory_migrate(cs);
- mpol_rebind_mm(mm, &cs->mems_allowed);
+ /*
+ * For v1 we can have empty effective_mems, but we cannot
+ * attach any tasks (see cpuset_can_attach_check()). For v2,
+ * effective_mems is guaranteed to not be empty.
+ */
+ mpol_rebind_mm(mm, &cs->effective_mems);
if (migrate)
cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems);
else
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 691d53fe0f64..e3fa7b2fac9d 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -479,6 +479,18 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags)
*/
DEFINE_PER_CPU(struct rq *, scx_locked_rq_state);
+static void switch_rq_lock(struct rq *from, struct rq *to)
+{
+ bool tracked = scx_locked_rq() == from;
+
+ if (tracked)
+ update_locked_rq(NULL);
+ raw_spin_rq_unlock(from);
+ raw_spin_rq_lock(to);
+ if (tracked)
+ update_locked_rq(to);
+}
+
/*
* Flipped on enable per sch->is_cid_type. Declared in internal.h so
* subsystem inlines can read it.
@@ -2274,8 +2286,7 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
deactivate_task(src_rq, p, 0);
set_task_cpu(p, cpu_of(dst_rq));
- raw_spin_rq_unlock(src_rq);
- raw_spin_rq_lock(dst_rq);
+ switch_rq_lock(src_rq, dst_rq);
/*
* We want to pass scx-specific enq_flags but activate_task() will
@@ -2307,6 +2318,7 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
* no to the BPF scheduler initiated migrations while offline.
*
* The caller must ensure that @p and @rq are on different CPUs.
+ * If enforce == true, caller must hold @p's rq lock.
*/
static bool task_can_run_on_remote_rq(struct scx_sched *sch,
struct task_struct *p, struct rq *rq,
@@ -2314,6 +2326,14 @@ static bool task_can_run_on_remote_rq(struct scx_sched *sch,
{
s32 cpu = cpu_of(rq);
+ /*
+ * To prevent races with @p still running on its old CPU while switching
+ * out, make sure we're holding @p's rq lock so as not to risk
+ * erroneously killing the BPF scheduler.
+ */
+ if (enforce)
+ lockdep_assert_rq_held(task_rq(p));
+
WARN_ON_ONCE(task_cpu(p) == cpu);
/*
@@ -2581,13 +2601,6 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
return;
}
- if (src_rq != dst_rq &&
- unlikely(!task_can_run_on_remote_rq(sch, p, dst_rq, true))) {
- dispatch_enqueue(sch, rq, find_global_dsq(sch, task_cpu(p)), p,
- enq_flags | SCX_ENQ_CLEAR_OPSS | SCX_ENQ_GDSQ_FALLBACK);
- return;
- }
-
/*
* @p is on a possibly remote @src_rq which we need to lock to move the
* task. If dequeue is in progress, it'd be locking @src_rq and waiting
@@ -2606,14 +2619,14 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
/* switch to @src_rq lock */
if (locked_rq != src_rq) {
- raw_spin_rq_unlock(locked_rq);
+ switch_rq_lock(locked_rq, src_rq);
locked_rq = src_rq;
- raw_spin_rq_lock(src_rq);
}
/* task_rq couldn't have changed if we're still the holding cpu */
if (likely(p->scx.holding_cpu == raw_smp_processor_id()) &&
!WARN_ON_ONCE(src_rq != task_rq(p))) {
+ bool fallback = false;
/*
* If @p is staying on the same rq, there's no need to go
* through the full deactivate/activate cycle. Optimize by
@@ -2623,6 +2636,11 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
p->scx.holding_cpu = -1;
dispatch_enqueue(sch, dst_rq, &dst_rq->scx.local_dsq, p,
enq_flags);
+ } else if (unlikely(!task_can_run_on_remote_rq(sch, p, dst_rq, true))) {
+ p->scx.holding_cpu = -1;
+ fallback = true;
+ dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)),
+ p, enq_flags | SCX_ENQ_GDSQ_FALLBACK);
} else {
move_remote_task_to_local_dsq(p, enq_flags,
src_rq, dst_rq);
@@ -2631,15 +2649,13 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
}
/* if the destination CPU is idle, wake it up */
- if (sched_class_above(p->sched_class, dst_rq->curr->sched_class))
+ if (!fallback && sched_class_above(p->sched_class, dst_rq->curr->sched_class))
resched_curr(dst_rq);
}
/* switch back to @rq lock */
- if (locked_rq != rq) {
- raw_spin_rq_unlock(locked_rq);
- raw_spin_rq_lock(rq);
- }
+ if (locked_rq != rq)
+ switch_rq_lock(locked_rq, rq);
}
/**
@@ -2970,24 +2986,38 @@ static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
/*
* @p is getting newly scheduled or got kicked after someone updated its
- * slice. Refresh whether tick can be stopped. See scx_can_stop_tick().
+ * slice. Update SCX_RQ_CAN_STOP_TICK to reflect whether the tick can be
+ * stopped. See scx_can_stop_tick().
+ *
+ * Moreover, refresh the load_avgs just when transitioning in and out of
+ * nohz. In the future, we might want to add a mechanism to update
+ * load_avgs periodically on tick-stopped CPUs.
*/
- if ((p->scx.slice == SCX_SLICE_INF) !=
- (bool)(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
- if (p->scx.slice == SCX_SLICE_INF)
+ if (p->scx.slice == SCX_SLICE_INF) {
+ if (!(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
+ /*
+ * Bypass mode always assigns finite slices, so @p
+ * can't have an infinite slice while bypassing.
+ * Therefore, sched_update_tick_dependency() can safely
+ * evaluate the outgoing task.
+ */
rq->scx.flags |= SCX_RQ_CAN_STOP_TICK;
- else
- rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
+ sched_update_tick_dependency(rq);
- sched_update_tick_dependency(rq);
+ update_other_load_avgs(rq);
+ }
+ } else {
+ if (rq->scx.flags & SCX_RQ_CAN_STOP_TICK) {
+ rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
+ update_other_load_avgs(rq);
+ }
/*
- * For now, let's refresh the load_avgs just when transitioning
- * in and out of nohz. In the future, we might want to add a
- * mechanism which calls the following periodically on
- * tick-stopped CPUs.
+ * @rq still references the outgoing scheduling context. A finite
+ * slice is sufficient by itself to require the tick.
*/
- update_other_load_avgs(rq);
+ if (tick_nohz_full_cpu(cpu_of(rq)))
+ tick_nohz_dep_set_cpu(cpu_of(rq), TICK_DEP_BIT_SCHED);
}
}
@@ -3082,9 +3112,14 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
* sched_class, %SCX_OPS_ENQ_LAST must be set. Tell
* ops.enqueue() that @p is the only one available for this cpu,
* which should trigger an explicit follow-up scheduling event.
+ *
+ * Core scheduling can force this CPU idle while @p stays
+ * runnable. @p's cookie then won't match the core's, so skip
+ * the warning in that case.
*/
if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
- WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST));
+ WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) &&
+ !(sch->ops.flags & SCX_OPS_ENQ_LAST));
do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
} else {
do_enqueue_task(rq, p, 0, -1);
@@ -3648,6 +3683,13 @@ static void scx_disable_task(struct scx_sched *sch, struct task_struct *p)
scx_set_task_state(p, SCX_TASK_READY);
/*
+ * Reset the SCX-managed fields when @p leaves the BPF scheduler's
+ * control, after ops.disable() has observed their final values.
+ */
+ p->scx.dsq_vtime = 0;
+ p->scx.slice = 0;
+
+ /*
* Verify the task is not in BPF scheduler's custody. If flag
* transitions are consistent, the flag should always be clear
* here.
@@ -3925,6 +3967,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p,
if (task_dead_and_done(p))
return;
+ /*
+ * When switching sched_class away from SCX, reweight_task_scx()
+ * is called _after_ scx_disable_task(). Skip calling ops.set_weight()
+ * since the BPF scheduler may have already forgotten the task in
+ * ops.disable().
+ * p->scx.weight will be recalculated in scx_enable_task() if the task
+ * ever returns to SCX class.
+ */
+ if (scx_get_task_state(p) != SCX_TASK_ENABLED)
+ return;
+
p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight));
if (SCX_HAS_OP(sch, set_weight))
SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);
@@ -4301,6 +4354,15 @@ bool scx_can_stop_tick(struct rq *rq)
if (p->sched_class != &ext_sched_class)
return true;
+ /*
+ * @rq->curr may still reference an outgoing EXT task after it has been
+ * dequeued. If no EXT tasks are accounted on @rq, ignore its stale
+ * slice state. If another task is dispatched from a DSQ,
+ * set_next_task_scx() will update the dependency for the incoming task.
+ */
+ if (!rq->scx.nr_running)
+ return true;
+
if (scx_bypassing(sch, cpu_of(rq)))
return false;
@@ -4901,6 +4963,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
cgroup_put(sch_cgroup(sch));
if (sch->sub_kset)
kobject_put(&sch->sub_kset->kobj);
+ if (scx_parent(sch))
+ kobject_put(&scx_parent(sch)->kobj);
#endif /* CONFIG_EXT_SUB_SCHED */
for_each_possible_cpu(cpu) {
@@ -5672,7 +5736,7 @@ static void free_kick_syncs(void)
int cpu;
for_each_possible_cpu(cpu) {
- struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
+ struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
struct scx_kick_syncs *to_free;
to_free = rcu_replace_pointer(*ksyncs, NULL, true);
@@ -6653,7 +6717,7 @@ static int alloc_kick_syncs(void)
* can exceed percpu allocator limits on large machines.
*/
for_each_possible_cpu(cpu) {
- struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
+ struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
struct scx_kick_syncs *new_ksyncs;
WARN_ON_ONCE(rcu_access_pointer(*ksyncs));
@@ -6825,11 +6889,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
sch->ops = *cmd->ops;
}
- rcu_assign_pointer(ops->priv, sch);
-
- sch->kobj.kset = scx_kset;
- INIT_LIST_HEAD(&sch->all);
-
#ifdef CONFIG_EXT_SUB_SCHED
char *buf = kzalloc(PATH_MAX, GFP_KERNEL);
if (!buf) {
@@ -6847,13 +6906,32 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
sch->cgrp = cgrp;
INIT_LIST_HEAD(&sch->children);
INIT_LIST_HEAD(&sch->sibling);
+#endif /* CONFIG_EXT_SUB_SCHED */
- if (parent)
+ /*
+ * Publishing makes @sch visible to scx_prog_sched() readers. Failure
+ * paths after this point must free @sch through kobject_put() whose
+ * release path defers the actual freeing by an RCU grace period.
+ */
+ rcu_assign_pointer(ops->priv, sch);
+
+ sch->kobj.kset = scx_kset;
+ INIT_LIST_HEAD(&sch->all);
+
+#ifdef CONFIG_EXT_SUB_SCHED
+ if (parent) {
+ /*
+ * Pin @parent for @sch's lifetime. The kobject hierarchy pins
+ * it only via @parent->sub_kset, which is dropped during
+ * disable. Released in scx_sched_free_rcu_work().
+ */
+ kobject_get(&parent->kobj);
ret = kobject_init_and_add(&sch->kobj, &scx_ktype,
&parent->sub_kset->kobj,
"sub-%llu", cgroup_id(cgrp));
- else
+ } else {
ret = kobject_init_and_add(&sch->kobj, &scx_ktype, NULL, "root");
+ }
if (ret < 0) {
RCU_INIT_POINTER(ops->priv, NULL);
@@ -6895,7 +6973,6 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
#ifdef CONFIG_EXT_SUB_SCHED
err_free_lb_resched:
- RCU_INIT_POINTER(ops->priv, NULL);
free_cpumask_var(sch->bypass_lb_resched_cpumask);
#endif
err_free_lb_cpumask:
@@ -6988,7 +7065,7 @@ static int validate_ops(struct scx_sched *sch, const struct sched_ext_ops *ops)
* run past the BPF allocation. Skip for cid-form.
*/
if (!sch->is_cid_type && (ops->cpu_acquire || ops->cpu_release))
- pr_warn("ops->cpu_acquire/release() are deprecated, use sched_switch TP instead\n");
+ pr_warn_ratelimited("ops->cpu_acquire/release() are deprecated, use sched_switch TP instead\n");
/*
* Sub-scheduler support is tied to the cid-form struct_ops. A sub-sched
@@ -7686,6 +7763,12 @@ err_unlock_and_disable:
percpu_up_write(&scx_fork_rwsem);
err_disable:
mutex_unlock(&scx_enable_mutex);
+ /*
+ * Some enable failures only return an errno (e.g. -ENOMEM from an
+ * allocation) without calling scx_error(). Record it so
+ * scx_flush_disable_work() runs the disable and ops.exit() fires.
+ */
+ scx_error(sch, "scx_sub_enable() failed (%d)", ret);
scx_flush_disable_work(sch);
cmd->ret = 0;
}
@@ -7806,7 +7889,7 @@ static int bpf_scx_btf_struct_access(struct bpf_verifier_log *log,
off + size <= offsetofend(struct task_struct, scx.slice)) ||
(off >= offsetof(struct task_struct, scx.dsq_vtime) &&
off + size <= offsetofend(struct task_struct, scx.dsq_vtime))) {
- pr_warn("sched_ext: Writing directly to p->scx.slice/dsq_vtime is deprecated, use scx_bpf_task_set_slice/dsq_vtime()");
+ pr_warn_ratelimited("sched_ext: Writing directly to p->scx.slice/dsq_vtime is deprecated, use scx_bpf_task_set_slice/dsq_vtime()\n");
return SCALAR_VALUE;
}
@@ -8796,10 +8879,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
in_balance = this_rq->scx.flags & SCX_RQ_IN_BALANCE;
if (in_balance) {
- if (this_rq != src_rq) {
- raw_spin_rq_unlock(this_rq);
- raw_spin_rq_lock(src_rq);
- }
+ if (this_rq != src_rq)
+ switch_rq_lock(this_rq, src_rq);
} else {
raw_spin_rq_lock(src_rq);
}
@@ -8831,10 +8912,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
dispatched = true;
out:
if (in_balance) {
- if (this_rq != locked_rq) {
- raw_spin_rq_unlock(locked_rq);
- raw_spin_rq_lock(this_rq);
- }
+ if (this_rq != locked_rq)
+ switch_rq_lock(locked_rq, this_rq);
} else {
raw_spin_rq_unlock_irqrestore(locked_rq, flags);
}
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 145272cb4d8a..673059fa9d72 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -1469,21 +1469,24 @@ static const char *scx_enable_state_str[] = {
* The sched_ext core uses a "lock dancing" protocol coordinated by
* p->scx.holding_cpu. When moving a task to a different rq:
*
- * 1. Verify task can be moved (CPU affinity, migration_disabled, etc.)
- * 2. Set p->scx.holding_cpu to the current CPU
- * 3. Set task state to %SCX_OPSS_NONE; dequeue waits while DISPATCHING
+ * 1. Set p->scx.holding_cpu to the current CPU
+ * 2. Set task state to %SCX_OPSS_NONE; dequeue waits while DISPATCHING
* is set, so clearing DISPATCHING first prevents the circular wait
* (safe to lock the rq we need)
- * 4. Unlock the current CPU's rq
- * 5. Lock src_rq (where the task currently lives)
- * 6. Verify p->scx.holding_cpu == current CPU, if not, dequeue won the
+ * 3. Unlock the current CPU's rq
+ * 4. Lock src_rq (where the task currently lives)
+ * 5. Verify p->scx.holding_cpu == current CPU, if not, dequeue won the
* race (dequeue clears holding_cpu to -1 when it takes the task), in
* this case migration is aborted
- * 7. If src_rq == dst_rq: clear holding_cpu and enqueue directly
+ * 6. If src_rq == dst_rq: clear holding_cpu and enqueue directly
* into dst_rq's local DSQ (no lock swap needed)
- * 8. Otherwise: call move_remote_task_to_local_dsq(), which releases
- * src_rq, locks dst_rq, and performs the deactivate/activate
- * migration cycle (dst_rq is held on return)
+ * 7. Otherwise, verify under src_rq lock that the task can be moved to dst_rq
+ * (CPU affinity, migration_disabled, etc.). If not, clear holding_cpu,
+ * leave the task on src_rq, and enqueue it on the fallback DSQ.
+ * 8. Otherwise (i.e. if the task can be moved to dst_rq), call
+ * move_remote_task_to_local_dsq(), which releases src_rq, locks dst_rq,
+ * and performs the deactivate/activate migration cycle
+ * (dst_rq is held on return)
* 9. Unlock dst_rq and re-lock the current CPU's rq to restore
* the lock state expected by the caller
*
diff --git a/lib/bug.c b/lib/bug.c
index 292420f45811..7c1c2c27f58e 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -219,14 +219,12 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;
-#ifdef CONFIG_KUNIT
/*
* Before the once logic so suppressed warnings do not consume
* the single-fire budget of WARN_ON_ONCE().
*/
if (warning && kunit_is_suppressed_warning(true))
return BUG_TRAP_TYPE_WARN;
-#endif
disable_trace_on_warning();
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index 591c1c2a7fb3..83d4c95e079e 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -8,8 +8,7 @@ config CRYPTO_LIB_UTILS
config CRYPTO_LIB_AES
tristate
- # Select dependencies of modes that are part of libaes.
- select CRYPTO_LIB_UTILS if CRYPTO_LIB_AES_CBC_MACS
+ select CRYPTO_LIB_UTILS
config CRYPTO_LIB_AES_ARCH
bool
diff --git a/lib/crypto/md5.c b/lib/crypto/md5.c
index 6bf130cfbbf9..3d2b017a0525 100644
--- a/lib/crypto/md5.c
+++ b/lib/crypto/md5.c
@@ -298,19 +298,5 @@ void hmac_md5_usingrawkey(const u8 *raw_key, size_t raw_key_len,
}
EXPORT_SYMBOL_GPL(hmac_md5_usingrawkey);
-#ifdef md5_mod_init_arch
-static int __init md5_mod_init(void)
-{
- md5_mod_init_arch();
- return 0;
-}
-subsys_initcall(md5_mod_init);
-
-static void __exit md5_mod_exit(void)
-{
-}
-module_exit(md5_mod_exit);
-#endif
-
MODULE_DESCRIPTION("MD5 and HMAC-MD5 library functions");
MODULE_LICENSE("GPL");
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 99e2be39671b..ebd3ac997f64 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -564,6 +564,8 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn,
if (r < 0)
return NULL;
+ flush_cache_vmap(start, end);
+
return pfn_to_page(pfn);
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index b6d963ce26d0..741d658e9630 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2763,7 +2763,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
}
mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
- cp->reason, mgmt_conn);
+ hci_to_mgmt_reason(cp->reason), mgmt_conn);
hci_disconn_cfm(conn, cp->reason);
@@ -3381,22 +3381,6 @@ unlock:
hci_dev_unlock(hdev);
}
-static u8 hci_to_mgmt_reason(u8 err)
-{
- switch (err) {
- case HCI_ERROR_CONNECTION_TIMEOUT:
- return MGMT_DEV_DISCONN_TIMEOUT;
- case HCI_ERROR_REMOTE_USER_TERM:
- case HCI_ERROR_REMOTE_LOW_RESOURCES:
- case HCI_ERROR_REMOTE_POWER_OFF:
- return MGMT_DEV_DISCONN_REMOTE;
- case HCI_ERROR_LOCAL_HOST_TERM:
- return MGMT_DEV_DISCONN_LOCAL_HOST;
- default:
- return MGMT_DEV_DISCONN_UNKNOWN;
- }
-}
-
static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c896d4edd013..532534bc601c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -1054,14 +1054,19 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
* In this kind of scenario skip the update and let the random
* address be updated at the next cycle.
*/
+ rcu_read_lock();
+
if (bacmp(&hdev->random_addr, BDADDR_ANY) &&
(hci_dev_test_flag(hdev, HCI_LE_ADV) ||
hci_lookup_le_connect(hdev))) {
bt_dev_dbg(hdev, "Deferring random address update");
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
+ rcu_read_unlock();
return 0;
}
+ rcu_read_unlock();
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR,
6, rpa, HCI_CMD_TIMEOUT);
}
@@ -2647,12 +2652,17 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev)
/* Cannot disable addr resolution if scanning is enabled or
* when initiating an LE connection.
*/
+ rcu_read_lock();
+
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
hci_lookup_le_connect(hdev)) {
+ rcu_read_unlock();
bt_dev_err(hdev, "Command not allowed when scan/LE connect");
return -EPERM;
}
+ rcu_read_unlock();
+
/* Cannot disable addr resolution if advertising is enabled. */
err = hci_pause_advertising_sync(hdev);
if (err) {
@@ -2790,6 +2800,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
struct hci_conn *conn;
+ rcu_read_lock();
+
conn = hci_conn_hash_lookup_create_pa_sync(hdev);
if (conn) {
struct conn_params pa;
@@ -2799,6 +2811,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
bacpy(&pa.addr, &conn->dst);
pa.addr_type = conn->dst_type;
+ rcu_read_unlock();
+
/* Clear first since there could be addresses left
* behind.
*/
@@ -2808,6 +2822,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
err = hci_le_add_accept_list_sync(hdev, &pa,
&num_entries);
goto done;
+ } else {
+ rcu_read_unlock();
}
}
@@ -2818,10 +2834,13 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
* the controller.
*/
list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
- if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type))
+ rcu_read_lock();
+
+ if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
+ rcu_read_unlock();
continue;
+ }
- /* Pointers not dereferenced, no locks needed */
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
&b->bdaddr,
b->bdaddr_type);
@@ -2829,6 +2848,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
&b->bdaddr,
b->bdaddr_type);
+ rcu_read_unlock();
+
/* If the device is not likely to connect or report,
* remove it from the acceptlist.
*/
@@ -2955,6 +2976,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
if (sent) {
struct hci_conn *conn;
+ rcu_read_lock();
+
conn = hci_conn_hash_lookup_ba(hdev, PA_LINK,
&sent->bdaddr);
if (conn) {
@@ -2979,8 +3002,12 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
phy++;
}
+ rcu_read_unlock();
+
if (num_phy)
goto done;
+ } else {
+ rcu_read_unlock();
}
}
}
@@ -3231,12 +3258,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
/* If there is at least one pending LE connection, we should
* keep the background scan running.
*/
+ bool exists;
/* If controller is connecting, we should not start scanning
* since some controllers are not able to scan and connect at
* the same time.
*/
- if (hci_lookup_le_connect(hdev))
+ rcu_read_lock();
+ exists = hci_lookup_le_connect(hdev);
+ rcu_read_unlock();
+ if (exists)
return 0;
bt_dev_dbg(hdev, "start background scanning");
@@ -3454,6 +3485,7 @@ int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable)
}
static bool disconnected_accept_list_entries(struct hci_dev *hdev)
+ __must_hold(&hdev->lock)
{
struct bdaddr_list *b;
@@ -3494,12 +3526,16 @@ int hci_update_scan_sync(struct hci_dev *hdev)
if (hdev->scanning_paused)
return 0;
+ hci_dev_lock(hdev);
+
if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
disconnected_accept_list_entries(hdev))
scan = SCAN_PAGE;
else
scan = SCAN_DISABLED;
+ hci_dev_unlock(hdev);
+
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;
@@ -6665,6 +6701,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
if (!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES))
hci_pause_advertising_sync(hdev);
+ hci_dev_lock(hdev);
+
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
conn->le_conn_min_interval = params->conn_min_interval;
@@ -6678,6 +6716,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->le_supv_timeout = hdev->le_supv_timeout;
}
+ hci_dev_unlock(hdev);
+
/* If controller is scanning, we stop it since some controllers are
* not able to scan and connect at the same time. Also set the
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete
@@ -7235,13 +7275,13 @@ unlock:
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
- struct hci_conn *acl, struct bt_iso_qos *qos)
+ u16 acl_handle, struct bt_iso_qos *qos)
{
struct hci_cp_le_past_params cp;
int err;
memset(&cp, 0, sizeof(cp));
- cp.handle = cpu_to_le16(acl->handle);
+ cp.handle = cpu_to_le16(acl_handle);
/* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent
* to the Host. HCI_LE_Periodic_Advertising_Report events will be
* enabled with duplicate filtering enabled.
@@ -7306,16 +7346,28 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
* 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that
* user really intended to use PAST.
*/
+ hci_dev_lock(hdev);
+
le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type);
if (le) {
struct hci_conn_params *params;
+ hci_conn_flags_t flags = 0;
+ u16 le_handle = le->handle;
params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type);
- if (params && params->flags & HCI_CONN_FLAG_PAST) {
- err = hci_le_past_params_sync(hdev, conn, le, qos);
+ if (params)
+ flags = params->flags;
+
+ hci_dev_unlock(hdev);
+
+ if (flags & HCI_CONN_FLAG_PAST) {
+ err = hci_le_past_params_sync(hdev, conn, le_handle,
+ qos);
if (!err)
goto done;
}
+ } else {
+ hci_dev_unlock(hdev);
}
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 733a4b70e10c..1db10e0f617f 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3091,6 +3091,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_unpair_device *cp = cmd->param;
struct hci_conn *conn;
+ hci_dev_lock(hdev);
+
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3098,6 +3100,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
+ if (conn)
+ hci_conn_get(conn);
+
+ hci_dev_unlock(hdev);
+
if (!conn)
return 0;
@@ -3105,6 +3112,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+ hci_conn_put(conn);
return 0;
}
@@ -3252,6 +3260,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_disconnect *cp = cmd->param;
struct hci_conn *conn;
+ hci_dev_lock(hdev);
+
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3259,6 +3269,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
+ if (conn)
+ hci_conn_get(conn);
+
+ hci_dev_unlock(hdev);
+
if (!conn)
return -ENOTCONN;
@@ -3266,6 +3281,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+ hci_conn_put(conn);
return 0;
}
@@ -7388,6 +7404,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
rp.max_tx_power = HCI_TX_POWER_INVALID;
}
+ if (conn)
+ hci_conn_put(conn);
+
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status,
&rp, sizeof(rp));
@@ -7402,6 +7421,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
int err;
__le16 handle;
+ hci_dev_lock(hdev);
+
/* Make sure we are still connected */
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
@@ -7409,12 +7430,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
else
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
- if (!conn || conn->state != BT_CONNECTED)
+ if (!conn || conn->state != BT_CONNECTED) {
+ hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
+ }
- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
handle = cpu_to_le16(conn->handle);
+ hci_dev_unlock(hdev);
+
/* Refresh RSSI each time */
err = hci_read_rssi_sync(hdev, handle);
@@ -7548,6 +7573,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err)
}
complete:
+ if (conn)
+ hci_conn_put(conn);
+
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp,
sizeof(rp));
@@ -7564,15 +7592,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data)
memset(&hci_cp, 0, sizeof(hci_cp));
hci_read_clock_sync(hdev, &hci_cp);
+ hci_dev_lock(hdev);
+
/* Make sure connection still exists */
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
- if (!conn || conn->state != BT_CONNECTED)
+ if (!conn || conn->state != BT_CONNECTED) {
+ hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
+ }
- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
hci_cp.handle = cpu_to_le16(conn->handle);
hci_cp.which = 0x01; /* Piconet clock */
+ hci_dev_unlock(hdev);
+
return hci_read_clock_sync(hdev, &hci_cp);
}
@@ -7937,14 +7971,36 @@ unlock:
static int conn_update_sync(struct hci_dev *hdev, void *data)
{
- struct hci_conn_params *params = data;
- struct hci_conn *conn;
+ struct hci_conn *conn = data;
+ struct hci_conn_params *params;
+ struct hci_conn_params local = {};
- conn = hci_conn_hash_lookup_le(hdev, &params->addr, params->addr_type);
- if (!conn)
- return -ECANCELED;
+ hci_dev_lock(hdev);
+
+ if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER)
+ goto cancel;
+
+ params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+ if (!params)
+ goto cancel;
+
+ local.conn_min_interval = params->conn_min_interval;
+ local.conn_max_interval = params->conn_max_interval;
+ local.conn_latency = params->conn_latency;
+ local.supervision_timeout = params->supervision_timeout;
+
+ hci_dev_unlock(hdev);
+
+ return hci_le_conn_update_sync(hdev, conn, &local);
- return hci_le_conn_update_sync(hdev, conn, params);
+cancel:
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+}
+
+static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err)
+{
+ hci_conn_put(data);
}
static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -8054,9 +8110,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
(conn->le_conn_min_interval != min ||
conn->le_conn_max_interval != max ||
conn->le_conn_latency != latency ||
- conn->le_supv_timeout != timeout))
- hci_cmd_sync_queue(hdev, conn_update_sync,
- hci_param, NULL);
+ conn->le_supv_timeout != timeout)) {
+ hci_conn_get(conn);
+ if (hci_cmd_sync_queue(hdev, conn_update_sync,
+ conn,
+ conn_update_sync_destroy) < 0)
+ hci_conn_put(conn);
+ }
}
}
@@ -9848,6 +9908,22 @@ bool mgmt_powering_down(struct hci_dev *hdev)
return false;
}
+u8 hci_to_mgmt_reason(u8 err)
+{
+ switch (err) {
+ case HCI_ERROR_CONNECTION_TIMEOUT:
+ return MGMT_DEV_DISCONN_TIMEOUT;
+ case HCI_ERROR_REMOTE_USER_TERM:
+ case HCI_ERROR_REMOTE_LOW_RESOURCES:
+ case HCI_ERROR_REMOTE_POWER_OFF:
+ return MGMT_DEV_DISCONN_REMOTE;
+ case HCI_ERROR_LOCAL_HOST_TERM:
+ return MGMT_DEV_DISCONN_LOCAL_HOST;
+ default:
+ return MGMT_DEV_DISCONN_UNKNOWN;
+ }
+}
+
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected)
@@ -9909,7 +9985,8 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type, status, true);
+ conn->dst_type,
+ hci_to_mgmt_reason(status), true);
return;
}
diff --git a/net/can/bcm.c b/net/can/bcm.c
index a4bef2c48a55..3d637a1e0ac1 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -58,6 +58,7 @@
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <net/can.h>
#include <net/sock.h>
@@ -92,6 +93,8 @@ MODULE_ALIAS("can-proto-2");
#define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
+static struct workqueue_struct *bcm_wq;
+
/*
* easy access to the first 64 bit of can(fd)_frame payload. cp->data is
* 64 bit aligned so the offset has to be multiples of 8 which is ensured
@@ -105,14 +108,16 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
struct bcm_op {
struct list_head list;
struct rcu_head rcu;
+ struct work_struct work;
int ifindex;
canid_t can_id;
u32 flags;
- unsigned long frames_abs, frames_filtered;
+ atomic_long_t frames_abs, frames_filtered;
struct bcm_timeval ival1, ival2;
struct hrtimer timer, thrtimer;
ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
int rx_ifindex;
+ int if_detected; /* first received ifindex in ANYDEV rx_op mode */
int cfsiz;
u32 count;
u32 nframes;
@@ -124,7 +129,9 @@ struct bcm_op {
struct canfd_frame last_sframe;
struct sock *sk;
struct net_device *rx_reg_dev;
- spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
+ netdevice_tracker rx_reg_dev_tracker;
+ spinlock_t bcm_tx_lock; /* protect tx data and timer updates */
+ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
};
struct bcm_sock {
@@ -224,10 +231,13 @@ static int bcm_proc_show(struct seq_file *m, void *v)
list_for_each_entry_rcu(op, &bo->rx_ops, list) {
- unsigned long reduction;
+ long reduction, frames_filtered, frames_abs;
+
+ frames_filtered = atomic_long_read(&op->frames_filtered);
+ frames_abs = atomic_long_read(&op->frames_abs);
/* print only active entries & prevent division by zero */
- if (!op->frames_abs)
+ if (!frames_abs)
continue;
seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
@@ -249,15 +259,15 @@ static int bcm_proc_show(struct seq_file *m, void *v)
(long long)ktime_to_us(op->kt_ival2));
seq_printf(m, "# recv %ld (%ld) => reduction: ",
- op->frames_filtered, op->frames_abs);
+ frames_filtered, frames_abs);
- reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
+ reduction = 100 - (frames_filtered * 100) / frames_abs;
seq_printf(m, "%s%ld%%\n",
(reduction == 100) ? "near " : "", reduction);
}
- list_for_each_entry(op, &bo->tx_ops, list) {
+ list_for_each_entry_rcu(op, &bo->tx_ops, list) {
seq_printf(m, "tx_op: %03X %s ", op->can_id,
bcm_proc_getifname(net, ifname, op->ifindex));
@@ -275,7 +285,8 @@ static int bcm_proc_show(struct seq_file *m, void *v)
seq_printf(m, "t2=%lld ",
(long long)ktime_to_us(op->kt_ival2));
- seq_printf(m, "# sent %ld\n", op->frames_abs);
+ seq_printf(m, "# sent %ld\n",
+ atomic_long_read(&op->frames_abs));
}
seq_putc(m, '\n');
@@ -285,26 +296,50 @@ static int bcm_proc_show(struct seq_file *m, void *v)
}
#endif /* CONFIG_PROC_FS */
+static void bcm_update_rx_stats(struct bcm_op *op)
+{
+ /* prevent overflow of the reduction% calculation in bcm_proc_show() */
+ if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) {
+ atomic_long_set(&op->frames_filtered, 0);
+ atomic_long_set(&op->frames_abs, 0);
+ }
+}
+
+static void bcm_update_tx_stats(struct bcm_op *op)
+{
+ /* tx_op has no reduction% calculation - use the full range and
+ * just keep the displayed counter non-negative on overflow
+ */
+ if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX)
+ atomic_long_set(&op->frames_abs, 0);
+}
+
/*
* bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
* of the given bcm tx op
*/
-static void bcm_can_tx(struct bcm_op *op)
+static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)
{
struct sk_buff *skb;
struct can_skb_ext *csx;
struct net_device *dev;
- struct canfd_frame *cf;
+ struct canfd_frame cframe;
+ bool cyclic = !cf;
+ unsigned int idx = 0;
int err;
/* no target device? => exit */
if (!op->ifindex)
return;
- /* read currframe under lock protection */
- spin_lock_bh(&op->bcm_tx_lock);
- cf = op->frames + op->cfsiz * op->currframe;
- spin_unlock_bh(&op->bcm_tx_lock);
+ if (cyclic) {
+ /* read currframe under lock protection */
+ spin_lock_bh(&op->bcm_tx_lock);
+ idx = op->currframe;
+ memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz);
+ cf = &cframe;
+ spin_unlock_bh(&op->bcm_tx_lock);
+ }
dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
if (!dev) {
@@ -335,16 +370,22 @@ static void bcm_can_tx(struct bcm_op *op)
spin_lock_bh(&op->bcm_tx_lock);
if (!err)
- op->frames_abs++;
+ bcm_update_tx_stats(op);
- op->currframe++;
+ /* only advance the cyclic sequence if nothing reset currframe while
+ * we were sending - a concurrent TX_RESET_MULTI_IDX means this
+ * frame's bookkeeping belongs to a sequence that no longer exists
+ */
+ if (!cyclic || op->currframe == idx) {
+ op->currframe++;
- /* reached last frame? */
- if (op->currframe >= op->nframes)
- op->currframe = 0;
+ /* reached last frame? */
+ if (op->currframe >= op->nframes)
+ op->currframe = 0;
- if (op->count > 0)
- op->count--;
+ if (op->count > 0)
+ op->count--;
+ }
spin_unlock_bh(&op->bcm_tx_lock);
out:
@@ -433,12 +474,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
{
ktime_t ival;
+ spin_lock_bh(&op->bcm_tx_lock);
+
if (op->kt_ival1 && op->count)
ival = op->kt_ival1;
- else if (op->kt_ival2)
+ else if (op->kt_ival2) {
ival = op->kt_ival2;
- else
+ } else {
+ spin_unlock_bh(&op->bcm_tx_lock);
return false;
+ }
+
+ spin_unlock_bh(&op->bcm_tx_lock);
hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
return true;
@@ -455,26 +502,48 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
struct bcm_msg_head msg_head;
+ bool tx_ival1, tx_ival2;
+
+ /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn
+ * ktime_t reads racing with concurrent bcm_tx_setup() updates
+ */
+ spin_lock_bh(&op->bcm_tx_lock);
+ tx_ival1 = op->kt_ival1 && (op->count > 0);
+ tx_ival2 = !!op->kt_ival2;
+ spin_unlock_bh(&op->bcm_tx_lock);
+
+ if (tx_ival1) {
+ u32 flags, count;
+ struct bcm_timeval ival1, ival2;
- if (op->kt_ival1 && (op->count > 0)) {
- bcm_can_tx(op);
- if (!op->count && (op->flags & TX_COUNTEVT)) {
+ bcm_can_tx(op, NULL);
+ /* snapshot variables under lock to avoid torn reads racing
+ * with concurrent bcm_tx_setup() updates
+ */
+ spin_lock_bh(&op->bcm_tx_lock);
+ flags = op->flags;
+ count = op->count;
+ ival1 = op->ival1;
+ ival2 = op->ival2;
+ spin_unlock_bh(&op->bcm_tx_lock);
+
+ if (!count && (flags & TX_COUNTEVT)) {
/* create notification to user */
memset(&msg_head, 0, sizeof(msg_head));
msg_head.opcode = TX_EXPIRED;
- msg_head.flags = op->flags;
- msg_head.count = op->count;
- msg_head.ival1 = op->ival1;
- msg_head.ival2 = op->ival2;
+ msg_head.flags = flags;
+ msg_head.count = count;
+ msg_head.ival1 = ival1;
+ msg_head.ival2 = ival2;
msg_head.can_id = op->can_id;
msg_head.nframes = 0;
bcm_send_to_user(op, &msg_head, NULL, 0);
}
- } else if (op->kt_ival2) {
- bcm_can_tx(op);
+ } else if (tx_ival2) {
+ bcm_can_tx(op, NULL);
}
return bcm_tx_set_expiry(op, &op->timer) ?
@@ -488,12 +557,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
{
struct bcm_msg_head head;
- /* update statistics */
- op->frames_filtered++;
-
- /* prevent statistics overflow */
- if (op->frames_filtered > ULONG_MAX/100)
- op->frames_filtered = op->frames_abs = 0;
+ /* update statistics (frames_filtered <= frames_abs) */
+ if (atomic_long_read(&op->frames_abs))
+ atomic_long_inc(&op->frames_filtered);
/* this element is not throttled anymore */
data->flags &= ~RX_THR;
@@ -618,6 +684,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
struct bcm_msg_head msg_head;
+ spin_lock_bh(&op->bcm_rx_update_lock);
+
/* if user wants to be informed, when cyclic CAN-Messages come back */
if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
/* clear received CAN frames to indicate 'nothing received' */
@@ -634,6 +702,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
msg_head.can_id = op->can_id;
msg_head.nframes = 0;
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+
bcm_send_to_user(op, &msg_head, NULL, 0);
return HRTIMER_NORESTART;
@@ -682,15 +752,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op)
static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
+ enum hrtimer_restart ret;
+
+ spin_lock_bh(&op->bcm_rx_update_lock);
- if (bcm_rx_thr_flush(op)) {
+ /* kt_ival2 may have been concurrently cleared by bcm_rx_setup()
+ * before it cancels this timer - never forward with a zero
+ * interval in that case.
+ */
+ if (bcm_rx_thr_flush(op) && op->kt_ival2) {
hrtimer_forward_now(hrtimer, op->kt_ival2);
- return HRTIMER_RESTART;
+ ret = HRTIMER_RESTART;
} else {
/* rearm throttle handling */
op->kt_lastmsg = 0;
- return HRTIMER_NORESTART;
+ ret = HRTIMER_NORESTART;
}
+
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+ return ret;
}
/*
@@ -700,8 +781,10 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
{
struct bcm_op *op = (struct bcm_op *)data;
const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
+ struct canfd_frame rtrframe;
unsigned int i;
unsigned char traffic_flags;
+ bool rtr_frame;
if (op->can_id != rxframe->can_id)
return;
@@ -715,22 +798,59 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
return;
}
+ /* An ANYDEV op with an active RX timeout and/or throttle timer
+ * tracks a single source interface: claim the first interface that
+ * delivers a matching frame and reject frames from any other one,
+ * before hrtimer_cancel() below can touch op->timer - this avoids
+ * racing bcm_rx_timeout_handler() across concurrent interfaces.
+ * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly
+ * hold a stale value from an earlier non-RTR configuration.
+ */
+ if (!op->ifindex) {
+ spin_lock_bh(&op->bcm_rx_update_lock);
+
+ if (!(op->flags & RX_RTR_FRAME) &&
+ (op->kt_ival1 || op->kt_ival2)) {
+ /* don't claim to vanishing interface */
+ if (!op->if_detected &&
+ READ_ONCE(skb->dev->reg_state) == NETREG_REGISTERED)
+ op->if_detected = skb->dev->ifindex;
+
+ if (op->if_detected != skb->dev->ifindex) {
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+ return;
+ }
+ }
+
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+
/* disable timeout */
hrtimer_cancel(&op->timer);
- /* save rx timestamp */
- op->rx_stamp = skb->tstamp;
- /* save originator for recvfrom() */
- op->rx_ifindex = skb->dev->ifindex;
- /* update statistics */
- op->frames_abs++;
+ /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */
+ spin_lock_bh(&op->bcm_rx_update_lock);
+
+ rtr_frame = op->flags & RX_RTR_FRAME;
+ if (rtr_frame) {
+ bcm_update_rx_stats(op);
+ /* snapshot RTR content under lock */
+ memcpy(&rtrframe, op->frames, op->cfsiz);
+ spin_unlock_bh(&op->bcm_rx_update_lock);
- if (op->flags & RX_RTR_FRAME) {
/* send reply for RTR-request (placed in op->frames[0]) */
- bcm_can_tx(op);
+ bcm_can_tx(op, &rtrframe);
return;
}
+ /* update statistics in the same critical section as bcm_rx_changed()
+ * below: frames_filtered must never be checked/incremented against a
+ * frames_abs snapshot from a concurrent bcm_rx_handler() call on
+ * another CPU for the same (wildcard) op, or frames_filtered can end
+ * up larger than frames_abs.
+ */
+ bcm_update_rx_stats(op);
+
/* compute flags to distinguish between own/local/remote CAN traffic */
traffic_flags = 0;
if (skb->sk) {
@@ -739,6 +859,13 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
traffic_flags |= RX_OWN;
}
+ /* save rx timestamp and originator for recvfrom() under lock: an
+ * ANYDEV op without an active timer can still run concurrently on
+ * different CPUs, so content and meta data must be bundled here.
+ */
+ op->rx_stamp = skb->tstamp;
+ op->rx_ifindex = skb->dev->ifindex;
+
if (op->flags & RX_FILTER_ID) {
/* the easiest case */
bcm_rx_update_and_send(op, op->last_frames, rxframe,
@@ -774,6 +901,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
rx_starttimer:
bcm_rx_starttimer(op);
+
+ spin_unlock_bh(&op->bcm_rx_update_lock);
}
/*
@@ -793,9 +922,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
return NULL;
}
-static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+static void bcm_free_op_work(struct work_struct *work)
{
- struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
+ struct bcm_op *op = container_of(work, struct bcm_op, work);
+
+ hrtimer_cancel(&op->timer);
+ hrtimer_cancel(&op->thrtimer);
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
@@ -803,9 +935,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head)
if ((op->last_frames) && (op->last_frames != &op->last_sframe))
kfree(op->last_frames);
+ /* the last possible access to op->timer/op->thrtimer has now
+ * happened above via hrtimer_cancel() - op->sk is no longer
+ * needed by any pending timer callback, so drop our reference
+ */
+ sock_put(op->sk);
+
kfree(op);
}
+static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+{
+ struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
+
+ INIT_WORK(&op->work, bcm_free_op_work);
+ queue_work(bcm_wq, &op->work);
+}
+
static void bcm_remove_op(struct bcm_op *op)
{
hrtimer_cancel(&op->timer);
@@ -822,6 +968,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
/* mark as removed subscription */
op->rx_reg_dev = NULL;
+ netdev_put(dev, &op->rx_reg_dev_tracker);
} else
printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
"mismatch %p %p\n", op->rx_reg_dev, dev);
@@ -852,17 +999,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
* Only remove subscriptions that had not
* been removed due to NETDEV_UNREGISTER
* in bcm_notifier()
+ *
+ * op->rx_reg_dev is a tracked reference taken
+ * when the subscription was registered, so it
+ * stays valid here even if a concurrent
+ * NETDEV_UNREGISTER already unlisted the dev.
*/
- if (op->rx_reg_dev) {
- struct net_device *dev;
-
- dev = dev_get_by_index(sock_net(op->sk),
- op->ifindex);
- if (dev) {
- bcm_rx_unreg(dev, op);
- dev_put(dev);
- }
- }
+ if (op->rx_reg_dev)
+ bcm_rx_unreg(op->rx_reg_dev, op);
} else
can_rx_unregister(sock_net(op->sk), NULL,
op->can_id,
@@ -930,6 +1074,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
struct canfd_frame *cf;
+ bool add_op_to_list = false;
unsigned int i;
int err;
@@ -948,6 +1093,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* check the given can_id */
op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
if (op) {
+ void *new_frames;
+
/* update existing BCM operation */
/*
@@ -958,11 +1105,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (msg_head->nframes > op->nframes)
return -E2BIG;
- /* update CAN frames content */
+ /* get new CAN frames content into a staging buffer before
+ * locking: validate and normalize the frames there so that
+ * bcm_can_tx() / bcm_tx_timeout_handler() never observe a
+ * partially updated or unvalidated frame in op->frames
+ */
+ new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL);
+ if (!new_frames)
+ return -ENOMEM;
+
for (i = 0; i < msg_head->nframes; i++) {
- cf = op->frames + op->cfsiz * i;
+ cf = new_frames + op->cfsiz * i;
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
+ if (err < 0) {
+ kfree(new_frames);
+ return err;
+ }
if (op->flags & CAN_FD_FRAME) {
if (cf->len > 64)
@@ -972,36 +1131,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
err = -EINVAL;
}
- if (err < 0)
+ if (err < 0) {
+ kfree(new_frames);
return err;
+ }
if (msg_head->flags & TX_CP_CAN_ID) {
/* copy can_id into frame */
cf->can_id = msg_head->can_id;
}
}
+
+ spin_lock_bh(&op->bcm_tx_lock);
+
+ /* update CAN frames content */
+ memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz);
+
op->flags = msg_head->flags;
- /* only lock for unlikely count/nframes/currframe changes */
if (op->nframes != msg_head->nframes ||
- op->flags & TX_RESET_MULTI_IDX ||
- op->flags & SETTIMER) {
-
- spin_lock_bh(&op->bcm_tx_lock);
+ op->flags & TX_RESET_MULTI_IDX) {
+ /* potentially update changed nframes */
+ op->nframes = msg_head->nframes;
+ /* restart multiple frame transmission */
+ op->currframe = 0;
+ }
- if (op->nframes != msg_head->nframes ||
- op->flags & TX_RESET_MULTI_IDX) {
- /* potentially update changed nframes */
- op->nframes = msg_head->nframes;
- /* restart multiple frame transmission */
- op->currframe = 0;
- }
+ if (op->flags & SETTIMER)
+ op->count = msg_head->count;
- if (op->flags & SETTIMER)
- op->count = msg_head->count;
+ spin_unlock_bh(&op->bcm_tx_lock);
- spin_unlock_bh(&op->bcm_tx_lock);
- }
+ kfree(new_frames);
} else {
/* insert new BCM operation for the given can_id */
@@ -1060,6 +1221,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
+ sock_hold(sk);
op->ifindex = ifindex;
/* initialize uninitialized (kzalloc) structure */
@@ -1070,17 +1232,18 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
- /* add this bcm_op to the list of the tx_ops */
- list_add(&op->list, &bo->tx_ops);
+ add_op_to_list = true;
} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
if (op->flags & SETTIMER) {
/* set timer values */
+ spin_lock_bh(&op->bcm_tx_lock);
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ spin_unlock_bh(&op->bcm_tx_lock);
/* disable an active timer due to zero values? */
if (!op->kt_ival1 && !op->kt_ival2)
@@ -1093,8 +1256,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->flags |= TX_ANNOUNCE;
}
+ /* add this bcm_op to the list of the tx_ops? */
+ if (add_op_to_list)
+ list_add_rcu(&op->list, &bo->tx_ops);
+
if (op->flags & TX_ANNOUNCE)
- bcm_can_tx(op);
+ bcm_can_tx(op, NULL);
if (op->flags & STARTTIMER)
bcm_tx_start_timer(op);
@@ -1108,6 +1275,39 @@ free_op:
return err;
}
+static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
+ struct bcm_op *op, void *new_frames)
+{
+ struct canfd_frame *frame0 = new_frames;
+
+ if (!(msg_head->flags & RX_RTR_FRAME))
+ return 0;
+
+ /* this frame is sent out as-is by bcm_can_tx() whenever a matching
+ * remote request is received, so validate its length the same way
+ * bcm_tx_setup() validates TX_SETUP frames before installing it
+ */
+ if (msg_head->flags & CAN_FD_FRAME) {
+ if (frame0->len > 64)
+ return -EINVAL;
+ } else {
+ if (frame0->len > 8)
+ return -EINVAL;
+ }
+
+ /* funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ * normalize this on the staged buffer, before it is
+ * ever installed into op->frames.
+ */
+ if ((msg_head->flags & TX_CP_CAN_ID) ||
+ frame0->can_id == op->can_id)
+ frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
+
+ return 0;
+}
+
/*
* bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
*/
@@ -1117,6 +1317,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
int do_rx_register;
+ int new_op = 0;
int err = 0;
if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
@@ -1142,6 +1343,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* check the given can_id */
op = bcm_find_op(&bo->rx_ops, msg_head, ifindex);
if (op) {
+ void *new_frames = NULL;
+
/* update existing BCM operation */
/*
@@ -1153,21 +1356,62 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
return -E2BIG;
if (msg_head->nframes) {
- /* update CAN frames content */
- err = memcpy_from_msg(op->frames, msg,
+ /* get new CAN frames content before locking */
+ new_frames = kmalloc(msg_head->nframes * op->cfsiz,
+ GFP_KERNEL);
+ if (!new_frames)
+ return -ENOMEM;
+
+ err = memcpy_from_msg(new_frames, msg,
msg_head->nframes * op->cfsiz);
- if (err < 0)
+ if (err < 0) {
+ kfree(new_frames);
return err;
+ }
- /* clear last_frames to indicate 'nothing received' */
- memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
+ err = bcm_rx_setup_rtr_check(msg_head, op, new_frames);
+ if (err < 0) {
+ kfree(new_frames);
+ return err;
+ }
}
+ spin_lock_bh(&op->bcm_rx_update_lock);
op->nframes = msg_head->nframes;
op->flags = msg_head->flags;
- /* Only an update -> do not call can_rx_register() */
- do_rx_register = 0;
+ if (msg_head->nframes) {
+ /* update CAN frames content */
+ memcpy(op->frames, new_frames,
+ msg_head->nframes * op->cfsiz);
+
+ /* clear last_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0,
+ msg_head->nframes * op->cfsiz);
+ }
+
+ if (msg_head->flags & SETTIMER) {
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ op->kt_lastmsg = 0;
+ op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */
+ }
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+
+ /* free temporary frames / kfree(NULL) is safe */
+ kfree(new_frames);
+
+ /* Don't register a new CAN filter for the rx_op update unless
+ * a concurrent NETDEV_UNREGISTER notifier already tore down
+ * the previous registration. In this case the receiver needs
+ * to be re-registered here so that this update doesn't
+ * silently stop delivering frames for the given ifindex.
+ * Ops with ifindex = 0 (all CAN interfaces) never carry a
+ * tracked rx_reg_dev and stay registered as-is.
+ */
+ do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0;
} else {
/* insert new BCM operation for the given can_id */
@@ -1176,6 +1420,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
return -ENOMEM;
spin_lock_init(&op->bcm_tx_lock);
+ spin_lock_init(&op->bcm_rx_update_lock);
op->can_id = msg_head->can_id;
op->nframes = msg_head->nframes;
op->cfsiz = CFSIZ(msg_head->flags);
@@ -1209,18 +1454,17 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (msg_head->nframes) {
err = memcpy_from_msg(op->frames, msg,
msg_head->nframes * op->cfsiz);
- if (err < 0) {
- if (op->frames != &op->sframe)
- kfree(op->frames);
- if (op->last_frames != &op->last_sframe)
- kfree(op->last_frames);
- kfree(op);
- return err;
- }
+ if (err < 0)
+ goto free_op;
+
+ err = bcm_rx_setup_rtr_check(msg_head, op, op->frames);
+ if (err < 0)
+ goto free_op;
}
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
+ sock_hold(sk);
op->ifindex = ifindex;
/* ifindex for timeout events w/o previous frame reception */
@@ -1232,40 +1476,31 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
- /* add this bcm_op to the list of the rx_ops */
- list_add(&op->list, &bo->rx_ops);
-
/* call can_rx_register() */
do_rx_register = 1;
+ new_op = 1;
} /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
/* check flags */
if (op->flags & RX_RTR_FRAME) {
- struct canfd_frame *frame0 = op->frames;
-
/* no timers in RTR-mode */
hrtimer_cancel(&op->thrtimer);
hrtimer_cancel(&op->timer);
-
- /*
- * funny feature in RX(!)_SETUP only for RTR-mode:
- * copy can_id into frame BUT without RTR-flag to
- * prevent a full-load-loopback-test ... ;-]
- */
- if ((op->flags & TX_CP_CAN_ID) ||
- (frame0->can_id == op->can_id))
- frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
-
} else {
if (op->flags & SETTIMER) {
- /* set timer value */
- op->ival1 = msg_head->ival1;
- op->ival2 = msg_head->ival2;
- op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
- op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ /* set timers (locked) for newly created op */
+ if (new_op) {
+ spin_lock_bh(&op->bcm_rx_update_lock);
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
+ op->kt_lastmsg = 0;
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
/* disable an active timer due to zero value? */
if (!op->kt_ival1)
@@ -1275,9 +1510,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
* In any case cancel the throttle timer, flush
* potentially blocked msgs and reset throttle handling
*/
- op->kt_lastmsg = 0;
hrtimer_cancel(&op->thrtimer);
+
+ spin_lock_bh(&op->bcm_rx_update_lock);
bcm_rx_thr_flush(op);
+ spin_unlock_bh(&op->bcm_rx_update_lock);
}
if ((op->flags & STARTTIMER) && op->kt_ival1)
@@ -1285,7 +1522,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
HRTIMER_MODE_REL_SOFT);
}
- /* now we can register for can_ids, if we added a new bcm_op */
+ /* now we can register for can_ids, if we added a new bcm_op
+ * or need to re-register after a NETDEV_UNREGISTER tore down
+ * the previous registration of an existing op
+ */
if (do_rx_register) {
if (ifindex) {
struct net_device *dev;
@@ -1298,23 +1538,62 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
bcm_rx_handler, op,
"bcm", sk);
- op->rx_reg_dev = dev;
+ /* keep a tracked reference so that a later
+ * unregister can safely reach the device even
+ * if a concurrent NETDEV_UNREGISTER has
+ * already unlisted it by ifindex
+ */
+ if (!err) {
+ op->rx_reg_dev = dev;
+ netdev_hold(dev,
+ &op->rx_reg_dev_tracker,
+ GFP_KERNEL);
+ }
dev_put(dev);
+ } else {
+ /* the requested device is gone - do not
+ * silently succeed without registering
+ */
+ err = -ENODEV;
}
- } else
+ } else {
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk);
+ }
+
if (err) {
- /* this bcm rx op is broken -> remove it */
- list_del_rcu(&op->list);
- bcm_remove_op(op);
+ /* newly created bcm rx op is broken -> remove it */
+ if (new_op) {
+ bcm_remove_op(op);
+ return err;
+ }
+
+ /* an existing op just stays unregistered.
+ * Cancel op->timer and (defensively) op->thrtimer.
+ * Other settings can't be reached until the next
+ * successful RX_SETUP.
+ */
+ hrtimer_cancel(&op->timer);
+ hrtimer_cancel(&op->thrtimer);
return err;
}
+
+ /* add a new bcm_op to the list of the rx_ops */
+ if (new_op)
+ list_add_rcu(&op->list, &bo->rx_ops);
}
return msg_head->nframes * op->cfsiz + MHSIZ;
+
+free_op:
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ if (op->last_frames != &op->last_sframe)
+ kfree(op->last_frames);
+ kfree(op);
+ return err;
}
/*
@@ -1373,12 +1652,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
- int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+ int ifindex;
struct bcm_msg_head msg_head;
int cfsiz;
int ret; /* read bytes or error codes as return value */
- if (!bo->bound)
+ /* Lockless fast-path check for bound socket */
+ if (!READ_ONCE(bo->bound))
return -ENOTCONN;
/* check for valid message length from userspace */
@@ -1394,17 +1674,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if ((size - MHSIZ) % cfsiz)
return -EINVAL;
+ lock_sock(sk);
+
+ /* Re-validate under the socket lock: a concurrent bcm_notify()
+ * may have unbound this socket (device removal) after the
+ * lockless fast-path check above. bo->ifindex is only ever
+ * mutated under lock_sock(), so reading it here - instead of
+ * before taking the lock - guarantees it can't be observed
+ * torn against bo->bound.
+ */
+ if (!bo->bound) {
+ ret = -ENOTCONN;
+ goto out_release;
+ }
+
+ /* default ifindex for this bcm_op */
+ ifindex = bo->ifindex;
+
/* check for alternative ifindex for this bcm_op */
if (!ifindex && msg->msg_name) {
/* no bound device as default => check msg_name */
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
- if (msg->msg_namelen < BCM_MIN_NAMELEN)
- return -EINVAL;
+ if (msg->msg_namelen < BCM_MIN_NAMELEN) {
+ ret = -EINVAL;
+ goto out_release;
+ }
- if (addr->can_family != AF_CAN)
- return -EINVAL;
+ if (addr->can_family != AF_CAN) {
+ ret = -EINVAL;
+ goto out_release;
+ }
/* ifindex from sendto() */
ifindex = addr->can_ifindex;
@@ -1413,20 +1714,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex);
- if (!dev)
- return -ENODEV;
+ if (!dev) {
+ ret = -ENODEV;
+ goto out_release;
+ }
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
- return -ENODEV;
+ ret = -ENODEV;
+ goto out_release;
}
dev_put(dev);
}
}
- lock_sock(sk);
-
switch (msg_head.opcode) {
case TX_SETUP:
@@ -1476,6 +1778,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
break;
}
+out_release:
release_sock(sk);
return ret;
@@ -1499,11 +1802,30 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
case NETDEV_UNREGISTER:
lock_sock(sk);
- /* remove device specific receive entries */
- list_for_each_entry(op, &bo->rx_ops, list)
+ /* rx_ops: remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list) {
if (op->rx_reg_dev == dev)
bcm_rx_unreg(dev, op);
+ /* release an ANYDEV op's claim (see bcm_rx_handler())
+ * on this now confirmed-gone interface.
+ */
+ if (!op->ifindex) {
+ spin_lock_bh(&op->bcm_rx_update_lock);
+ if (op->if_detected == dev->ifindex)
+ op->if_detected = 0;
+ spin_unlock_bh(&op->bcm_rx_update_lock);
+ }
+ }
+
+ /* tx_ops: stop device specific cyclic transmissions on the
+ * vanishing ifindex. Cancelling the timer is enough to stop
+ * cyclic bcm_can_tx() calls as there is no re-arming.
+ */
+ list_for_each_entry(op, &bo->tx_ops, list)
+ if (op->ifindex == dev->ifindex)
+ hrtimer_cancel(&op->timer);
+
/* remove device reference, if this is our bound device */
if (bo->bound && bo->ifindex == dev->ifindex) {
#if IS_ENABLED(CONFIG_PROC_FS)
@@ -1512,7 +1834,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
bo->bcm_proc_read = NULL;
}
#endif
- bo->bound = 0;
+ /* Paired with the lockless fast-path check in
+ * bcm_sendmsg(); bo->ifindex itself is only ever
+ * accessed under lock_sock() so it needs no
+ * annotation.
+ */
+ WRITE_ONCE(bo->bound, 0);
bo->ifindex = 0;
notify_enodev = 1;
}
@@ -1616,8 +1943,10 @@ static int bcm_release(struct socket *sock)
remove_proc_entry(bo->procname, net->can.bcmproc_dir);
#endif /* CONFIG_PROC_FS */
- list_for_each_entry_safe(op, next, &bo->tx_ops, list)
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
+ list_del_rcu(&op->list);
bcm_remove_op(op);
+ }
list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
/*
@@ -1629,16 +1958,14 @@ static int bcm_release(struct socket *sock)
* Only remove subscriptions that had not
* been removed due to NETDEV_UNREGISTER
* in bcm_notifier()
+ *
+ * op->rx_reg_dev is a tracked reference taken
+ * when the subscription was registered, so it
+ * stays valid here even if a concurrent
+ * NETDEV_UNREGISTER already unlisted the device.
*/
- if (op->rx_reg_dev) {
- struct net_device *dev;
-
- dev = dev_get_by_index(net, op->ifindex);
- if (dev) {
- bcm_rx_unreg(dev, op);
- dev_put(dev);
- }
- }
+ if (op->rx_reg_dev)
+ bcm_rx_unreg(op->rx_reg_dev, op);
} else
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
@@ -1648,12 +1975,14 @@ static int bcm_release(struct socket *sock)
synchronize_rcu();
- list_for_each_entry_safe(op, next, &bo->rx_ops, list)
+ list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+ list_del_rcu(&op->list);
bcm_remove_op(op);
+ }
/* remove device reference */
if (bo->bound) {
- bo->bound = 0;
+ WRITE_ONCE(bo->bound, 0);
bo->ifindex = 0;
}
@@ -1723,7 +2052,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
}
#endif /* CONFIG_PROC_FS */
- bo->bound = 1;
+ /* bo->ifindex above is fully assigned before this point; pairs
+ * with the lockless fast-path check in bcm_sendmsg()
+ */
+ WRITE_ONCE(bo->bound, 1);
fail:
release_sock(sk);
@@ -1839,11 +2171,15 @@ static int __init bcm_module_init(void)
{
int err;
+ bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0);
+ if (!bcm_wq)
+ return -ENOMEM;
+
pr_info("can: broadcast manager protocol\n");
err = register_pernet_subsys(&canbcm_pernet_ops);
if (err)
- return err;
+ goto register_pernet_failed;
err = register_netdevice_notifier(&canbcm_notifier);
if (err)
@@ -1861,6 +2197,8 @@ register_proto_failed:
unregister_netdevice_notifier(&canbcm_notifier);
register_notifier_failed:
unregister_pernet_subsys(&canbcm_pernet_ops);
+register_pernet_failed:
+ destroy_workqueue(bcm_wq);
return err;
}
@@ -1869,6 +2207,8 @@ static void __exit bcm_module_exit(void)
can_proto_unregister(&bcm_can_proto);
unregister_netdevice_notifier(&canbcm_notifier);
unregister_pernet_subsys(&canbcm_pernet_ops);
+ rcu_barrier();
+ destroy_workqueue(bcm_wq);
}
module_init(bcm_module_init);
diff --git a/net/can/isotp.c b/net/can/isotp.c
index c48b4a818297..54becaf6898f 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -152,11 +152,13 @@ struct isotp_sock {
struct sock sk;
int bound;
int ifindex;
+ struct net_device *dev;
+ netdevice_tracker dev_tracker;
canid_t txid;
canid_t rxid;
ktime_t tx_gap;
ktime_t lastrxcf_tstamp;
- struct hrtimer rxtimer, txtimer, txfrtimer;
+ struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
struct can_isotp_options opt;
struct can_isotp_fc_options rxfc, txfc;
struct can_isotp_ll_options ll;
@@ -164,6 +166,7 @@ struct isotp_sock {
u32 force_tx_stmin;
u32 force_rx_stmin;
u32 cfecho; /* consecutive frame echo tag */
+ u32 tx_gen; /* generation, bumped per new tx transfer */
struct tpcon rx, tx;
struct list_head notifier;
wait_queue_head_t wait;
@@ -376,6 +379,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
hrtimer_cancel(&so->txtimer);
+ /* isotp_tx_timeout() may have given up on this job while
+ * hrtimer_cancel() above waited for it to finish; so->rx_lock
+ * (held by our caller isotp_rcv()) rules out a concurrent claim,
+ * so a plain recheck is enough here.
+ */
+ if (so->tx.state != ISOTP_WAIT_FC &&
+ so->tx.state != ISOTP_WAIT_FIRST_FC)
+ return 1;
+
if ((cf->len < ae + FC_CONTENT_SZ) ||
((so->opt.flags & ISOTP_CHECK_PADDING) &&
check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
@@ -422,7 +434,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
so->tx.bs = 0;
so->tx.state = ISOTP_SENDING;
/* send CF frame and enable echo timeout handling */
- hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+ hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
isotp_send_cframe(so);
break;
@@ -575,6 +587,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
hrtimer_cancel(&so->rxtimer);
+ /* isotp_rx_timer_handler() may have raced us for so->rx.state
+ * while hrtimer_cancel() above waited for it to finish, already
+ * reporting ETIMEDOUT and resetting the reception; don't process
+ * this CF into a reassembly that has already been given up on.
+ */
+ if (so->rx.state != ISOTP_WAIT_DATA)
+ return 1;
+
/* CFs are never longer than the FF */
if (cf->len > so->rx.ll_dl)
return 1;
@@ -870,20 +890,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
/* only handle my own local echo CF/SF skb's (no FF!) */
- if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
+ if (skb->sk != sk)
return;
+ /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
+ * (no isotp_rcv() caller here), so take it ourselves
+ */
+ spin_lock(&so->rx_lock);
+
+ /* so->cfecho may since belong to a new transfer; recheck under lock */
+ if (so->cfecho != *(u32 *)cf->data)
+ goto out_unlock;
+
/* cancel local echo timeout */
- hrtimer_cancel(&so->txtimer);
+ hrtimer_cancel(&so->echotimer);
/* local echo skb with consecutive frame has been consumed */
so->cfecho = 0;
+ /* claiming a transfer also takes so->rx_lock, so a plain recheck
+ * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
+ * a new claim while we're still in here
+ */
+ if (so->tx.state != ISOTP_SENDING)
+ goto out_unlock;
+
if (so->tx.idx >= so->tx.len) {
/* we are done */
so->tx.state = ISOTP_IDLE;
wake_up_interruptible(&so->wait);
- return;
+ goto out_unlock;
}
if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
@@ -891,53 +927,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
so->tx.state = ISOTP_WAIT_FC;
hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
- return;
+ goto out_unlock;
}
/* no gap between data frames needed => use burst mode */
if (!so->tx_gap) {
/* enable echo timeout handling */
- hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+ hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
isotp_send_cframe(so);
- return;
+ goto out_unlock;
}
/* start timer to send next consecutive frame with correct delay */
hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
+
+out_unlock:
+ spin_unlock(&so->rx_lock);
}
-static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
+/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
+ * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
+ * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
+ * a new transfer with an error from the one that just timed out.
+ */
+static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
{
- struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
- txtimer);
struct sock *sk = &so->sk;
+ u32 gen = READ_ONCE(so->tx_gen);
+ u32 old_state = READ_ONCE(so->tx.state);
/* don't handle timeouts in IDLE or SHUTDOWN state */
- if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
+ if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
+ return HRTIMER_NORESTART;
+
+ /* only claim the timeout if the state is still unchanged */
+ if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
return HRTIMER_NORESTART;
/* we did not get any flow control or echo frame in time */
- /* report 'communication error on send' */
- sk->sk_err = ECOMM;
- if (!sock_flag(sk, SOCK_DEAD))
- sk_error_report(sk);
+ if (READ_ONCE(so->tx_gen) == gen) {
+ /* report 'communication error on send' */
+ sk->sk_err = ECOMM;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk_error_report(sk);
+ }
- /* reset tx state */
- so->tx.state = ISOTP_IDLE;
wake_up_interruptible(&so->wait);
return HRTIMER_NORESTART;
}
+/* so->txtimer: fires when a Flow Control frame does not arrive in time */
+static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
+{
+ struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+ txtimer);
+
+ return isotp_tx_timeout(so);
+}
+
+/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
+static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
+{
+ struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+ echotimer);
+
+ return isotp_tx_timeout(so);
+}
+
static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
{
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txfrtimer);
/* start echo timeout handling and cover below protocol error */
- hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+ hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
/* cfecho should be consumed by isotp_rcv_echo() here */
@@ -958,13 +1024,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
+ struct hrtimer *tx_hrt = &so->echotimer;
+ u32 new_state = ISOTP_SENDING;
int off;
int err;
if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
return -EADDRNOTAVAIL;
- while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
+ /* claim the socket under so->rx_lock: this serializes the claim
+ * with the RX path and with sendmsg()'s own error paths below, so
+ * none of them can ever see a transfer mid-claim
+ */
+ for (;;) {
+ spin_lock_bh(&so->rx_lock);
+ if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
+ break;
+ spin_unlock_bh(&so->rx_lock);
+
/* we do not support multiple buffers - for now */
if (msg->msg_flags & MSG_DONTWAIT)
return -EAGAIN;
@@ -973,9 +1050,29 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
return -EADDRNOTAVAIL;
/* wait for complete transmission of current pdu */
- err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+ err = wait_event_interruptible(so->wait,
+ so->tx.state == ISOTP_IDLE);
if (err)
- goto err_event_drop;
+ return err;
+ }
+
+ /* new transfer: bump so->tx_gen and drain the old one's timers,
+ * still under the so->rx_lock we just claimed the socket with
+ */
+ WRITE_ONCE(so->tx.state, ISOTP_SENDING);
+ WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
+ hrtimer_cancel(&so->txtimer);
+ hrtimer_cancel(&so->echotimer);
+ hrtimer_cancel(&so->txfrtimer);
+ so->cfecho = 0;
+ spin_unlock_bh(&so->rx_lock);
+
+ /* so->bound is only checked once above - a wakeup may have
+ * unbound/rebound the socket meanwhile, so re-validate it
+ */
+ if (!so->bound) {
+ err = -EADDRNOTAVAIL;
+ goto err_out_drop;
}
/* PDU size > default => try max_pdu_size */
@@ -1086,18 +1183,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
so->cfecho = *(u32 *)cf->data;
} else {
/* standard flow control check */
- so->tx.state = ISOTP_WAIT_FIRST_FC;
+ new_state = ISOTP_WAIT_FIRST_FC;
/* start timeout for FC */
hrtimer_sec = ISOTP_FC_TIMEOUT;
+ tx_hrt = &so->txtimer;
/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
so->cfecho = 0;
}
}
- hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
+ spin_lock_bh(&so->rx_lock);
+ if (so->tx.state == ISOTP_SHUTDOWN) {
+ /* isotp_release() has since taken over and already drained
+ * our timers - don't send into a socket that's going away
+ */
+ spin_unlock_bh(&so->rx_lock);
+ kfree_skb(skb);
+ dev_put(dev);
+ wake_up_interruptible(&so->wait);
+ return -EADDRNOTAVAIL;
+ }
+ /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
+ so->tx.state = new_state;
+ hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
HRTIMER_MODE_REL_SOFT);
+ spin_unlock_bh(&so->rx_lock);
/* send the first or only CAN frame */
cf->flags = so->ll.tx_flags;
@@ -1110,13 +1222,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
__func__, ERR_PTR(err));
+ spin_lock_bh(&so->rx_lock);
/* no transmission -> no timeout monitoring */
- hrtimer_cancel(&so->txtimer);
-
- /* reset consecutive frame echo tag */
- so->cfecho = 0;
-
- goto err_out_drop;
+ hrtimer_cancel(tx_hrt);
+ goto err_out_drop_locked;
}
if (wait_tx_done) {
@@ -1132,14 +1241,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
return size;
+err_out_drop:
+ /* claimed but nothing sent yet - no timer to cancel */
+ spin_lock_bh(&so->rx_lock);
+ goto err_out_drop_locked;
err_event_drop:
- /* got signal: force tx state machine to be idle */
- so->tx.state = ISOTP_IDLE;
+ /* interrupted waiting on our own transfer - drain its timers */
+ spin_lock_bh(&so->rx_lock);
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
-err_out_drop:
- /* drop this PDU and unlock a potential wait queue */
+ hrtimer_cancel(&so->echotimer);
+err_out_drop_locked:
+ /* release the claim; so->rx_lock still held from above */
+ so->cfecho = 0;
so->tx.state = ISOTP_IDLE;
+ spin_unlock_bh(&so->rx_lock);
wake_up_interruptible(&so->wait);
return err;
@@ -1201,13 +1317,20 @@ static int isotp_release(struct socket *sock)
so = isotp_sk(sk);
net = sock_net(sk);
- /* wait for complete transmission of current pdu */
- while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
- cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
+ /* best-effort: wait for a running pdu to finish, but don't block on
+ * it forever - give up after the first signal
+ */
+ while (so->tx.state != ISOTP_IDLE &&
+ wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
;
- /* force state machines to be idle also when a signal occurred */
+ /* claim the socket under so->rx_lock like sendmsg() does, so its
+ * claim can't race the forced ISOTP_SHUTDOWN below; force it
+ * unconditionally, even when a signal cut the wait above short
+ */
+ spin_lock_bh(&so->rx_lock);
so->tx.state = ISOTP_SHUTDOWN;
+ spin_unlock_bh(&so->rx_lock);
so->rx.state = ISOTP_IDLE;
spin_lock(&isotp_notifier_lock);
@@ -1219,36 +1342,43 @@ static int isotp_release(struct socket *sock)
list_del(&so->notifier);
spin_unlock(&isotp_notifier_lock);
+ rtnl_lock();
lock_sock(sk);
- /* remove current filters & unregister */
- if (so->bound) {
- if (so->ifindex) {
- struct net_device *dev;
-
- dev = dev_get_by_index(net, so->ifindex);
- if (dev) {
- if (isotp_register_rxid(so))
- can_rx_unregister(net, dev, so->rxid,
- SINGLE_MASK(so->rxid),
- isotp_rcv, sk);
+ /* remove current filters & unregister
+ * tracked reference so->dev is taken at bind() time with rtnl_lock
+ */
+ if (so->bound && so->dev) {
+ if (isotp_register_rxid(so))
+ can_rx_unregister(net, so->dev, so->rxid,
+ SINGLE_MASK(so->rxid),
+ isotp_rcv, sk);
- can_rx_unregister(net, dev, so->txid,
- SINGLE_MASK(so->txid),
- isotp_rcv_echo, sk);
- dev_put(dev);
- synchronize_rcu();
- }
- }
+ can_rx_unregister(net, so->dev, so->txid,
+ SINGLE_MASK(so->txid),
+ isotp_rcv_echo, sk);
+ netdev_put(so->dev, &so->dev_tracker);
}
+ so->ifindex = 0;
+ so->bound = 0;
+ so->dev = NULL;
+
+ rtnl_unlock();
+
+ /* Always wait for a grace period before touching the timers below.
+ * A concurrent NETDEV_UNREGISTER may have already unregistered our
+ * filters and cleared so->bound in isotp_notify() without waiting
+ * for in-flight isotp_rcv() callers to finish, so this call must not
+ * be skipped just because so->bound is already 0 here.
+ */
+ synchronize_rcu();
+
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
+ hrtimer_cancel(&so->echotimer);
hrtimer_cancel(&so->rxtimer);
- so->ifindex = 0;
- so->bound = 0;
-
sock_orphan(sk);
sock->sk = NULL;
@@ -1303,6 +1433,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
if (!addr->can_ifindex)
return -ENODEV;
+ rtnl_lock();
lock_sock(sk);
if (so->bound) {
@@ -1310,6 +1441,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
goto out;
}
+ /* A transmission or reception that outlived a previous binding
+ * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
+ * and RX watchdog timers bound how long this takes. Checked together
+ * with so->bound in the same lock_sock() section above, so there is
+ * no window in which a concurrent isotp_notify() could be missed.
+ */
+ if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
+ err = -EAGAIN;
+ goto out;
+ }
+
/* ensure different CAN IDs when the rx_id is to be registered */
if (isotp_register_rxid(so) && rx_id == tx_id) {
err = -EADDRNOTAVAIL;
@@ -1322,14 +1464,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
goto out;
}
if (dev->type != ARPHRD_CAN) {
- dev_put(dev);
err = -ENODEV;
- goto out;
+ goto out_put_dev;
}
if (READ_ONCE(dev->mtu) < so->ll.mtu) {
- dev_put(dev);
err = -EINVAL;
- goto out;
+ goto out_put_dev;
}
if (!(dev->flags & IFF_UP))
notify_enetdown = 1;
@@ -1347,16 +1487,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
isotp_rcv_echo, sk, "isotpe", sk);
- dev_put(dev);
-
/* switch to new settings */
so->ifindex = ifindex;
so->rxid = rx_id;
so->txid = tx_id;
so->bound = 1;
+ /* bind() ok -> hold a reference for so->dev so that isotp_release()
+ * can safely reach the device later, even if a concurrent
+ * NETDEV_UNREGISTER has already unlisted it by ifindex.
+ */
+ so->dev = dev;
+ netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL);
+
+out_put_dev:
+ /* remove potential reference from dev_get_by_index() */
+ dev_put(dev);
out:
release_sock(sk);
+ rtnl_unlock();
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
@@ -1559,7 +1708,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
- if (so->ifindex != dev->ifindex)
+ if (so->dev != dev)
return;
switch (msg) {
@@ -1575,10 +1724,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
can_rx_unregister(dev_net(dev), dev, so->txid,
SINGLE_MASK(so->txid),
isotp_rcv_echo, sk);
+ netdev_put(so->dev, &so->dev_tracker);
}
so->ifindex = 0;
so->bound = 0;
+ so->dev = NULL;
release_sock(sk);
sk->sk_err = ENODEV;
@@ -1638,6 +1789,7 @@ static int isotp_init(struct sock *sk)
so->ifindex = 0;
so->bound = 0;
+ so->dev = NULL;
so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
@@ -1664,10 +1816,14 @@ static int isotp_init(struct sock *sk)
so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
- hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL_SOFT);
+ hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ hrtimer_setup(&so->txtimer, isotp_tx_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ hrtimer_setup(&so->echotimer, isotp_echo_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
init_waitqueue_head(&so->wait);
spin_lock_init(&so->rx_lock);
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index df93d57907da..8a31cb23bc76 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -351,6 +351,18 @@ static void j1939_session_skb_drop_old(struct j1939_session *session)
}
}
+static bool j1939_address_is_local(struct j1939_priv *priv, u8 addr)
+{
+ bool local = false;
+
+ read_lock_bh(&priv->lock);
+ if (j1939_address_is_unicast(addr) && priv->ents[addr].nusers)
+ local = true;
+ read_unlock_bh(&priv->lock);
+
+ return local;
+}
+
void j1939_session_skb_queue(struct j1939_session *session,
struct sk_buff *skb)
{
@@ -359,8 +371,7 @@ void j1939_session_skb_queue(struct j1939_session *session,
j1939_ac_fixup(priv, skb);
- if (j1939_address_is_unicast(skcb->addr.da) &&
- priv->ents[skcb->addr.da].nusers)
+ if (j1939_address_is_local(priv, skcb->addr.da))
skcb->flags |= J1939_ECU_LOCAL_DST;
skcb->flags |= J1939_ECU_LOCAL_SRC;
@@ -2038,8 +2049,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv,
return ERR_PTR(ret);
/* fix DST flags, it may be used there soon */
- if (j1939_address_is_unicast(skcb->addr.da) &&
- priv->ents[skcb->addr.da].nusers)
+ if (j1939_address_is_local(priv, skcb->addr.da))
skcb->flags |= J1939_ECU_LOCAL_DST;
/* src is always local, I'm sending ... */
diff --git a/net/can/raw.c b/net/can/raw.c
index a26942e78e68..82d9c0499c95 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -562,8 +562,8 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
return RAW_MIN_NAMELEN;
}
-static int raw_setsockopt(struct socket *sock, int level, int optname,
- sockptr_t optval, unsigned int optlen)
+static int raw_setsockopt_locked(struct socket *sock, int optname,
+ sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
@@ -575,9 +575,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
int flag;
int err = 0;
- if (level != SOL_CAN_RAW)
- return -EINVAL;
-
switch (optname) {
case CAN_RAW_FILTER:
if (optlen % sizeof(struct can_filter) != 0)
@@ -598,17 +595,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return -EFAULT;
}
- rtnl_lock();
- lock_sock(sk);
-
dev = ro->dev;
- if (ro->bound && dev) {
- if (dev->reg_state != NETREG_REGISTERED) {
- if (count > 1)
- kfree(filter);
- err = -ENODEV;
- goto out_fil;
- }
+ if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
+ if (count > 1)
+ kfree(filter);
+ return -ENODEV;
}
if (ro->bound) {
@@ -622,7 +613,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (err) {
if (count > 1)
kfree(filter);
- goto out_fil;
+ return err;
}
/* remove old filter registrations */
@@ -642,11 +633,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
}
ro->filter = filter;
ro->count = count;
-
- out_fil:
- release_sock(sk);
- rtnl_unlock();
-
break;
case CAN_RAW_ERR_FILTER:
@@ -658,16 +644,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
err_mask &= CAN_ERR_MASK;
- rtnl_lock();
- lock_sock(sk);
-
dev = ro->dev;
- if (ro->bound && dev) {
- if (dev->reg_state != NETREG_REGISTERED) {
- err = -ENODEV;
- goto out_err;
- }
- }
+ if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
+ return -ENODEV;
/* remove current error mask */
if (ro->bound) {
@@ -676,7 +655,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
err_mask);
if (err)
- goto out_err;
+ return err;
/* remove old err_mask registration */
raw_disable_errfilter(sock_net(sk), dev, sk,
@@ -685,11 +664,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
/* link new err_mask to the socket */
ro->err_mask = err_mask;
-
- out_err:
- release_sock(sk);
- rtnl_unlock();
-
break;
case CAN_RAW_LOOPBACK:
@@ -769,6 +743,26 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return err;
}
+static int raw_setsockopt(struct socket *sock, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ int err;
+
+ if (level != SOL_CAN_RAW)
+ return -EINVAL;
+
+ rtnl_lock();
+ lock_sock(sk);
+
+ err = raw_setsockopt_locked(sock, optname, optval, optlen);
+
+ release_sock(sk);
+ rtnl_unlock();
+
+ return err;
+}
+
static int raw_getsockopt(struct socket *sock, int level, int optname,
sockopt_t *opt)
{
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index ecd659f79fd4..1d295a8769fa 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -158,8 +158,6 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk)
struct bpf_local_storage_elem *selem;
int ret = 0;
- RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
-
rcu_read_lock_dont_migrate();
sk_storage = rcu_dereference(sk->sk_bpf_storage);
diff --git a/net/core/dev.c b/net/core/dev.c
index 4b3d5cfdf6e0..5933c5dab09e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4018,6 +4018,9 @@ out_free:
return NULL;
}
+/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
+ * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
+ */
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
{
netdev_features_t features;
@@ -4089,7 +4092,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
skb->prev = skb;
skb = validate_xmit_skb(skb, dev, again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb))
continue;
if (!head)
@@ -4860,8 +4863,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
goto recursion_alert;
skb = validate_xmit_skb(skb, dev, &again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb)) {
+ if (PTR_ERR(skb) == -EINPROGRESS)
+ rc = NET_XMIT_SUCCESS;
goto out;
+ }
HARD_TX_LOCK(dev, txq, cpu);
diff --git a/net/core/sock.c b/net/core/sock.c
index 8a59bfaa8096..498a57f34f5b 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2492,6 +2492,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
sock_copy(newsk, sk);
newsk->sk_prot_creator = prot;
+#ifdef CONFIG_BPF_SYSCALL
+ RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
+#endif
/* SANITY */
if (likely(newsk->sk_net_refcnt)) {
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index c60ba6d292f9..9efbd8ca7db8 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk)
{
if (sk_is_tcp(sk))
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
+ if (sk_is_udp(sk))
+ return sk_hashed(sk);
if (sk_is_stream_unix(sk))
return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
if (sk_is_vsock(sk) &&
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index e11dc86ceda0..6badad29593b 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1385,7 +1385,7 @@ succeeded:
out_remove_new_fa:
fib_remove_alias(t, tp, l, new_fa);
out_free_new_fa:
- kmem_cache_free(fn_alias_kmem, new_fa);
+ alias_free_mem_rcu(new_fa);
out:
fib_release_info(fi);
err:
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f36b6..8e905b50dead 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -334,6 +334,7 @@ unlock:
static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
{
+ struct sk_psock *psock;
bool slow;
if (cmd != SIOCINQ)
@@ -344,7 +345,21 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
return -EINVAL;
slow = lock_sock_fast(sk);
- *karg = sk_psock_msg_inq(sk);
+ psock = sk_psock_get(sk);
+ if (unlikely(!psock)) {
+ unlock_sock_fast(sk, slow);
+ return tcp_ioctl(sk, cmd, karg);
+ }
+ *karg = sk_psock_get_msg_len_nolock(psock);
+ /* Without a verdict program, ingress data is never diverted to
+ * ingress_msg: it stays in sk_receive_queue and is read through
+ * the fallback to tcp_recvmsg(), so account for it like
+ * tcp_ioctl() does.
+ */
+ if (!READ_ONCE(psock->progs.stream_verdict) &&
+ !READ_ONCE(psock->progs.skb_verdict))
+ *karg += tcp_inq(sk);
+ sk_psock_put(sk, psock);
unlock_sock_fast(sk, slow);
return 0;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 209ef7522508..4a46da375043 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2318,8 +2318,10 @@ do_time_wait:
}
drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb);
- if (drop_reason)
- break;
+ if (drop_reason) {
+ inet_twsk_put(inet_twsk(sk));
+ goto discard_it;
+ }
}
/* to ACK */
fallthrough;
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 6d80f85e55fa..a7025ec87035 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -120,7 +120,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
ktime_t tstamp = skb->tstamp;
struct ip6_frag_state state;
u8 *prevhdr, nexthdr = 0;
- unsigned int mtu, hlen;
+ unsigned int mtu, hlen, nexthdr_offset;
int hroom, err = 0;
__be32 frag_id;
@@ -129,6 +129,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
goto blackhole;
hlen = err;
nexthdr = *prevhdr;
+ nexthdr_offset = prevhdr - skb_network_header(skb);
mtu = skb->dev->mtu;
if (frag_max_size > mtu ||
@@ -147,6 +148,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
(err = skb_checksum_help(skb)))
goto blackhole;
+ prevhdr = skb_network_header(skb) + nexthdr_offset;
hroom = LL_RESERVED_SPACE(skb->dev);
if (skb_has_frag_list(skb)) {
unsigned int first_len = skb_pagelen(skb);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index ebe161d72fbd..522ba45ce9b7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1977,8 +1977,10 @@ do_time_wait:
}
drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb);
- if (drop_reason)
- break;
+ if (drop_reason) {
+ inet_twsk_put(inet_twsk(sk));
+ goto discard_it;
+ }
}
/* to ACK */
fallthrough;
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 125ea9a5b8a0..3b749475f6ed 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
if (!xdst->u.rt6.rt6i_idev) {
netdev_put(dev, &xdst->u.dst.dev_tracker);
+ xdst->u.dst.dev = NULL;
return -ENODEV;
}
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index fed240b453bd..b85fb9767dec 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2089,6 +2089,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
}
}
}
+ if (sk)
+ sock_hold(sk);
read_unlock(&iucv_sk_list.lock);
if (!iucv)
sk = NULL;
@@ -2138,6 +2140,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
kfree_skb(skb);
}
+ if (sk)
+ sock_put(sk);
return err;
}
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index e8f427375c68..260460d50f54 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -767,7 +767,6 @@ static struct sock *llc_create_incoming_sock(struct sock *sk,
newllc->dev = dev;
dev_hold(dev);
llc_sap_add_socket(llc->sap, newsk);
- llc_sap_hold(llc->sap);
out:
return newsk;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 3b58af59f7e4..b00191e02a63 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1146,9 +1146,6 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
fd->max_interval = params->max_interval;
old = sdata_dereference(link->u.ap.fils_discovery, sdata);
- if (old)
- kfree_rcu(old, rcu_head);
-
if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
@@ -1160,6 +1157,9 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
}
+ if (old)
+ kfree_rcu(old, rcu_head);
+
*changed |= BSS_CHANGED_FILS_DISCOVERY;
return 0;
}
@@ -1179,8 +1179,6 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
link_conf->unsol_bcast_probe_resp_interval = params->interval;
old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
- if (old)
- kfree_rcu(old, rcu_head);
if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
@@ -1193,6 +1191,9 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
}
+ if (old)
+ kfree_rcu(old, rcu_head);
+
*changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
return 0;
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index d0fd6054f182..882f91abbb66 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -668,7 +668,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
- sta_info_flush(sdata, -1);
+ netif_carrier_off(sdata->dev);
+ if (!sta_info_flush(sdata, -1))
+ synchronize_net();
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
@@ -682,8 +684,6 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
}
spin_unlock_bh(&ifibss->incomplete_lock);
- netif_carrier_off(sdata->dev);
-
sdata->vif.cfg.ibss_joined = false;
sdata->vif.cfg.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false;
@@ -710,7 +710,6 @@ static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy,
u.ibss.csa_connection_drop_work);
ieee80211_ibss_disconnect(sdata);
- synchronize_rcu();
skb_queue_purge(&sdata->skb_queue);
/* trigger a scan to find another IBSS network to join */
@@ -1029,8 +1028,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
u8 rx_nss = sta->sta.deflink.rx_nss;
- /* Force rx_nss recalculation */
- sta->sta.deflink.rx_nss = 0;
+ ieee80211_sta_init_nss_bw_capa(&sta->deflink,
+ &sdata->deflink.conf->chanreq.oper);
rate_control_rate_init(&sta->deflink);
if (sta->sta.deflink.rx_nss != rx_nss)
changed |= IEEE80211_RC_NSS_CHANGED;
@@ -1797,8 +1796,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
- synchronize_rcu();
-
skb_queue_purge(&sdata->skb_queue);
timer_delete_sync(&sdata->u.ibss.timer);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 086272c3ec08..43460a705a6b 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -588,6 +588,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
WARN_ON(!list_empty(&sdata->u.ap.vlans));
} else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
/* remove all packets in parent bc_buf pointing to this dev */
+ __skb_queue_head_init(&freeq);
ps = &sdata->bss->ps;
spin_lock_irqsave(&ps->bc_buf.lock, flags);
@@ -595,10 +596,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
if (skb->dev == sdata->dev) {
__skb_unlink(skb, &ps->bc_buf);
local->total_ps_buffered--;
- ieee80211_free_txskb(&local->hw, skb);
+ __skb_queue_tail(&freeq, skb);
}
}
spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
+
+ skb_queue_walk_safe(&freeq, skb, tmp) {
+ __skb_unlink(skb, &freeq);
+ ieee80211_free_txskb(&local->hw, skb);
+ }
}
if (going_down)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 90d295cc364f..eb1eaaf34612 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1602,7 +1602,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL);
if (!sband) {
result = -ENOMEM;
- goto fail_rate;
+ goto fail_band;
}
wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n",
@@ -1678,6 +1678,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
#endif
wiphy_unregister(local->hw.wiphy);
fail_wiphy_register:
+ fail_band:
rtnl_lock();
rate_control_deinitialize(local);
ieee80211_remove_interfaces(local);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9e92337bb6f9..fa773f3b0541 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5641,13 +5641,15 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+ u16 reason_code;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
- if (len < 24 + 2)
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
return;
+ reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+
if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) {
ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code);
return;
@@ -7138,7 +7140,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
- u16 capab_info, status_code, aid;
+ u16 capab_info, status_code, aid = 0;
struct ieee80211_elems_parse_params parse_params = {
.bss = NULL,
.link_id = -1,
@@ -7217,8 +7219,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (elems->aid_resp)
aid = le16_to_cpu(elems->aid_resp->aid);
- else
+ else if (!assoc_data->s1g)
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+ else if (status_code == WLAN_STATUS_SUCCESS)
+ goto abandon_assoc;
/*
* The 5 MSB of the AID field are reserved for a non-S1G STA. For
diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c
index 1800bb96dd29..19e08661be43 100644
--- a/net/mac80211/nan.c
+++ b/net/mac80211/nan.c
@@ -253,9 +253,12 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_nan_channel *sched_idx_to_chan[IEEE80211_NAN_MAX_CHANNELS] = {};
struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched;
- struct ieee80211_nan_sched_cfg backup_sched;
+ struct ieee80211_nan_sched_cfg *backup_sched __free(kfree) = kmalloc_obj(*backup_sched);
int ret;
+ if (!backup_sched)
+ return -ENOMEM;
+
if (sched->n_channels > IEEE80211_NAN_MAX_CHANNELS)
return -EOPNOTSUPP;
@@ -275,13 +278,13 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
- memcpy(backup_sched.schedule, sched_cfg->schedule,
- sizeof(backup_sched.schedule));
- memcpy(backup_sched.channels, sched_cfg->channels,
- sizeof(backup_sched.channels));
- memcpy(backup_sched.avail_blob, sched_cfg->avail_blob,
- sizeof(backup_sched.avail_blob));
- backup_sched.avail_blob_len = sched_cfg->avail_blob_len;
+ memcpy(backup_sched->schedule, sched_cfg->schedule,
+ sizeof(backup_sched->schedule));
+ memcpy(backup_sched->channels, sched_cfg->channels,
+ sizeof(backup_sched->channels));
+ memcpy(backup_sched->avail_blob, sched_cfg->avail_blob,
+ sizeof(backup_sched->avail_blob));
+ backup_sched->avail_blob_len = sched_cfg->avail_blob_len;
memcpy(sched_cfg->avail_blob, sched->nan_avail_blob,
sched->nan_avail_blob_len);
@@ -380,17 +383,17 @@ err:
if (!chan_def->chan)
continue;
- if (!cfg80211_chandef_identical(&backup_sched.channels[i].chanreq.oper,
+ if (!cfg80211_chandef_identical(&backup_sched->channels[i].chanreq.oper,
chan_def))
ieee80211_nan_remove_channel(sdata,
&sched_cfg->channels[i]);
}
/* Re-add all backed up channels */
- for (int i = 0; i < ARRAY_SIZE(backup_sched.channels); i++) {
+ for (int i = 0; i < ARRAY_SIZE(backup_sched->channels); i++) {
struct ieee80211_nan_channel *chan = &sched_cfg->channels[i];
- *chan = backup_sched.channels[i];
+ *chan = backup_sched->channels[i];
/*
* For deferred update, no channels were removed and the channel
@@ -421,11 +424,11 @@ err:
}
}
- memcpy(sched_cfg->schedule, backup_sched.schedule,
- sizeof(backup_sched.schedule));
- memcpy(sched_cfg->avail_blob, backup_sched.avail_blob,
- sizeof(backup_sched.avail_blob));
- sched_cfg->avail_blob_len = backup_sched.avail_blob_len;
+ memcpy(sched_cfg->schedule, backup_sched->schedule,
+ sizeof(backup_sched->schedule));
+ memcpy(sched_cfg->avail_blob, backup_sched->avail_blob,
+ sizeof(backup_sched->avail_blob));
+ sched_cfg->avail_blob_len = backup_sched->avail_blob_len;
sched_cfg->deferred = false;
bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index fb9a3574afe9..d9ea19be075d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1526,6 +1526,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
if (status->flag & RX_FLAG_DUP_VALIDATED)
return RX_CONTINUE;
+ if (ieee80211_is_ext(hdr->frame_control))
+ return RX_CONTINUE;
+
/*
* Drop duplicate 802.11 retransmissions
* (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
@@ -4510,12 +4513,16 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
- bool multicast = is_multicast_ether_addr(hdr->addr1) ||
- ieee80211_is_s1g_beacon(hdr->frame_control);
+ bool multicast;
static const u8 nan_network_id[ETH_ALEN] __aligned(2) = {
0x51, 0x6F, 0x9A, 0x01, 0x00, 0x00
};
+ if (ieee80211_is_s1g_beacon(hdr->frame_control))
+ return sdata->vif.type == NL80211_IFTYPE_STATION && bssid;
+
+ multicast = is_multicast_ether_addr(hdr->addr1);
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
if (!bssid && !sdata->u.mgd.use_4addr)
@@ -5212,6 +5219,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
hdr = (struct ieee80211_hdr *)rx->skb->data;
}
+ if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
+ ieee80211_invoke_rx_handlers(rx);
+ return true;
+ }
+
/* Store a copy of the pre-translated link addresses for SW crypto */
if (unlikely(is_unicast_ether_addr(hdr->addr1) &&
!ieee80211_is_data(hdr->frame_control)))
@@ -5301,6 +5313,13 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
struct sta_info *sta;
int link_id = -1;
+ if (ieee80211_is_s1g_beacon(hdr->frame_control)) {
+ if (!ieee80211_rx_data_set_sta(rx, NULL, -1))
+ return false;
+
+ return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+ }
+
/*
* Look up link station first, in case there's a
* chance that they might have a link address that
@@ -5376,6 +5395,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
err = -ENOBUFS;
else
err = skb_linearize(skb);
+ } else if (ieee80211_is_s1g_beacon(fc)) {
+ size_t s1g_hdr_len = offsetof(struct ieee80211_ext,
+ u.s1g_beacon.variable) +
+ ieee80211_s1g_optional_len(fc);
+
+ if (skb->len < s1g_hdr_len)
+ err = -ENOBUFS;
+ else
+ err = skb_linearize(skb);
+ } else if (ieee80211_is_ext(fc)) {
+ err = -EINVAL;
} else {
err = !pskb_may_pull(skb, ieee80211_hdrlen(fc));
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 02b587ff8504..22eba0e6e54c 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -355,6 +355,15 @@ static void sta_info_free_link(struct link_sta_info *link_sta)
free_percpu(link_sta->pcpu_rx_stats);
}
+static void sta_link_free_rcu(struct rcu_head *head)
+{
+ struct sta_link_alloc *alloc =
+ container_of(head, struct sta_link_alloc, rcu_head);
+
+ sta_info_free_link(&alloc->info);
+ kfree(alloc);
+}
+
static void sta_accumulate_removed_link_stats(struct sta_info *sta, int link_id)
{
struct link_sta_info *link_sta = wiphy_dereference(sta->local->hw.wiphy,
@@ -439,10 +448,8 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id,
RCU_INIT_POINTER(sta->link[link_id], NULL);
RCU_INIT_POINTER(sta->sta.link[link_id], NULL);
- if (alloc) {
- sta_info_free_link(&alloc->info);
- kfree_rcu(alloc, rcu_head);
- }
+ if (alloc)
+ call_rcu(&alloc->rcu_head, sta_link_free_rcu);
ieee80211_sta_recalc_aggregates(&sta->sta);
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c13b209fad47..91b14112e24f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2607,6 +2607,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local,
return info_id;
}
+static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id)
+{
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_remove(&local->ack_status_frames, info_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ kfree_skb(ack_skb);
+}
+
/**
* ieee80211_build_hdr - build 802.11 header in the given frame
* @sdata: virtual interface to build the header for
@@ -2982,7 +2994,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto free;
}
}
@@ -3050,6 +3063,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
return skb;
free:
+ if (info_id)
+ ieee80211_remove_ack_skb(local, info_id);
kfree_skb(skb);
return ERR_PTR(ret);
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index f6d4ae4127c8..59f73dabe6e0 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -73,6 +73,9 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
if (ieee80211_is_s1g_beacon(fc)) {
struct ieee80211_ext *ext = (void *) hdr;
+ if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa))
+ return NULL;
+
return ext->u.s1g_beacon.sa;
}
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index ca504d9626cf..318cb7e2ac5f 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -2186,6 +2186,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
int ifindex;
if (i == RTA_OIF) {
+ if (!tb[i])
+ continue;
+
ifindex = nla_get_u32(tb[i]);
filter->dev = dev_get_by_index_rcu(net, ifindex);
if (!filter->dev)
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
index d54d7da58334..b0e00be85cb1 100644
--- a/net/netfilter/ipvs/ip_vs_app.c
+++ b/net/netfilter/ipvs/ip_vs_app.c
@@ -361,14 +361,13 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
struct ip_vs_iphdr *ipvsh)
{
int diff;
- const unsigned int tcp_offset = ip_hdrlen(skb);
struct tcphdr *th;
__u32 seq;
- if (skb_ensure_writable(skb, tcp_offset + sizeof(*th)))
+ if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
+ th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
@@ -438,14 +437,13 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
struct ip_vs_iphdr *ipvsh)
{
int diff;
- const unsigned int tcp_offset = ip_hdrlen(skb);
struct tcphdr *th;
__u32 seq;
- if (skb_ensure_writable(skb, tcp_offset + sizeof(*th)))
+ if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
+ th = (struct tcphdr *)(skb_network_header(skb) + 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 35cbe821c259..bafab93451d0 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -1219,8 +1219,7 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
snet.in6 = ciph.saddr.in6;
offset = ciph.len;
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
- pp, offset, sizeof(struct ipv6hdr),
- hooknum);
+ pp, offset, ipvsh->len, hooknum);
}
#endif
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index ce542ed4b013..9fef4335da13 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -736,13 +736,11 @@ int
ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
- struct iphdr *iph = ip_hdr(skb);
-
- if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr,
+ if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr,
IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0)
goto tx_error;
- ip_send_check(iph);
+ ip_send_check(ip_hdr(skb));
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c
index 91582069f6d2..e9ea6d9466e7 100644
--- a/net/netfilter/nf_conncount.c
+++ b/net/netfilter/nf_conncount.c
@@ -211,8 +211,8 @@ check_connections:
/* Not found, but might be about to be confirmed */
if (PTR_ERR(found) == -EAGAIN) {
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
- nf_ct_zone_id(zone, zone->dir))
+ nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) ==
+ nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL))
goto out_put; /* already exists */
} else {
collect++;
@@ -223,7 +223,7 @@ check_connections:
found_ct = nf_ct_tuplehash_to_ctrack(found);
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_equal(found_ct, zone, zone->dir)) {
+ nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) {
/*
* We should not see tuples twice unless someone hooks
* this into a table without "-p tcp --syn".
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 9df159448b89..cc8d8e85169f 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -77,7 +77,7 @@ next:
hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &evicted_list);
- if (time_after(stop, jiffies)) {
+ if (time_after(jiffies, stop)) {
ret = STATE_RESTART;
break;
}
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 2a829b5e8240..b66e65439341 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -127,18 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow,
switch (route->tuple[dir].xmit_type) {
case FLOW_OFFLOAD_XMIT_DIRECT:
- if (flow_tuple->tun_num) {
+ if (route->tuple[!dir].in.num_tuns) {
flow_tuple->dst_cache = dst;
flow_tuple->dst_cookie =
flow_offload_dst_cookie(flow_tuple);
+ } else {
+ dst_release(dst);
}
memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
ETH_ALEN);
memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
ETH_ALEN);
flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
- if (!flow_tuple->tun_num)
- dst_release(dst);
break;
case FLOW_OFFLOAD_XMIT_XFRM:
case FLOW_OFFLOAD_XMIT_NEIGH:
diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c
index b4f7bbc3f3ca..51c7f7ce88d9 100644
--- a/net/netfilter/xt_nat.c
+++ b/net/netfilter/xt_nat.c
@@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par)
static int xt_nat_checkentry(const struct xt_tgchk_param *par)
{
+ switch (par->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_INET:
+ break;
+ default:
+ return -EINVAL;
+ }
+
return nf_ct_netns_get(par->net, par->family);
}
diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c
index dd98f758176c..a388881c68d4 100644
--- a/net/netfilter/xt_physdev.c
+++ b/net/netfilter/xt_physdev.c
@@ -130,11 +130,6 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
if (X(physoutdev))
return -ENAMETOOLONG;
}
-
- if (X(in_mask))
- return -ENAMETOOLONG;
- if (X(out_mask))
- return -ENAMETOOLONG;
#undef X
if (!brnf_probed) {
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 13052408a132..d8079dee700e 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -2496,13 +2496,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa,
return used;
}
-static inline void add_nested_action_end(struct sw_flow_actions *sfa,
- int st_offset)
+static inline int add_nested_action_end(struct sw_flow_actions *sfa,
+ int st_offset)
{
- struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions +
- st_offset);
+ struct nlattr *a;
+ u32 attr_len;
+
+ if (WARN_ON_ONCE(st_offset < 0 ||
+ (u32)st_offset > sfa->actions_len))
+ return -EINVAL;
+
+ attr_len = sfa->actions_len - (u32)st_offset;
+ if (WARN_ON_ONCE(attr_len < NLA_HDRLEN))
+ return -EINVAL;
- a->nla_len = sfa->actions_len - st_offset;
+ if (attr_len > U16_MAX)
+ return -EMSGSIZE;
+
+ a = (struct nlattr *)((u8 *)sfa->actions + st_offset);
+ a->nla_len = attr_len;
+ return 0;
+}
+
+/* Free the generated action-list tail at @start and truncate it.
+ * If @nested, @start points to its containing nlattr header.
+ */
+static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested)
+{
+ const struct nlattr *actions;
+ u32 len;
+
+ if (start < 0)
+ return;
+
+ if (WARN_ON_ONCE((u32)start > sfa->actions_len))
+ return;
+
+ actions = (const struct nlattr *)((u8 *)sfa->actions + start);
+ len = sfa->actions_len - (u32)start;
+
+ if (nested) {
+ if (len < NLA_HDRLEN)
+ goto out;
+
+ actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN);
+ len -= NLA_HDRLEN;
+ }
+
+ ovs_nla_free_nested_actions(actions, len);
+out:
+ sfa->actions_len = start;
}
static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
@@ -2522,6 +2565,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
const struct nlattr *probability, *actions;
const struct nlattr *a;
+ int actions_start;
int rem, start, err;
struct sample_arg arg;
@@ -2565,18 +2609,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg),
log);
if (err)
- return err;
+ goto err;
+ actions_start = (*sfa)->actions_len;
err = __ovs_nla_copy_actions(net, actions, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, actions_start, false);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
static int validate_and_copy_dec_ttl(struct net *net,
@@ -2624,18 +2677,31 @@ static int validate_and_copy_dec_ttl(struct net *net,
return start;
action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log);
- if (action_start < 0)
- return action_start;
+ if (action_start < 0) {
+ err = action_start;
+ goto err;
+ }
err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type,
vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
+
+ err = add_nested_action_end(*sfa, action_start);
+ if (err)
+ goto err_free;
- add_nested_action_end(*sfa, action_start);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, action_start, true);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
static int validate_and_copy_clone(struct net *net,
@@ -2646,6 +2712,7 @@ static int validate_and_copy_clone(struct net *net,
u32 mpls_label_count, bool log, bool last,
u32 depth)
{
+ int actions_start;
int start, err;
u32 exec;
@@ -2661,17 +2728,26 @@ static int validate_and_copy_clone(struct net *net,
err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec,
sizeof(exec), log);
if (err)
- return err;
+ goto err;
+ actions_start = (*sfa)->actions_len;
err = __ovs_nla_copy_actions(net, attr, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, actions_start, false);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
void ovs_match_init(struct sw_flow_match *match,
@@ -2763,20 +2839,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL,
GFP_KERNEL);
- if (!tun_dst)
- return -ENOMEM;
+ if (!tun_dst) {
+ err = -ENOMEM;
+ goto err;
+ }
err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL);
- if (err) {
- dst_release((struct dst_entry *)tun_dst);
- return err;
- }
+ if (err)
+ goto err_free_tun_dst;
a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
sizeof(*ovs_tun), log);
if (IS_ERR(a)) {
- dst_release((struct dst_entry *)tun_dst);
- return PTR_ERR(a);
+ err = PTR_ERR(a);
+ goto err_free_tun_dst;
}
ovs_tun = nla_data(a);
@@ -2797,8 +2873,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
ip_tunnel_info_opts_set(tun_info,
TUN_METADATA_OPTS(&key, key.tun_opts_len),
key.tun_opts_len, dst_opt_type);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (WARN_ON_ONCE(err))
+ goto err_free_tun_dst;
+
+ return 0;
+err_free_tun_dst:
+ dst_release((struct dst_entry *)tun_dst);
+err:
+ (*sfa)->actions_len = start;
return err;
}
@@ -2971,7 +3055,7 @@ static int validate_set(const struct nlattr *a,
/* Convert non-masked non-tunnel set actions to masked set actions. */
if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) {
- int start, len = key_len * 2;
+ int err, start, len = key_len * 2;
struct nlattr *at;
*skip_copy = true;
@@ -2983,8 +3067,11 @@ static int validate_set(const struct nlattr *a,
return start;
at = __add_action(sfa, key_type, NULL, len, log);
- if (IS_ERR(at))
- return PTR_ERR(at);
+ if (IS_ERR(at)) {
+ err = PTR_ERR(at);
+ (*sfa)->actions_len = start;
+ return err;
+ }
memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */
memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */
@@ -2994,7 +3081,11 @@ static int validate_set(const struct nlattr *a,
mask->ipv6_label &= htonl(0x000FFFFF);
}
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (WARN_ON_ONCE(err)) {
+ (*sfa)->actions_len = start;
+ return err;
+ }
}
return 0;
@@ -3040,7 +3131,8 @@ static int validate_and_copy_check_pkt_len(struct net *net,
const struct nlattr *acts_if_greater, *acts_if_lesser_eq;
struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1];
struct check_pkt_len_arg arg;
- int nested_acts_start;
+ int greater_acts_start = -1;
+ int lesser_acts_start = -1;
int start, err;
err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX,
@@ -3075,37 +3167,58 @@ static int validate_and_copy_check_pkt_len(struct net *net,
err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg,
sizeof(arg), log);
if (err)
- return err;
+ goto err_free;
- nested_acts_start = add_nested_action_start(sfa,
- OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log);
- if (nested_acts_start < 0)
- return nested_acts_start;
+ lesser_acts_start =
+ add_nested_action_start(sfa,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL,
+ log);
+ if (lesser_acts_start < 0) {
+ err = lesser_acts_start;
+ goto err_free;
+ }
err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, nested_acts_start);
+ err = add_nested_action_end(*sfa, lesser_acts_start);
+ if (err)
+ goto err_free;
- nested_acts_start = add_nested_action_start(sfa,
- OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log);
- if (nested_acts_start < 0)
- return nested_acts_start;
+ greater_acts_start =
+ add_nested_action_start(sfa,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER,
+ log);
+ if (greater_acts_start < 0) {
+ err = greater_acts_start;
+ goto err_free;
+ }
err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
+
+ err = add_nested_action_end(*sfa, greater_acts_start);
+ if (err)
+ goto err_free;
- add_nested_action_end(*sfa, nested_acts_start);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, greater_acts_start, true);
+ ovs_nla_trim(*sfa, lesser_acts_start, true);
+ ovs_nla_trim(*sfa, start, false);
+ return err;
}
static int validate_psample(const struct nlattr *attr)
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index 876b30c5709e..b14807761d82 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -342,14 +342,20 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
[TCA_TUNNEL_KEY_ENC_TTL] = { .type = NLA_U8 },
};
-static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
+static void tunnel_key_release_params_rcu(struct rcu_head *head)
{
- if (!p)
- return;
+ struct tcf_tunnel_key_params *p = container_of(head, typeof(*p), rcu);
+
if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
dst_release(&p->tcft_enc_metadata->dst);
+ kfree(p);
+}
- kfree_rcu(p, rcu);
+static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
+{
+ if (!p)
+ return;
+ call_rcu(&p->rcu, tunnel_key_release_params_rcu);
}
static int tunnel_key_init(struct net *net, struct nlattr *nla,
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index d23d935e128e..3893b44448b3 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -74,7 +74,8 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
const struct sctp_association *asoc,
const union sctp_subtype type,
void *arg,
- struct sctp_cmd_seq *commands);
+ struct sctp_cmd_seq *commands,
+ struct sctp_errhdr *err);
static enum sctp_disposition sctp_sf_shut_8_4_5(
struct net *net,
const struct sctp_endpoint *ep,
@@ -2529,9 +2530,15 @@ enum sctp_disposition sctp_sf_cookie_echoed_err(
* errors.
*/
sctp_walk_errors(err, chunk->chunk_hdr) {
- if (SCTP_ERROR_STALE_COOKIE == err->cause)
- return sctp_sf_do_5_2_6_stale(net, ep, asoc, type,
- arg, commands);
+ if (err->cause != SCTP_ERROR_STALE_COOKIE)
+ continue;
+ /* The staleness is only meaningful if the cause is long
+ * enough to hold it; a shorter one is malformed.
+ */
+ if (ntohs(err->length) < sizeof(*err) + sizeof(__be32))
+ break;
+ return sctp_sf_do_5_2_6_stale(net, ep, asoc, type,
+ arg, commands, err);
}
/* It is possible to have malformed error causes, and that
@@ -2573,13 +2580,13 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
const struct sctp_association *asoc,
const union sctp_subtype type,
void *arg,
- struct sctp_cmd_seq *commands)
+ struct sctp_cmd_seq *commands,
+ struct sctp_errhdr *err)
{
int attempts = asoc->init_err_counter + 1;
- struct sctp_chunk *chunk = arg, *reply;
struct sctp_cookie_preserve_param bht;
struct sctp_bind_addr *bp;
- struct sctp_errhdr *err;
+ struct sctp_chunk *reply;
u32 stale;
if (attempts > asoc->max_init_attempts) {
@@ -2590,8 +2597,6 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
return SCTP_DISPOSITION_DELETE_TCB;
}
- err = (struct sctp_errhdr *)(chunk->skb->data);
-
/* When calculating the time extension, an implementation
* SHOULD use the RTT information measured based on the
* previous COOKIE ECHO / ERROR exchange, and should add no
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 3dcf63b04c41..610238d723ff 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1335,6 +1335,7 @@ void wiphy_unregister(struct wiphy *wiphy)
/* this has nothing to do now but make sure it's gone */
cancel_work_sync(&rdev->wiphy_work);
+ cancel_work_sync(&rdev->sched_scan_res_wk);
cancel_work_sync(&rdev->rfkill_block);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
@@ -1424,6 +1425,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
list_del_rcu(&wdev->list);
synchronize_net();
rdev->devlist_generation++;
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
cfg80211_mlme_purge_registrations(wdev);
@@ -1613,7 +1615,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
INIT_LIST_HEAD(&wdev->mgmt_registrations);
INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock);
- INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
+ wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
@@ -1637,7 +1639,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;
- INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
+ wiphy_work_init(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
}
void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
@@ -1743,11 +1745,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev, -1);
- scoped_guard(wiphy, &rdev->wiphy)
+ scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_remove_links(wdev);
- /* since we just did cfg80211_leave() nothing to do there */
- cancel_work_sync(&wdev->disconnect_wk);
- cancel_work_sync(&wdev->pmsr_free_wk);
+ /* since we just did cfg80211_leave() nothing to do there */
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
+ }
break;
case NETDEV_DOWN:
wiphy_lock(&rdev->wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index df47ed6208a5..ac6ce9f967ec 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -428,7 +428,7 @@ void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *peer_addr,
const u8 *td_bitmap, u8 td_bitmap_len);
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
-void cfg80211_autodisconnect_wk(struct work_struct *work);
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work);
/* SME implementation */
void cfg80211_conn_work(struct work_struct *work);
@@ -586,7 +586,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
-void cfg80211_pmsr_free_wk(struct work_struct *work);
+void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work);
void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id);
void cfg80211_remove_links(struct wireless_dev *wdev);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 2a2c173058ba..7824b7ac2770 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -32,15 +32,11 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED,
.req_ie = data->req_ies,
.req_ie_len = data->req_ies_len,
- .resp_ie = mgmt->u.assoc_resp.variable,
- .resp_ie_len = data->len -
- offsetof(struct ieee80211_mgmt,
- u.assoc_resp.variable),
- .status = le16_to_cpu(mgmt->u.assoc_resp.status_code),
.ap_mld_addr = data->ap_mld_addr,
.assoc_encrypted = data->assoc_encrypted,
};
unsigned int link_id;
+ bool is_s1g = false;
for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
cr.links[link_id].status = data->links[link_id].status;
@@ -61,16 +57,32 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
if (cr.links[link_id].bss->channel->band == NL80211_BAND_S1GHZ) {
WARN_ON(link_id);
- cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
- cr.resp_ie_len = data->len -
- offsetof(struct ieee80211_mgmt,
- u.s1g_assoc_resp.variable);
+ is_s1g = true;
}
if (cr.ap_mld_addr)
cr.valid_links |= BIT(link_id);
}
+ if (is_s1g) {
+ if (data->len < offsetof(struct ieee80211_mgmt,
+ u.s1g_assoc_resp.variable))
+ goto free_bss;
+ cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
+ cr.resp_ie_len = data->len -
+ offsetof(struct ieee80211_mgmt,
+ u.s1g_assoc_resp.variable);
+ } else {
+ if (data->len < offsetof(struct ieee80211_mgmt,
+ u.assoc_resp.variable))
+ goto free_bss;
+ cr.resp_ie = mgmt->u.assoc_resp.variable;
+ cr.resp_ie_len = data->len -
+ offsetof(struct ieee80211_mgmt,
+ u.assoc_resp.variable);
+ }
+ cr.status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
trace_cfg80211_send_rx_assoc(dev, data);
/*
@@ -79,22 +91,24 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
* and got a reject -- we only try again with an assoc
* frame instead of reassoc.
*/
- if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) {
- for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
- struct cfg80211_bss *bss = data->links[link_id].bss;
-
- if (!bss)
- continue;
-
- cfg80211_unhold_bss(bss_from_pub(bss));
- cfg80211_put_bss(wiphy, bss);
- }
- return;
- }
+ if (cfg80211_sme_rx_assoc_resp(wdev, cr.status))
+ goto free_bss;
nl80211_send_rx_assoc(rdev, dev, data);
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS);
+ return;
+
+free_bss:
+ for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
+ struct cfg80211_bss *bss = data->links[link_id].bss;
+
+ if (!bss)
+ continue;
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+ }
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
@@ -151,19 +165,35 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
+ if (len < sizeof(fc))
+ return;
+
+ fc = mgmt->frame_control;
- if (WARN_ON(len < 2))
+ if (ieee80211_is_auth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.auth.status_code))
+ return;
+ } else if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
- if (ieee80211_is_auth(mgmt->frame_control))
+ if (ieee80211_is_auth(fc))
cfg80211_process_auth(wdev, buf, len);
- else if (ieee80211_is_deauth(mgmt->frame_control))
+ else if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, false);
- else if (ieee80211_is_disassoc(mgmt->frame_control))
+ else
cfg80211_process_disassoc(wdev, buf, len, false);
}
EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
@@ -216,15 +246,28 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
+ if (len < sizeof(fc))
+ return;
- if (WARN_ON(len < 2))
+ fc = mgmt->frame_control;
+
+ if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
- if (ieee80211_is_deauth(mgmt->frame_control))
+ if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, reconnect);
else
cfg80211_process_disassoc(wdev, buf, len, reconnect);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 53b4b3f76697..5adcb6bd0fc5 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -461,7 +461,9 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
static const struct nla_policy
nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
- [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
+ [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] =
+ NLA_POLICY_RANGE(NLA_U32, NL80211_PREAMBLE_LEGACY,
+ NL80211_PREAMBLE_HE),
[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
@@ -630,7 +632,7 @@ nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
[NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
[NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] =
- NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS),
+ NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1),
};
static const struct nla_policy
@@ -6510,7 +6512,8 @@ static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
}
static struct cfg80211_mbssid_elems *
-nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs,
+ struct netlink_ext_ack *extack)
{
struct nlattr *nl_elems;
struct cfg80211_mbssid_elems *elems;
@@ -6521,6 +6524,12 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
return ERR_PTR(-EINVAL);
nla_for_each_nested(nl_elems, attrs, rem_elems) {
+ int ret;
+
+ ret = validate_ie_attr(nl_elems, extack);
+ if (ret)
+ return ERR_PTR(ret);
+
if (num_elems >= 255)
return ERR_PTR(-EINVAL);
num_elems++;
@@ -6787,7 +6796,8 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
struct cfg80211_mbssid_elems *mbssid =
nl80211_parse_mbssid_elems(&rdev->wiphy,
- attrs[NL80211_ATTR_MBSSID_ELEMS]);
+ attrs[NL80211_ATTR_MBSSID_ELEMS],
+ extack);
if (IS_ERR(mbssid))
return PTR_ERR(mbssid);
@@ -6803,8 +6813,10 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (IS_ERR(rnr))
return PTR_ERR(rnr);
- if (rnr && rnr->cnt < bcn->mbssid_ies->cnt)
+ if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) {
+ kfree(rnr);
return -EINVAL;
+ }
bcn->rnr_ies = rnr;
}
@@ -22942,7 +22954,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
wdev->nl_owner_dead = true;
schedule_work(&rdev->destroy_work);
} else if (wdev->conn_owner_nlportid == notify->portid) {
- schedule_work(&wdev->disconnect_wk);
+ wiphy_work_queue(wdev->wiphy,
+ &wdev->disconnect_wk);
}
cfg80211_release_pmsr(wdev, notify->portid);
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
index c8447448f3a5..34c3625f7fd5 100644
--- a/net/wireless/pmsr.c
+++ b/net/wireless/pmsr.c
@@ -125,6 +125,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
"FTM: LCI request not supported");
+ return -EOPNOTSUPP;
}
out->ftm.request_civicloc =
@@ -133,6 +134,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
"FTM: civic location request not supported");
+ return -EOPNOTSUPP;
}
out->ftm.trigger_based =
@@ -310,6 +312,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
{
struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
+ bool have_measurement_type = false;
struct nlattr *treq;
int err, rem;
@@ -376,6 +379,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
}
nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
+ if (have_measurement_type) {
+ NL_SET_ERR_MSG_ATTR(info->extack, treq,
+ "multiple measurement types in request data");
+ return -EINVAL;
+ }
+
+ have_measurement_type = true;
+
switch (nla_type(treq)) {
case NL80211_PMSR_TYPE_FTM:
err = pmsr_parse_ftm(rdev, treq, out, info);
@@ -385,10 +396,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
"unsupported measurement type");
err = -EINVAL;
}
+ if (err)
+ return err;
}
- if (err)
- return err;
+ if (!have_measurement_type) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ req[NL80211_PMSR_REQ_ATTR_DATA],
+ "missing measurement type in request data");
+ return -EINVAL;
+ }
return 0;
}
@@ -427,6 +444,11 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
}
}
+ if (!count) {
+ NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified");
+ return -EINVAL;
+ }
+
req = kzalloc_flex(*req, peers, count);
if (!req)
return -ENOMEM;
@@ -807,13 +829,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
}
}
-void cfg80211_pmsr_free_wk(struct work_struct *work)
+void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev = container_of(work, struct wireless_dev,
pmsr_free_wk);
- guard(wiphy)(wdev->wiphy);
-
cfg80211_pmsr_process_abort(wdev);
}
@@ -829,7 +849,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
}
spin_unlock_bh(&wdev->pmsr_lock);
- cancel_work_sync(&wdev->pmsr_free_wk);
+ wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
if (found)
cfg80211_pmsr_process_abort(wdev);
@@ -844,7 +864,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
list_for_each_entry(req, &wdev->pmsr_list, list) {
if (req->nl_portid == portid) {
req->nl_portid = 0;
- schedule_work(&wdev->pmsr_free_wk);
+ wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
}
}
spin_unlock_bh(&wdev->pmsr_lock);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 05b7dc6b766c..071083cc3367 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem,
return true;
if (elem->id == WLAN_EID_EXTENSION) {
- if (!ext_id_len)
+ if (!ext_id_len || !elem->datalen)
return true;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
@@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
/* For ML probe response, match the MLE in the frame body with
* MLD id being 'bssid_index'
*/
- if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 &&
+ if (parent->id == WLAN_EID_EXTENSION &&
parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK &&
+ ieee80211_mle_type_ok(parent->data + 1,
+ IEEE80211_ML_CONTROL_TYPE_BASIC,
+ parent->datalen - 1) &&
bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) {
if (!cfg80211_copy_elem_with_frags(parent,
ie, ielen,
@@ -3311,14 +3314,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
bssid = ext->u.s1g_beacon.sa;
capability = le16_to_cpu(compat->compat_info);
beacon_interval = le16_to_cpu(compat->beacon_int);
+ tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp);
+ tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32;
} else {
bssid = mgmt->bssid;
beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
}
- tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
-
if (ieee80211_is_probe_resp(mgmt->frame_control))
ftype = CFG80211_BSS_FTYPE_PRESP;
else if (ext)
@@ -3612,8 +3616,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* translate "Scan for SSID" request */
if (wreq) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out;
+ }
memcpy(creq->req.ssids[0].ssid, wreq->essid,
wreq->essid_len);
creq->req.ssids[0].ssid_len = wreq->essid_len;
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index b451df3096dd..2a719b5c487e 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1578,13 +1578,11 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
* Used to clean up after the connection / connection attempt owner socket
* disconnects
*/
-void cfg80211_autodisconnect_wk(struct work_struct *work)
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev =
container_of(work, struct wireless_dev, disconnect_wk);
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
- guard(wiphy)(wdev->wiphy);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
if (wdev->conn_owner_nlportid) {
switch (wdev->iftype) {
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 630f3dd31cc5..f153bf695b9d 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
err = x->type_offload->xmit(x, skb, esp_features);
if (err) {
if (err == -EINPROGRESS)
- return NULL;
+ return ERR_PTR(-EINPROGRESS);
XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
kfree_skb(skb);
@@ -224,7 +224,15 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
pskb = skb2;
}
- return skb;
+ /* skb_gso_segment() set skb->prev to the last segment, but async
+ * crypto may have stolen it above without updating ->prev. Repoint
+ * it at the last retained segment so validate_xmit_skb_list() does
+ * not chain onto a segment now owned by the crypto engine.
+ */
+ if (skb)
+ skb->prev = pskb;
+
+ return skb ? skb : ERR_PTR(-EINPROGRESS);
}
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
@@ -313,6 +321,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
}
xso->dev = dev;
+ xso->ifindex = dev->ifindex;
netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC);
if (xuo->flags & XFRM_OFFLOAD_INBOUND)
diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index ad810d1f97c0..597aedeac26e 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb,
}
__skb_frag_ref(tofrag);
shinfo->nr_frags++;
+ shinfo->flags |= SKBFL_SHARED_FRAG;
/* see if we are done */
fraglen = tofrag->len;
diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c
index 458931062a04..eb1b6f67739e 100644
--- a/net/xfrm/xfrm_nat_keepalive.c
+++ b/net/xfrm/xfrm_nat_keepalive.c
@@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb,
ka->encap_sport, sock_net_uid(net, NULL));
rt = ip_route_output_key(net, &fl4);
- if (IS_ERR(rt))
+ if (IS_ERR(rt)) {
+ kfree_skb(skb);
return PTR_ERR(rt);
+ }
skb_dst_set(skb, &rt->dst);
@@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb,
dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL);
if (IS_ERR(dst)) {
local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock);
+ kfree_skb(skb);
return PTR_ERR(dst);
}
@@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
sizeof(struct ipv6hdr)) +
sizeof(struct udphdr);
const u8 nat_ka_payload = 0xFF;
- int err = -EAFNOSUPPORT;
struct sk_buff *skb;
struct udphdr *uh;
@@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
switch (ka->family) {
case AF_INET:
- err = nat_keepalive_send_ipv4(skb, ka);
+ nat_keepalive_send_ipv4(skb, ka);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
- err = nat_keepalive_send_ipv6(skb, ka, uh);
+ nat_keepalive_send_ipv6(skb, ka, uh);
break;
#endif
- }
- if (err)
+ default:
kfree_skb(skb);
+ break;
+ }
}
struct nat_keepalive_work_ctx {
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 7ef861a0e823..932a313b9460 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
}
}
- if (policy->selector.prefixlen_d < dbits ||
- policy->selector.prefixlen_s < sbits)
+ if (policy->selector.prefixlen_d >= dbits &&
+ policy->selector.prefixlen_s >= sbits)
continue;
bin = xfrm_policy_inexact_alloc_bin(policy, dir);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index c58cd024e3c6..36a4f6793ede 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1547,6 +1547,7 @@ found:
xso->type = XFRM_DEV_OFFLOAD_PACKET;
xso->dir = xdo->dir;
xso->dev = dev;
+ xso->ifindex = dev->ifindex;
xso->flags = XFRM_DEV_OFFLOAD_FLAG_ACQ;
netdev_hold(dev, &xso->dev_tracker, GFP_ATOMIC);
error = dev->xfrmdev_ops->xdo_dev_state_add(dev, x,
@@ -2071,8 +2072,11 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
x->mode_cbs = orig->mode_cbs;
if (x->mode_cbs && x->mode_cbs->clone_state) {
- if (x->mode_cbs->clone_state(x, orig))
+ if (x->mode_cbs->clone_state(x, orig)) {
+ if (!x->mode_data)
+ x->mode_cbs = NULL;
goto error;
+ }
}
x->props.reqid = m->new_reqid;
@@ -3010,7 +3014,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
if (sockptr_is_null(optval) && !optlen) {
xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
- __sk_dst_reset(sk);
+ sk_dst_reset(sk);
return 0;
}
@@ -3050,7 +3054,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
if (err >= 0) {
xfrm_sk_policy_insert(sk, err, pol);
xfrm_pol_put(pol);
- __sk_dst_reset(sk);
+ sk_dst_reset(sk);
err = 0;
}
@@ -3291,6 +3295,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
if (x->mode_cbs->init_state)
err = x->mode_cbs->init_state(x);
module_put(x->mode_cbs->owner);
+ if (err && !x->mode_data)
+ x->mode_cbs = NULL;
}
error:
return err;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 6384795ee6b2..d6db63304ba6 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1201,17 +1201,26 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
return 0;
}
-static void xso_to_xuo(const struct xfrm_dev_offload *xso,
- struct xfrm_user_offload *xuo)
+static void xso_to_xuo_ifindex(const struct xfrm_dev_offload *xso, int ifindex,
+ struct xfrm_user_offload *xuo)
{
- xuo->ifindex = xso->dev->ifindex;
+ xuo->ifindex = ifindex;
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
xuo->flags = XFRM_OFFLOAD_INBOUND;
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
xuo->flags |= XFRM_OFFLOAD_PACKET;
}
-static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
+#ifdef CONFIG_XFRM_MIGRATE
+static void xso_to_xuo(const struct xfrm_dev_offload *xso,
+ struct xfrm_user_offload *xuo)
+{
+ xso_to_xuo_ifindex(xso, xso->dev->ifindex, xuo);
+}
+#endif
+
+static int copy_user_offload_ifindex(const struct xfrm_dev_offload *xso,
+ int ifindex, struct sk_buff *skb)
{
struct xfrm_user_offload *xuo;
struct nlattr *attr;
@@ -1222,11 +1231,22 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
xuo = nla_data(attr);
memset(xuo, 0, sizeof(*xuo));
- xso_to_xuo(xso, xuo);
+ xso_to_xuo_ifindex(xso, ifindex, xuo);
return 0;
}
+static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
+{
+ return copy_user_offload_ifindex(xso, xso->dev->ifindex, skb);
+}
+
+static int copy_user_state_offload(const struct xfrm_dev_offload *xso,
+ struct sk_buff *skb)
+{
+ return copy_user_offload_ifindex(xso, READ_ONCE(xso->ifindex), skb);
+}
+
static bool xfrm_redact(void)
{
return IS_ENABLED(CONFIG_SECURITY) &&
@@ -1433,8 +1453,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
&x->replay);
if (ret)
goto out;
- if(x->xso.dev)
- ret = copy_user_offload(&x->xso, skb);
+ if (READ_ONCE(x->xso.dev))
+ ret = copy_user_state_offload(&x->xso, skb);
if (ret)
goto out;
if (x->if_id) {
@@ -2104,13 +2124,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
switch (ut[i].mode) {
case XFRM_MODE_TUNNEL:
case XFRM_MODE_BEET:
+ case XFRM_MODE_IPTFS:
if (ut[i].optional && dir == XFRM_POLICY_OUT) {
NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy");
return -EINVAL;
}
break;
- case XFRM_MODE_IPTFS:
- break;
default:
if (ut[i].family != prev_family) {
NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change");
@@ -4046,8 +4065,8 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
l += nla_total_size(sizeof(*x->coaddr));
if (x->props.extra_flags)
l += nla_total_size(sizeof(x->props.extra_flags));
- if (x->xso.dev)
- l += nla_total_size(sizeof(struct xfrm_user_offload));
+ if (READ_ONCE(x->xso.dev))
+ l += nla_total_size(sizeof(struct xfrm_user_offload));
if (x->props.smark.v | x->props.smark.m) {
l += nla_total_size(sizeof(x->props.smark.v));
l += nla_total_size(sizeof(x->props.smark.m));
diff --git a/security/landlock/net.c b/security/landlock/net.c
index cbff59ec3aba..46c17116fcf4 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -351,6 +351,14 @@ static int hook_socket_sendmsg(struct socket *const sock,
access_mask_t access_request;
int ret = 0;
+ if ((msg->msg_flags & MSG_FASTOPEN) && address && sk_is_tcp(sock->sk)) {
+ ret = current_check_access_socket(
+ sock, address, addrlen, LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ true);
+ if (ret != 0)
+ return ret;
+ }
+
if (sk_is_udp(sock->sk))
access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
else
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 61f3c253d5c9..0437adf17428 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -35,8 +35,8 @@ struct landlock_layer {
*/
struct {
/**
- * @quiet: Suppresses denial logs for the object covered by this
- * rule in this domain. For filesystem rules, this inherits
+ * @flags.quiet: Suppresses denial logs for the object covered by
+ * this rule in this domain. For filesystem rules, this inherits
* down the file hierarchy.
*/
u8 quiet : 1;
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 7ddf211f75c3..360d226d0f51 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -95,8 +95,7 @@ static int hook_ptrace_access_check(struct task_struct *const child,
if (!parent_subject)
return 0;
- scoped_guard(rcu)
- {
+ scoped_guard(rcu) {
const struct landlock_ruleset *const child_dom =
landlock_get_task_domain(child);
err = domain_ptrace(parent_subject->domain, child_dom);
@@ -370,8 +369,7 @@ static int hook_task_kill(struct task_struct *const p,
if (!subject)
return 0;
- scoped_guard(rcu)
- {
+ scoped_guard(rcu) {
is_scoped = domain_is_scoped(subject->domain,
landlock_get_task_domain(p),
signal_scope.scope);
@@ -422,8 +420,7 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg)
return 0;
- scoped_guard(rcu)
- {
+ scoped_guard(rcu) {
is_scoped = domain_is_scoped(subject->domain,
landlock_get_task_domain(tsk),
signal_scope.scope);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 70a3388c047d..8d6945edae7a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3969,9 +3969,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
-static int __file_map_prot_check(const struct cred *cred,
- const struct file *file, unsigned long prot,
- bool shared, bool bf_user_file)
+static int __file_map_prot_check(const struct file *file, unsigned long prot,
+ bool shared, bool mounter_check,
+ bool bf_user_file)
{
struct inode *inode = NULL;
bool prot_exec = prot & PROT_EXEC;
@@ -3984,10 +3984,10 @@ static int __file_map_prot_check(const struct cred *cred,
inode = file_inode(file);
}
- if (default_noexec && prot_exec &&
+ if (!mounter_check && default_noexec && prot_exec &&
(!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
int rc;
- u32 sid = cred_sid(cred);
+ u32 sid = current_sid();
/*
* We are making executable an anonymous mapping or a private
@@ -4000,6 +4000,8 @@ static int __file_map_prot_check(const struct cred *cred,
}
if (file) {
+ const struct cred *cred = mounter_check ?
+ file->f_cred : current_cred();
/* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
if (shared && prot_write)
@@ -4013,11 +4015,11 @@ static int __file_map_prot_check(const struct cred *cred,
return 0;
}
-static inline int file_map_prot_check(const struct cred *cred,
- const struct file *file,
- unsigned long prot, bool shared)
+static inline int file_map_prot_check(const struct file *file,
+ unsigned long prot, bool shared,
+ bool mounter_check)
{
- return __file_map_prot_check(cred, file, prot, shared, false);
+ return __file_map_prot_check(file, prot, shared, mounter_check, false);
}
static int selinux_mmap_addr(unsigned long addr)
@@ -4033,12 +4035,14 @@ static int selinux_mmap_addr(unsigned long addr)
return rc;
}
-static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
- unsigned long prot, bool shared)
+static int selinux_mmap_file_common(struct file *file, unsigned long prot,
+ bool shared, bool mounter_check)
{
if (file) {
int rc;
struct common_audit_data ad;
+ const struct cred *cred = mounter_check ?
+ file->f_cred : current_cred();
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
@@ -4047,15 +4051,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
return rc;
}
- return file_map_prot_check(cred, file, prot, shared);
+ return file_map_prot_check(file, prot, shared, mounter_check);
}
static int selinux_mmap_file(struct file *file,
unsigned long reqprot __always_unused,
unsigned long prot, unsigned long flags)
{
- return selinux_mmap_file_common(current_cred(), file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
+ return selinux_mmap_file_common(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED,
+ false);
}
/**
@@ -4087,8 +4092,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma,
if (vma->vm_flags & VM_EXEC)
prot |= PROT_EXEC;
- return selinux_mmap_file_common(backing_file->f_cred, backing_file,
- prot, vma->vm_flags & VM_SHARED);
+ return selinux_mmap_file_common(backing_file, prot,
+ vma->vm_flags & VM_SHARED,
+ true);
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
@@ -4149,11 +4155,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
}
}
- rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
+ rc = __file_map_prot_check(file, prot, shared, false, backing_file);
if (rc)
return rc;
if (backing_file) {
- rc = file_map_prot_check(file->f_cred, file, prot, shared);
+ rc = file_map_prot_check(file, prot, shared, true);
if (rc)
return rc;
}
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 4d73ecb30d79..41e019b9e24e 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -13,7 +13,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 7744ea6a0791..5abae26d2bba 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -17,7 +17,6 @@
#include <linux/firewire-constants.h>
#include <linux/jiffies.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 82b647d383c5..60d17a6fddd1 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -12,7 +12,6 @@
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index 7e42f5778a8a..c9c35c67db27 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -12,7 +12,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index c8d5879efe28..a8aadf754e72 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -13,7 +13,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index c66be0a89ccf..7333a76b50aa 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -12,7 +12,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 39ea9a6dde33..aeb83e1595b7 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -9,7 +9,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index d07ffcb27be6..9c42c9b48f21 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -12,7 +12,6 @@
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
index 3d92262763f6..40da2832ba66 100644
--- a/sound/hda/codecs/conexant.c
+++ b/sound/hda/codecs/conexant.c
@@ -162,9 +162,6 @@ static void cx_fixup_headset_recog(struct hda_codec *codec)
{
unsigned int mic_present;
- /* fix some headset type recognize fail issue, such as EDIFIER headset */
- /* set micbias output current comparator threshold from 66% to 55%. */
- snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
/* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register
* value adjustment trim from 2.2K ohms to 2.0K ohms.
*/
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index f7700713dc62..443bc92c5e4b 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3942,6 +3942,7 @@ enum {
ALC275_FIXUP_DELL_XPS,
ALC293_FIXUP_LENOVO_SPK_NOISE,
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ ALC233_FIXUP_WUJIE_SPEAKERS,
ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED,
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DISABLE_MIC_VREF,
@@ -4117,6 +4118,9 @@ enum {
ALC236_FIXUP_DELL_DUAL_CODECS,
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
ALC287_FIXUP_TAS2781_I2C,
+ ALC287_FIXUP_ASUS_ALLY_X,
+ ALC287_FIXUP_ASUS_ALLY_X_SPEAKER,
+ ALC287_FIXUP_ASUS_ALLY_X_I2C,
ALC295_FIXUP_DELL_TAS2781_I2C,
ALC245_FIXUP_TAS2781_SPI_2,
ALC287_FIXUP_TXNW2781_I2C,
@@ -4169,6 +4173,7 @@ enum {
ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO,
ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX,
ALC287_FIXUP_ACER_MICMUTE_LED,
+ ALC236_FIXUP_DELL_HP_POP_NOISE,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -4206,6 +4211,13 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec,
}
static const struct hda_fixup alc269_fixups[] = {
+ [ALC233_FIXUP_WUJIE_SPEAKERS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170150 }, /* internal speaker */
+ { }
+ },
+ },
[ALC269_FIXUP_GPIO2] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_gpio2,
@@ -6476,6 +6488,27 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
},
+ [ALC287_FIXUP_ASUS_ALLY_X] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC287_FIXUP_ASUS_ALLY_X_SPEAKER,
+ },
+ [ALC287_FIXUP_ASUS_ALLY_X_SPEAKER] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_ASUS_ALLY_X_I2C,
+ },
+ [ALC287_FIXUP_ASUS_ALLY_X_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_tias_i2c,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK,
+ },
[ALC245_FIXUP_TAS2781_SPI_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = tas2781_fixup_spi,
@@ -6755,6 +6788,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC2XX_FIXUP_HEADSET_MIC,
},
+ [ALC236_FIXUP_DELL_HP_POP_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_invalidate_dacs,
+ },
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@@ -6906,6 +6943,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0e6b, "Dell Pro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0e6d, "Dell Pro Micro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -6992,6 +7031,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B),
SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11),
SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360),
+ SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
@@ -7121,6 +7161,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a36, "HP Pavilion Plus 14-eh0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -7268,7 +7309,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX),
SND_PCI_QUIRK(0x103c, 0x8dc9, "HP Laptop 15-fc0xxx", ALC236_FIXUP_HP_DMIC),
SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS),
- SND_PCI_QUIRK(0x103c, 0x8dd7, "HP Laptop 15-fd0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8dd7, "HP Laptop 15-fd0xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED),
@@ -7460,7 +7501,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE),
- SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X),
SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2),
HDA_CODEC_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
@@ -7766,6 +7807,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1),
HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */
+ HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */
SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
@@ -7885,6 +7927,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -7915,6 +7958,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0xc011, 0x1d05, "MECHREVO WUJIE Series", ALC233_FIXUP_WUJIE_SPEAKERS),
SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
@@ -7922,6 +7966,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0xf111, 0x000f, "Framework Laptop 13 Pro PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0xf111, 0x010f, "Framework Laptop 13 PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0xf111, 0x0010, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST),
#if 0
/* Below is a quirk table taken from the old code.
diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig
index 2a2e8804bf9e..1cfd83e251e4 100644
--- a/sound/hda/codecs/side-codecs/Kconfig
+++ b/sound/hda/codecs/side-codecs/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config SND_HDA_CIRRUS_SCODEC
- tristate
+ tristate "Cirrus side-codec library" if KUNIT
config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST
tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
index a0ea08eb96a9..78c2cf387a00 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -512,20 +512,6 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
NULL, "bin");
return;
}
-
- /*
- * Check for system-specific bin files without wmfw before
- * falling back to generic firmware
- */
- if (amp_name)
- cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
- base_name, system_name, amp_name, "bin");
- if (!*coeff_firmware)
- cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
- base_name, system_name, NULL, "bin");
-
- if (*coeff_firmware)
- return;
}
ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
@@ -616,13 +602,15 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
&wmfw_firmware, &wmfw_filename,
&coeff_firmware, &coeff_filename);
- /*
- * If the BIOS didn't patch the firmware a bin file is mandatory to
- * enable the ASP·
- */
- if (!coeff_firmware && firmware_missing) {
- dev_err(cs35l56->base.dev, ".bin file required but not found\n");
- goto err_fw_release;
+ /* If the BIOS didn't patch the firmware a wmfw and bin file are mandatory */
+ if (firmware_missing) {
+ if (!wmfw_firmware) {
+ dev_err(cs35l56->base.dev, ".%s file required but not found\n", "wmfw");
+ goto err_fw_release;
+ } else if (!coeff_firmware) {
+ dev_err(cs35l56->base.dev, ".%s file required but not found\n", "bin");
+ goto err_fw_release;
+ }
}
mutex_lock(&cs35l56->base.irq_lock);
diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c
index e7b866fc52c1..d6eb17aa9e08 100644
--- a/sound/hda/core/regmap.c
+++ b/sound/hda/core/regmap.c
@@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
- verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8);
return snd_hdac_exec_verb(codec, verb, 0, val);
}
@@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
- verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+ verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) |
(val & 0xffff);
return snd_hdac_exec_verb(codec, verb, 0, NULL);
}
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 977e4f2a7a70..a04c301df4d6 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -15,13 +15,17 @@ config SND_SOC_AMD_ACP_COMMON
config SND_SOC_ACPI_AMD_MATCH
tristate
- select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA
select SND_SOC_ACPI if ACPI
config SND_SOC_ACPI_AMD_SDCA_QUIRKS
- tristate
- depends on ACPI
+ bool "AMD ACPI SDCA quirks"
+ depends on SND_SOC_ACPI_AMD_MATCH
depends on SND_SOC_SDCA
+ depends on SND_SOC_ACPI_AMD_MATCH = m || SND_SOC_SDCA = y
+ default y
+ help
+ Enable SDCA quirk support for AMD ACPI match tables.
+ This is compiled into the snd-soc-acpi-amd-match module.
if SND_SOC_AMD_ACP_COMMON
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 81d23aded348..ab5f9dc871a6 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -24,10 +24,10 @@ snd-acp-mach-y := acp-mach-common.o
snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
snd-acp-sof-mach-y := acp-sof-mach.o
snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
+snd-soc-acpi-amd-match-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += soc-acpi-amd-sdca-quirks.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
-snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
@@ -41,7 +41,6 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
-obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index 18f2918d4ada..ccd01152c87d 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -553,6 +553,46 @@ static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
{}
};
+static const struct snd_soc_acpi_adr_device tas2783_2_adr[] = {
+ {
+ /* left */
+ .adr = 0x00003c0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "tas2783-1",
+ },
+ {
+ /* right */
+ .adr = 0x00003d0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "tas2783-2",
+ },
+ {
+ /* left */
+ .adr = 0x0000390102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "tas2783-3",
+ },
+ {
+ /* right */
+ .adr = 0x00003a0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "tas2783-4",
+ },
+};
+
+static const struct snd_soc_acpi_link_adr acp70_tas2783_2[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(tas2783_2_adr),
+ .adr_d = tas2783_2_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_adr_device rt1320_0_single_adr[] = {
{
.adr = 0x000030025D132001ull,
@@ -660,6 +700,11 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = {
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
{
+ .link_mask = BIT(0),
+ .links = acp70_tas2783_2,
+ .drv_name = "amd_sdw",
+ },
+ {
.link_mask = BIT(0) | BIT(1),
.links = acp70_rt1320_l0_rt722_l1,
.drv_name = "amd_sdw",
@@ -775,5 +820,5 @@ MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
-MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
#endif
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
index 63bf9e3c0ae1..9248b6d09e76 100644
--- a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
@@ -35,8 +35,4 @@ bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
return false;
}
-EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS");
-MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks");
-MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 4ecda224157b..729f9aaba69e 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -248,7 +248,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
if (sdw_dma_irq_flag)
return IRQ_WAKE_THREAD;
- if (irq_flag | wake_irq_flag)
+ if (irq_flag || wake_irq_flag)
return IRQ_HANDLED;
else
return IRQ_NONE;
@@ -602,7 +602,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
return -ENODEV;
}
- ret = pci_request_regions(pci, "AMD ACP6.2 audio");
+ ret = pci_request_regions(pci, "AMD ACP6.3 audio");
if (ret < 0) {
dev_err(&pci->dev, "pci_request_regions failed\n");
goto disable_pci;
@@ -693,8 +693,37 @@ static int snd_acp_runtime_resume(struct device *dev)
return acp_hw_runtime_resume(dev);
}
+static void acp_disable_msi_on_resume(struct pci_dev *pdev)
+{
+ u16 control;
+
+ if (!pdev->msi_cap)
+ return;
+
+ pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control);
+ if (control & PCI_MSI_FLAGS_ENABLE) {
+ dev_warn(&pdev->dev,
+ "ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n",
+ control);
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control);
+ }
+}
+
static int snd_acp_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /*
+ * BIOS/firmware may re-enable MSI in PCI config space during
+ * system resume even though this driver only uses legacy INTx
+ * interrupts. If MSI is left enabled with stale address/data
+ * registers, the device will write interrupts to a bogus address
+ * causing IOMMU IO_PAGE_FAULT and interrupt delivery failure.
+ * Explicitly clear the MSI Enable bit before reinitializing
+ * the ACP hardware.
+ */
+ acp_disable_msi_on_resume(pdev);
return acp_hw_resume(dev);
}
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 934666295ee3..dbf45cabfffe 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
#define CS42L43_JACK_ABSENT 0x0
#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE)
#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
@@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
.mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
- .report = CS42L43_JACK_LINEIN,
+ .report = CS42L43_JACK_MICROPHONE,
},
[CS42L43_JACK_RAW_OPTICAL] = {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 4796fce084ff..d6353af07380 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -1843,6 +1843,15 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
}
}
+static void rt712_sdca_reset(struct rt712_sdca_priv *rt712)
+{
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG,
+ RT712_PARA_VERB_CTL, RT712_HIDDEN_REG_SW_RESET,
+ RT712_HIDDEN_REG_SW_RESET);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
@@ -1870,6 +1879,8 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
pm_runtime_get_noresume(&slave->dev);
+ rt712_sdca_reset(rt712);
+
rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val);
rt712->hw_id = (val & 0xf000) >> 12;
rt712->version_id = (val & 0x0f00) >> 8;
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index 2f7cfc2be970..e1d62f30418a 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
if (tas2562->sdz_gpio == NULL) {
tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down",
GPIOD_OUT_HIGH);
- if (IS_ERR(tas2562->sdz_gpio))
+ if (IS_ERR(tas2562->sdz_gpio)) {
if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- tas2562->sdz_gpio = NULL;
+ tas2562->sdz_gpio = NULL;
+ }
}
if (tas2562->model_id == TAS2110)
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index d43daf9b025d..59eb0328bbb5 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -855,6 +855,7 @@ static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = {
SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC),
SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC),
SND_PCI_QUIRK(0x17aa, 0x3821, "Lenovo 0x3821", SOC_SDW_SIDECAR_AMPS),
+ SND_PCI_QUIRK(0x17aa, 0x383c, "Lenovo 0x383c", SOC_SDW_SIDECAR_AMPS),
{}
};
diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c
index e0e00ec026dc..a9861c5d6637 100644
--- a/sound/soc/meson/aiu-fifo-spdif.c
+++ b/sound/soc/meson/aiu-fifo-spdif.c
@@ -24,6 +24,7 @@
#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)
#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)
#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)
+#define AIU_RST_SOFT_958_FAST BIT(2)
#define AIU_FIFO_SPDIF_BLOCK 8
@@ -68,11 +69,15 @@ static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_write(component, AIU_RST_SOFT,
+ AIU_RST_SOFT_958_FAST);
fifo_spdif_dcu_enable(component, true);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
+ snd_soc_component_write(component, AIU_RST_SOFT,
+ AIU_RST_SOFT_958_FAST);
fifo_spdif_dcu_enable(component, false);
break;
default:
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 7925aa3f63ba..98b15a527e37 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -164,6 +164,7 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
}
static const struct of_device_id snd_sc8280xp_dt_match[] = {
+ {.compatible = "qcom,eliza-sndcard", "eliza"},
{.compatible = "qcom,kaanapali-sndcard", "kaanapali"},
{.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"},
{.compatible = "qcom,qcs615-sndcard", "qcs615"},
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index b20aae0caf60..a16e59248480 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -134,14 +134,22 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
struct device *dev = &urb->dev->dev;
struct snd_usb_caiaqdev *cdev = urb->context;
unsigned char *buf = urb->transfer_buffer;
+ unsigned int payload_len;
+ unsigned int copy_len;
if (urb->status || !cdev) {
dev_warn(dev, "received EP1 urb->status = %i\n", urb->status);
return;
}
+ if (urb->actual_length < 1)
+ return;
+
+ payload_len = urb->actual_length - 1;
switch(buf[0]) {
case EP1_CMD_GET_DEVICE_INFO:
+ if (payload_len < sizeof(struct caiaq_device_spec))
+ break;
memcpy(&cdev->spec, buf+1, sizeof(struct caiaq_device_spec));
cdev->spec.fw_version = le16_to_cpu(cdev->spec.fw_version);
dev_dbg(dev, "device spec (firmware %d): audio: %d in, %d out, "
@@ -157,18 +165,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_AUDIO_PARAMS:
+ if (payload_len < 1)
+ break;
cdev->audio_parm_answer = buf[1];
wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_MIDI_READ:
+ if (urb->actual_length < 3 || urb->actual_length - 3 < buf[2])
+ break;
snd_usb_caiaq_midi_handle_input(cdev, buf[1], buf + 3, buf[2]);
break;
case EP1_CMD_READ_IO:
if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
- if (urb->actual_length > sizeof(cdev->control_state))
- urb->actual_length = sizeof(cdev->control_state);
- memcpy(cdev->control_state, buf + 1, urb->actual_length);
+ copy_len = min_t(unsigned int, payload_len, sizeof(cdev->control_state));
+ memcpy(cdev->control_state, buf + 1, copy_len);
wake_up(&cdev->ep1_wait_queue);
break;
}
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index eabbf41fdfb2..8d924330c54c 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -203,6 +203,8 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev,
switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
+ if (len < 6)
+ return;
snd_caiaq_input_report_abs(cdev, ABS_X, buf, 2);
snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 0);
snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 1);
@@ -210,11 +212,15 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev,
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
+ if (len < 6)
+ return;
snd_caiaq_input_report_abs(cdev, ABS_X, buf, 0);
snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 1);
snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 2);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+ if (len < 16)
+ return;
snd_caiaq_input_report_abs(cdev, ABS_HAT0X, buf, 4);
snd_caiaq_input_report_abs(cdev, ABS_HAT0Y, buf, 2);
snd_caiaq_input_report_abs(cdev, ABS_HAT1X, buf, 6);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index b4c855c25eef..703c118f9d4e 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1473,8 +1473,8 @@ sticky:
if (!cval->cmask) {
snd_usb_set_cur_mix_value(cval, 0, 0, cval->max);
} else {
+ idx = 0;
for (i = 0; i < MAX_CHANNELS; i++) {
- idx = 0;
if (cval->cmask & BIT(i)) {
snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max);
idx++;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 1cb588691e16..41149561aa06 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2210,6 +2210,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
+ DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN),
DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */
@@ -2461,6 +2463,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
QUIRK_FLAG_IGNORE_CTL_ERROR),
+ DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */
+ 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */
@@ -2483,6 +2487,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_MIXER_GET_CUR_BROKEN),
DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */
QUIRK_FLAG_CTL_MSG_DELAY),
+ DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x2fc6, 0xf0b7, /* iBasso DC07 Pro */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
diff --git a/tools/sched_ext/include/scx/cid.bpf.h b/tools/sched_ext/include/scx/cid.bpf.h
index db247e42fb45..6b0b4e41b288 100644
--- a/tools/sched_ext/include/scx/cid.bpf.h
+++ b/tools/sched_ext/include/scx/cid.bpf.h
@@ -391,7 +391,9 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a,
if (a->base != b->base || a->nr_cids != b->nr_cids)
return false;
- nr_words = CMASK_NR_WORDS(a->nr_cids);
+ if (a->nr_cids == 0)
+ return true;
+ nr_words = (a->base + a->nr_cids - 1) / 64 - a->base / 64 + 1;
bpf_for(i, 0, CMASK_MAX_WORDS) {
if (i >= nr_words)
@@ -402,36 +404,6 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a,
return true;
}
-/*
- * True iff every bit set in @a is also set in @b over the intersection of
- * their ranges. Bits of @a outside @b's range fail the test.
- */
-static __always_inline bool cmask_subset(const struct scx_cmask __arena *a,
- const struct scx_cmask __arena *b)
-{
- u32 a_end = a->base + a->nr_cids;
- u32 b_end = b->base + b->nr_cids;
- u32 a_wbase = a->base / 64;
- u32 b_wbase = b->base / 64;
- u32 nr_words, i;
-
- /* any bit of @a outside @b's range is a subset violation */
- if (a->base < b->base || a_end > b_end)
- return false;
-
- nr_words = CMASK_NR_WORDS(a->nr_cids);
- bpf_for(i, 0, CMASK_MAX_WORDS) {
- u32 wi_b;
-
- if (i >= nr_words)
- break;
- wi_b = a_wbase + i - b_wbase;
- if (a->bits[i] & ~b->bits[wi_b])
- return false;
- }
- return true;
-}
-
/**
* cmask_next_set - find the first set bit at or after @cid
* @m: cmask to search
@@ -489,15 +461,65 @@ static __always_inline u32 cmask_first_set(const struct scx_cmask __arena *m)
(cid) = cmask_next_set((m), (cid) + 1))
/*
+ * True iff every bit set in @a is also set in @b. Matches the kernel-side
+ * scx_cmask_subset(): ranges don't need to nest, and set bits of @a outside
+ * @b's range fail the test.
+ */
+static __always_inline bool cmask_subset(const struct scx_cmask __arena *a,
+ const struct scx_cmask __arena *b)
+{
+ u32 a_end = a->base + a->nr_cids;
+ u32 b_end = b->base + b->nr_cids;
+ u32 a_wbase = a->base / 64;
+ u32 b_wbase = b->base / 64;
+ u32 lo = a->base > b->base ? a->base : b->base;
+ u32 hi = a_end < b_end ? a_end : b_end;
+ u32 lo_word, hi_word, i;
+
+ /* set bits of @a outside @b's range can't be in @b */
+ if (a->base < b->base &&
+ cmask_next_set(a, a->base) < (b->base < a_end ? b->base : a_end))
+ return false;
+ if (a_end > b_end &&
+ cmask_next_set(a, a->base > b_end ? a->base : b_end) < a_end)
+ return false;
+
+ if (lo >= hi)
+ return true;
+
+ /*
+ * Walk the words the range intersection spans. Plain word tests
+ * suffice: the scans above guarantee @a has no set bit outside @b's
+ * range and padding bits are kept clear by all cmask helpers.
+ */
+ lo_word = lo / 64;
+ hi_word = (hi - 1) / 64;
+
+ bpf_for(i, 0, CMASK_MAX_WORDS) {
+ u32 w = lo_word + i;
+
+ if (w > hi_word)
+ break;
+ if (a->bits[w - a_wbase] & ~b->bits[w - b_wbase])
+ return false;
+ }
+ return true;
+}
+
+/*
* Population count over [base, base + nr_cids). Padding bits in the head/tail
* words are guaranteed zero by the mutating helpers, so a flat popcount over
- * all words is correct.
+ * the words the range spans is correct.
*/
static __always_inline u32 cmask_weight(const struct scx_cmask __arena *m)
{
- u32 nr_words = CMASK_NR_WORDS(m->nr_cids), i;
+ u32 nr_words, i;
u32 count = 0;
+ if (!m->nr_cids)
+ return 0;
+ nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1;
+
bpf_for(i, 0, CMASK_MAX_WORDS) {
if (i >= nr_words)
break;
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index d4f845c32804..a329f901c5ed 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -84,6 +84,7 @@ static void find_controls(void)
if (err < 0) {
ksft_print_msg("Failed to get hctl for card %d: %s\n",
card, snd_strerror(err));
+ free(card_data);
goto next_card;
}
@@ -339,9 +340,9 @@ static bool ctl_value_index_valid(struct ctl_data *ctl,
}
if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
- ksft_print_msg("%s.%d value %lld more than maximum %ld\n",
+ ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
ctl->name, index, int64_val,
- snd_ctl_elem_info_get_max(ctl->info));
+ snd_ctl_elem_info_get_max64(ctl->info));
return false;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..61cc5aaba025
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "kfunc_implicit_args_tracing.skel.h"
+
+void test_kfunc_implicit_args_tracing(void)
+{
+ struct kfunc_implicit_args_tracing *skel;
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+ int err, fd;
+
+ skel = kfunc_implicit_args_tracing__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ err = kfunc_implicit_args_tracing__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto cleanup;
+
+ fd = bpf_program__fd(skel->progs.trigger_implicit_arg);
+ err = bpf_prog_test_run_opts(fd, &topts);
+ if (!ASSERT_OK(err, "test_run"))
+ goto cleanup;
+
+ ASSERT_EQ(topts.retval, 5, "kfunc_retval");
+ ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt");
+ ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg");
+ ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result");
+ ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt");
+ ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg");
+ ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result");
+
+cleanup:
+ kfunc_implicit_args_tracing__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c
new file mode 100644
index 000000000000..b8538fc4fc3f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "test_kmods/bpf_testmod.h"
+#include "bpf_util.h"
+
+static void check_attach_reject(const struct bpf_insn *program, size_t prog_len)
+{
+ LIBBPF_OPTS(bpf_prog_load_opts, opts);
+ char error[4096];
+ int bpf_fd, tp_fd;
+
+ opts.log_level = 2;
+ opts.log_buf = error;
+ opts.log_size = sizeof(error);
+
+ bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
+ program, prog_len, &opts);
+ if (!ASSERT_GE(bpf_fd, 0, "prog_load"))
+ return;
+
+ tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd);
+ ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open");
+ if (tp_fd >= 0)
+ close(tp_fd);
+
+ close(bpf_fd);
+}
+
+void test_raw_tp_writable_reject_bad_access(void)
+{
+ const struct bpf_insn program[] = {
+ /* r6 is our tp buffer */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
+ /* one byte beyond the end of the writable context */
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
+ sizeof(struct bpf_testmod_test_writable_ctx)),
+ BPF_EXIT_INSN(),
+ };
+
+ const struct bpf_insn negative_var_off_program[] = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
+ /* make var_off negative, but keep the effective access offset non-negative */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8),
+ /* one byte beyond the end of the writable context */
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
+ sizeof(struct bpf_testmod_test_writable_ctx) + 8),
+ BPF_EXIT_INSN(),
+ };
+
+ if (test__start_subtest("past_end"))
+ check_attach_reject(program, ARRAY_SIZE(program));
+
+ if (test__start_subtest("negative_var_off_past_end"))
+ check_attach_reject(negative_var_off_program,
+ ARRAY_SIZE(negative_var_off_program));
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
deleted file mode 100644
index 216b0dfac0fe..000000000000
--- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <test_progs.h>
-#include <linux/nbd.h>
-#include "bpf_util.h"
-
-void test_raw_tp_writable_reject_nbd_invalid(void)
-{
- __u32 duration = 0;
- char error[4096];
- int bpf_fd = -1, tp_fd = -1;
-
- const struct bpf_insn program[] = {
- /* r6 is our tp buffer */
- BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
- /* one byte beyond the end of the nbd_request struct */
- BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
- sizeof(struct nbd_request)),
- BPF_EXIT_INSN(),
- };
-
- LIBBPF_OPTS(bpf_prog_load_opts, opts,
- .log_level = 2,
- .log_buf = error,
- .log_size = sizeof(error),
- );
-
- bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
- program, ARRAY_SIZE(program),
- &opts);
- if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load",
- "failed: %d errno %d\n", bpf_fd, errno))
- return;
-
- tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
- if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open",
- "erroneously succeeded\n"))
- goto out_bpffd;
-
- close(tp_fd);
-out_bpffd:
- close(bpf_fd);
-}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index cb3229711f93..e5fc038d747b 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -853,7 +853,7 @@ static void test_sockmap_many_socket(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -922,7 +922,7 @@ static void test_sockmap_many_maps(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -993,7 +993,7 @@ static void test_sockmap_same_sock(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -1373,6 +1373,43 @@ end:
test_sockmap_pass_prog__destroy(skel);
}
+/* A socket in a sockmap without a verdict program keeps its ingress data
+ * in sk_receive_queue: FIONREAD must account for it.
+ */
+static void test_sockmap_no_verdict_fionread(void)
+{
+ int err, map, zero = 0, sent, avail;
+ int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
+ struct test_sockmap_pass_prog *skel;
+ char buf[256] = "0123456789";
+
+ skel = test_sockmap_pass_prog__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+ map = bpf_map__fd(skel->maps.sock_map_rx);
+
+ err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
+ if (!ASSERT_OK(err, "create_socket_pairs()"))
+ goto out;
+
+ err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
+ if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
+ goto out_close;
+
+ sent = xsend(p1, &buf, sizeof(buf), 0);
+ ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
+ avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
+ ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
+
+out_close:
+ close(c0);
+ close(p0);
+ close(c1);
+ close(p1);
+out:
+ test_sockmap_pass_prog__destroy(skel);
+}
+
void test_sockmap_basic(void)
{
if (test__start_subtest("sockmap create_update_free"))
@@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
test_sockmap_skb_verdict_shutdown();
if (test__start_subtest("sockmap skb_verdict fionread"))
test_sockmap_skb_verdict_fionread(true);
+ if (test__start_subtest("sockmap no_verdict fionread"))
+ test_sockmap_no_verdict_fionread();
if (test__start_subtest("sockmap skb_verdict fionread on drop"))
test_sockmap_skb_verdict_fionread(false);
if (test__start_subtest("sockmap skb_verdict change tail"))
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
index cc0c68bab907..1c96a3cf4b97 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
int family, int sotype, int mapfd)
{
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
s = xsocket(family, sotype, 0);
if (s == -1)
@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (sotype == SOCK_STREAM) {
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
- } else if (err)
- FAIL_ERRNO("map_update: expected success");
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
xclose(s);
}
@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
struct sockaddr_storage addr;
socklen_t len = 0;
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
init_addr_loopback(family, &addr, &len);
@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
+ if (sotype == SOCK_STREAM) {
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
+ } else {
+ ASSERT_OK(err, "map_update");
+ }
close:
xclose(s);
}
@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
/* insert */
TEST(test_insert_invalid),
TEST(test_insert_opened),
- TEST(test_insert_bound, SOCK_STREAM),
+ TEST(test_insert_bound),
TEST(test_insert),
/* delete */
TEST(test_delete_after_insert),
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 8a3d69e2453c..be97f6887f0e 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -78,6 +78,7 @@
#include "verifier_precision.skel.h"
#include "verifier_prevent_map_lookup.skel.h"
#include "verifier_private_stack.skel.h"
+#include "verifier_ptr_to_buf.skel.h"
#include "verifier_raw_stack.skel.h"
#include "verifier_raw_tp_writable.skel.h"
#include "verifier_reg_equal.skel.h"
@@ -230,6 +231,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); }
void test_verifier_precision(void) { RUN(verifier_precision); }
void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); }
void test_verifier_private_stack(void) { RUN(verifier_private_stack); }
+void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); }
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); }
diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..995f8b8b5b9e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+
+extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
+
+char _license[] SEC("license") = "GPL";
+
+/* Shared arg checks; reports arg count and aux, returns 1 on success. */
+static __always_inline __u64
+check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg)
+{
+ __u64 a = 0, aux = 0, z = 0;
+ __u64 result;
+ __s64 err;
+
+ *arg_cnt = bpf_get_func_arg_cnt(ctx);
+ result = *arg_cnt == 2;
+
+ err = bpf_get_func_arg(ctx, 0, &a);
+ result &= err == 0 && (int)a == 5;
+
+ err = bpf_get_func_arg(ctx, 1, &aux);
+ *aux_arg = aux;
+ result &= err == 0 && aux != 0;
+
+ err = bpf_get_func_arg(ctx, 2, &z);
+ result &= err == -EINVAL;
+
+ return result;
+}
+
+__u64 fentry_result;
+__u64 fentry_arg_cnt;
+__u64 fentry_aux_arg;
+
+SEC("fentry/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fentry)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fentry_result &= err == -EOPNOTSUPP;
+
+ return 0;
+}
+
+__u64 fexit_result;
+__u64 fexit_arg_cnt;
+__u64 fexit_aux_arg;
+
+SEC("fexit/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fexit)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fexit_result &= err == 0 && ret == 5;
+
+ return 0;
+}
+
+SEC("syscall")
+int trigger_implicit_arg(void *ctx)
+{
+ return bpf_kfunc_implicit_arg(5);
+}
diff --git a/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
new file mode 100644
index 000000000000..12cf24db46a0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+SEC("iter/bpf_map_elem")
+__description("PTR_TO_BUF: reject negative const offset")
+__failure
+__msg("invalid negative rdwr buffer offset")
+__naked void ptr_to_buf_reject_negative_const_offset(void)
+{
+ asm volatile ("r0 = 0; \
+ r2 = *(u64 *)(r1 + %[value_off]); \
+ if r2 == 0 goto l0_%=; \
+ r2 += -8; \
+ r0 = *(u64 *)(r2 + 0); \
+l0_%=: \
+ exit; \
+ "
+ :
+ : __imm_const(value_off,
+ offsetof(struct bpf_iter__bpf_map_elem, value))
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
index 14a0172e2141..4055a6443bc2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
+++ b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
@@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\
: __clobber_all);
}
+SEC("raw_tracepoint.w")
+__description("raw_tracepoint_writable: reject negative const offset")
+__failure
+__msg("invalid negative tracepoint buffer offset")
+__naked void tracepoint_writable_reject_negative_const_offset(void)
+{
+ asm volatile (" \
+ r6 = *(u64 *)(r1 + 0); \
+ r6 += -8; \
+ r0 = *(u64 *)(r6 + 0); \
+ exit; \
+" :
+ :
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index c32da7bd8be2..6a2641ee7897 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -759,16 +759,15 @@ static void test_sockmap(unsigned int tasks, void *data)
goto out_sockmap;
}
- /* Test update with unsupported UDP socket */
+ /* Test update with unsupported unbound UDP socket */
udp = socket(AF_INET, SOCK_DGRAM, 0);
- i = 0;
- err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY);
- if (err) {
- printf("Failed socket update SOCK_DGRAM '%i:%i'\n",
- i, udp);
+ CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno);
+ err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY);
+ close(udp);
+ if (!err) {
+ printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp);
goto out_sockmap;
}
- close(udp);
/* Test update without programs */
for (i = 0; i < 6; i++) {
diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
index cb59cf436dd0..d9111f2102bc 100755
--- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
+++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
@@ -44,7 +44,8 @@ function trigger_reactivation() {
# Restore MACs
ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" \
address "${SAVED_DSTMAC}"
- if [ "${BINDMODE}" == "mac" ]; then
+ if [ "${BINDMODE}" == "mac" ] &&
+ [ "$(mac_get "${SRCIF}")" != "${SAVED_SRCMAC}" ]; then
ip link set dev "${SRCIF}" down
ip link set dev "${SRCIF}" address "${SAVED_SRCMAC}"
# Rename device in order to trigger target resume, as initial
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
index 2915206777b6..89660a9adf44 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
@@ -16,23 +16,32 @@ echo > dynamic_events
FUNC1='foo_bar*'
FUNC2='vfs_read'
+:;: "Save enabled functions count" ;:
+ecount=`cat enabled_functions | wc -l`
+
+count_enabled_functions() {
+ count=`cat enabled_functions | wc -l`
+ count=$(($count-$ecount))
+ echo $count
+}
+
:;: "Add an event on the test module" ;:
echo "f:test1 $FUNC1" >> dynamic_events
echo 1 > events/fprobes/test1/enable
:;: "Ensure it is enabled" ;:
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -ne 0
:;: "Check the enabled_functions is cleared on unloading" ;:
rmmod trace-events-sample
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
:;: "Check it is kept clean" ;:
modprobe trace-events-sample
echo 1 > events/fprobes/test1/enable || echo "OK"
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
:;: "Add another event not on the test module" ;:
@@ -40,19 +49,19 @@ echo "f:test2 $FUNC2" >> dynamic_events
echo 1 > events/fprobes/test2/enable
:;: "Ensure it is enabled" ;:
-ofuncs=`cat enabled_functions | wc -l`
+ofuncs=`count_enabled_functions`
test $ofuncs -ne 0
:;: "Disable and remove the first event"
echo 0 > events/fprobes/test1/enable
echo "-:fprobes/test1" >> dynamic_events
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $ofuncs -eq $funcs
:;: "Disable and remove other events" ;:
echo 0 > events/fprobes/enable
echo > dynamic_events
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
rmmod trace-events-sample
@@ -63,12 +72,12 @@ echo "f:test1 $FUNC1" >> dynamic_events
echo 1 > events/fprobes/test1/enable
echo "f:test2 $FUNC2" >> dynamic_events
echo 1 > events/fprobes/test2/enable
-ofuncs=`cat enabled_functions | wc -l`
+ofuncs=`count_enabled_functions`
test $ofuncs -ne 0
:;: "Unload module (ftrace entry should be removed)" ;:
rmmod trace-events-sample
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -ne 0
test $ofuncs -ne $funcs
@@ -77,7 +86,7 @@ echo 0 > events/fprobes/test2/enable
echo "-:fprobes/test2" >> dynamic_events
:;: "Ensure ftrace is disabled." ;:
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
echo 0 > events/fprobes/enable
diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
index 8d275e3238d9..04eb8546fc07 100644
--- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
+++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
@@ -1,7 +1,7 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: event trigger - test poll wait on histogram
-# requires: set_event events/sched/sched_process_free/trigger events/sched/sched_process_free/hist
+# requires: set_event events/sched/sched_process_exit/trigger events/sched/sched_process_exit/hist
# flags: instance
POLL=${FTRACETEST_ROOT}/poll
@@ -11,7 +11,7 @@ if [ ! -x ${POLL} ]; then
exit_unresolved
fi
-EVENT=events/sched/sched_process_free/
+EVENT=events/sched/sched_process_exit/
# Check poll ops is supported. Before implementing poll on hist file, it
# returns soon with POLLIN | POLLOUT, but not POLLPRI.
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..6fc34e9bf8e1 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -174,6 +174,7 @@ TEST_GEN_PROGS_arm64 += arm64/hello_el2
TEST_GEN_PROGS_arm64 += arm64/host_sve
TEST_GEN_PROGS_arm64 += arm64/hypercalls
TEST_GEN_PROGS_arm64 += arm64/external_aborts
+TEST_GEN_PROGS_arm64 += arm64/mmio_sign_ext
TEST_GEN_PROGS_arm64 += arm64/page_fault_test
TEST_GEN_PROGS_arm64 += arm64/psci_test
TEST_GEN_PROGS_arm64 += arm64/sea_to_user
diff --git a/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c
new file mode 100644
index 000000000000..25196f1e6322
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mmio_sign_ext - Test sign-extending MMIO load emulation (LDRSB/LDRSH/LDRSW)
+ *
+ * Copyright (c) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+
+#include <asm/ptrace.h>
+
+#include "processor.h"
+#include "test_util.h"
+
+#define MMIO_ADDR 0x8000000ULL
+
+/* AP[1]: allow unprivileged (EL0) access to a mapping. */
+#define PTE_USER BIT(6)
+
+/* SPSR for ERET to EL0t with DAIF masked. */
+#define SPSR_EL0 (PSR_MODE_EL0t | PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
+
+struct mmio_test {
+ const char *name;
+ uint64_t data; /* access-width value, host byte order */
+ uint8_t len;
+ uint64_t expected; /* sign-extended result; same for LE and BE */
+};
+
+/* Paired 1:1, in order, with the loads in guest_loads_le() and el0_be_loads. */
+static const struct mmio_test tests[] = {
+ /* LDRSB Xt: byte sign-extended to 64 bits */
+ { "LDRSB Xt 0xFF", 0xFF, 1, 0xFFFFFFFFFFFFFFFFULL },
+ { "LDRSB Xt 0x7F", 0x7F, 1, 0x7FULL },
+
+ /* LDRSB Wt: byte sign-extended to 32 bits, upper 32 bits zeroed */
+ { "LDRSB Wt 0xFF", 0xFF, 1, 0xFFFFFFFFULL },
+ { "LDRSB Wt 0x7F", 0x7F, 1, 0x7FULL },
+
+ /* LDRSH Xt: halfword sign-extended to 64 bits */
+ { "LDRSH Xt 0x8001", 0x8001, 2, 0xFFFFFFFFFFFF8001ULL },
+ { "LDRSH Xt 0x7FFF", 0x7FFF, 2, 0x7FFFULL },
+
+ /* LDRSH Wt: halfword sign-extended to 32 bits, upper 32 bits zeroed */
+ { "LDRSH Wt 0x8001", 0x8001, 2, 0xFFFF8001ULL },
+ { "LDRSH Wt 0x7FFF", 0x7FFF, 2, 0x7FFFULL },
+
+ /* LDRSW Xt: word sign-extended to 64 bits (no Wt form) */
+ { "LDRSW Xt 0x80000001", 0x80000001, 4, 0xFFFFFFFF80000001ULL },
+ { "LDRSW Xt 0x7FFFFFFF", 0x7FFFFFFF, 4, 0x7FFFFFFFULL },
+};
+
+/* Issue one sign-extending load from MMIO and report the result. */
+#define GUEST_LDRS(load) do { \
+ uint64_t val; \
+ \
+ asm volatile(load : "=r"(val) : "r"(MMIO_ADDR) : "memory"); \
+ GUEST_SYNC(val); \
+} while (0)
+
+/* Little-endian pass: loads issued at EL1. */
+static void guest_loads_le(void)
+{
+ GUEST_LDRS("ldrsb %0, [%1]");
+ GUEST_LDRS("ldrsb %0, [%1]");
+ GUEST_LDRS("ldrsb %w0, [%1]");
+ GUEST_LDRS("ldrsb %w0, [%1]");
+ GUEST_LDRS("ldrsh %0, [%1]");
+ GUEST_LDRS("ldrsh %0, [%1]");
+ GUEST_LDRS("ldrsh %w0, [%1]");
+ GUEST_LDRS("ldrsh %w0, [%1]");
+ GUEST_LDRS("ldrsw %0, [%1]");
+ GUEST_LDRS("ldrsw %0, [%1]");
+}
+
+/*
+ * Run the big-endian loads at EL0, where SCTLR_EL1.E0E flips only the data
+ * endianness; at EL1, SCTLR_EL1.EE would also flip the page-table walk and
+ * fault on the little-endian tables. x0 holds MMIO_ADDR; results return in
+ * x19..x28 (tests[] order) via a single SVC.
+ */
+extern char el0_be_loads[];
+asm(
+" .pushsection .text, \"ax\"\n"
+" .global el0_be_loads\n"
+"el0_be_loads:\n"
+" ldrsb x19, [x0]\n"
+" ldrsb x20, [x0]\n"
+" ldrsb w21, [x0]\n"
+" ldrsb w22, [x0]\n"
+" ldrsh x23, [x0]\n"
+" ldrsh x24, [x0]\n"
+" ldrsh w25, [x0]\n"
+" ldrsh w26, [x0]\n"
+" ldrsw x27, [x0]\n"
+" ldrsw x28, [x0]\n"
+" svc #0\n"
+" .popsection\n"
+);
+
+/* EL1 handler for the EL0 SVC: report the results, then finish. */
+static void el0_svc_handler(struct ex_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ GUEST_SYNC(regs->regs[19 + i]);
+
+ GUEST_DONE();
+}
+
+static bool guest_mixed_endian_el0(void)
+{
+ uint64_t mmfr0 = read_sysreg(id_aa64mmfr0_el1);
+
+ return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) ||
+ SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0);
+}
+
+static void guest_code(void)
+{
+ guest_loads_le();
+
+ if (guest_mixed_endian_el0()) {
+ write_sysreg(read_sysreg(sctlr_el1) | SCTLR_EL1_E0E, sctlr_el1);
+ isb();
+
+ asm volatile(
+ " msr elr_el1, %[pc]\n"
+ " msr spsr_el1, %[spsr]\n"
+ " mov x0, %[mmio]\n"
+ " isb\n"
+ " eret\n"
+ :
+ : [pc] "r"(el0_be_loads),
+ [spsr] "r"((uint64_t)SPSR_EL0),
+ [mmio] "r"(MMIO_ADDR)
+ : "x0", "memory");
+ __builtin_unreachable(); /* el0_svc_handler ends the test */
+ }
+
+ GUEST_DONE();
+}
+
+static void handle_mmio(struct kvm_run *run, const struct mmio_test *t, bool be)
+{
+ int i;
+
+ TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR);
+ TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read for %s", t->name);
+ TEST_ASSERT_EQ(run->mmio.len, t->len);
+
+ memset(run->mmio.data, 0, sizeof(run->mmio.data));
+ if (be) {
+ /* The guest reads the device bytes most-significant first. */
+ for (i = 0; i < t->len; i++)
+ run->mmio.data[i] = t->data >> (8 * (t->len - 1 - i));
+ } else {
+ /* Works because arm64 KVM hosts are always little-endian. */
+ memcpy(run->mmio.data, &t->data, t->len);
+ }
+}
+
+static void expect_sync(struct kvm_vcpu *vcpu, struct ucall *uc,
+ const struct mmio_test *t)
+{
+ switch (get_ucall(vcpu, uc)) {
+ case UCALL_SYNC:
+ TEST_ASSERT(uc->args[1] == t->expected,
+ "%s: got %#lx, want %#lx", t->name,
+ (unsigned long)uc->args[1], (unsigned long)t->expected);
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(*uc);
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall for %s", t->name);
+ }
+}
+
+/* OR PTE_USER into the leaf descriptors covering [gva, gva + len). */
+static void make_el0_accessible(struct kvm_vm *vm, uint64_t gva, uint64_t len)
+{
+ uint64_t addr;
+
+ for (addr = gva & ~((uint64_t)vm->page_size - 1); addr < gva + len;
+ addr += vm->page_size)
+ *virt_get_pte_hva(vm, addr) |= PTE_USER;
+}
+
+static bool vcpu_mixed_endian_el0(struct kvm_vcpu *vcpu)
+{
+ uint64_t mmfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1));
+
+ return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) ||
+ SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0);
+}
+
+int main(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ unsigned int i;
+ bool be;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1);
+
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+ vm_install_sync_handler(vm, VECTOR_SYNC_LOWER_64, ESR_ELx_EC_SVC64,
+ el0_svc_handler);
+
+ be = vcpu_mixed_endian_el0(vcpu);
+ if (be)
+ make_el0_accessible(vm, MMIO_ADDR, vm->page_size);
+
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(tests) * (be ? 2 : 1));
+
+ /* Little-endian pass: one load and one result per iteration. */
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ const struct mmio_test *t = &tests[i];
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ handle_mmio(vcpu->run, t, false);
+
+ vcpu_run(vcpu);
+ expect_sync(vcpu, &uc, t);
+
+ ksft_test_result_pass("%s\n", t->name);
+ }
+
+ if (be) {
+ /* The EL0 stub issues all the loads, then reports the results. */
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ handle_mmio(vcpu->run, &tests[i], true);
+ }
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ vcpu_run(vcpu);
+ expect_sync(vcpu, &uc, &tests[i]);
+ ksft_test_result_pass("BE %s\n", tests[i].name);
+ }
+ }
+
+ vcpu_run(vcpu);
+ TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE, "Expected UCALL_DONE");
+
+ kvm_vm_free(vm);
+
+ ksft_finished();
+}
diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
index 42bc023d5193..d59abb198d86 100644
--- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
+++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
@@ -313,6 +313,49 @@ out:
kvm_vm_free(vm_no_vcpu);
}
+static void test_sev_snp_migrate_reject(void)
+{
+ struct kvm_vm *src_vm, *dst_vm;
+ int ret;
+
+ src_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ snp_vm_init(src_vm);
+ __vm_vcpu_add(src_vm, 0);
+ vm_sev_launch(src_vm, snp_default_policy(), NULL);
+
+ dst_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ __vm_vcpu_add(dst_vm, 0);
+
+ ret = __sev_migrate_from(dst_vm, src_vm);
+ TEST_ASSERT(ret == -1 && errno == EINVAL,
+ "SNP VM migration should be rejected. ret: %d, errno: %d",
+ ret, errno);
+
+ kvm_vm_free(src_vm);
+ kvm_vm_free(dst_vm);
+}
+
+static void test_sev_snp_mirror_reject(void)
+{
+ struct kvm_vm *src_vm, *dst_vm;
+ int ret;
+
+ src_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ snp_vm_init(src_vm);
+ __vm_vcpu_add(src_vm, 0);
+ vm_sev_launch(src_vm, snp_default_policy(), NULL);
+
+ dst_vm = aux_vm_create(false);
+
+ ret = __sev_mirror_create(dst_vm, src_vm);
+ TEST_ASSERT(ret == -1 && errno == EINVAL,
+ "SNP VM mirroring should be rejected. ret: %d, errno: %d",
+ ret, errno);
+
+ kvm_vm_free(src_vm);
+ kvm_vm_free(dst_vm);
+}
+
static void test_sev_move_copy(void)
{
struct kvm_vm *dst_vm, *dst2_vm, *dst3_vm, *sev_vm, *mirror_vm,
@@ -384,12 +427,16 @@ int main(int argc, char *argv[])
test_sev_migrate_parameters();
if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM))
test_sev_move_copy();
+ if (kvm_cpu_has(X86_FEATURE_SEV_SNP))
+ test_sev_snp_migrate_reject();
}
if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) {
test_sev_mirror(/* es= */ false);
if (have_sev_es)
test_sev_mirror(/* es= */ true);
test_sev_mirror_parameters();
+ if (kvm_cpu_has(X86_FEATURE_SEV_SNP))
+ test_sev_snp_mirror_reject();
}
return 0;
}
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 2ed1f76b7a8b..be2eb88092fb 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -1281,6 +1281,103 @@ TEST_F(protocol, connect_unspec)
EXPECT_EQ(0, close(bind_fd));
}
+TEST_F(protocol, tcp_fastopen)
+{
+ const bool restricted = variant->sandbox == TCP_SANDBOX &&
+ variant->prot.type == SOCK_STREAM &&
+ (variant->prot.protocol == IPPROTO_TCP ||
+ variant->prot.protocol == IPPROTO_IP) &&
+ (variant->prot.domain == AF_INET ||
+ variant->prot.domain == AF_INET6);
+ const struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ int bind_fd, client_fd, status;
+ char buf;
+ pid_t child;
+
+ bind_fd = socket_variant(&self->srv0);
+ ASSERT_LE(0, bind_fd);
+ EXPECT_EQ(0, bind_variant(bind_fd, &self->srv0));
+ if (self->srv0.protocol.type == SOCK_STREAM)
+ EXPECT_EQ(0, listen(bind_fd, backlog));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ int connect_fd, ret;
+
+ /* Closes listening socket for the child. */
+ EXPECT_EQ(0, close(bind_fd));
+
+ connect_fd = socket_variant(&self->srv0);
+ ASSERT_LE(0, connect_fd);
+
+ if (variant->sandbox == TCP_SANDBOX) {
+ const int ruleset_fd = landlock_create_ruleset(
+ &ruleset_attr, sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ EXPECT_EQ(0, close(ruleset_fd));
+ }
+
+ /* Fast Open with no address. */
+ ret = sendto_variant(connect_fd, NULL, NULL, 0, MSG_FASTOPEN);
+ if (self->srv0.protocol.domain == AF_UNIX) {
+ EXPECT_EQ(-ENOTCONN, ret);
+ } else if (self->srv0.protocol.type == SOCK_DGRAM) {
+ EXPECT_EQ(-EDESTADDRREQ, ret);
+ } else {
+ EXPECT_EQ(-EINVAL, ret);
+ }
+
+ /* Fast Open to a denied address. */
+ ret = sendto_variant(connect_fd, &self->srv0, "A", 1,
+ MSG_FASTOPEN);
+ if (restricted) {
+ EXPECT_EQ(-EACCES, ret);
+ } else if (self->srv0.protocol.domain == AF_UNIX &&
+ self->srv0.protocol.type == SOCK_STREAM) {
+ EXPECT_EQ(-EOPNOTSUPP, ret);
+ } else {
+ EXPECT_EQ(0, ret);
+ }
+
+ EXPECT_EQ(0, close(connect_fd));
+ _exit(_metadata->exit_code);
+ return;
+ }
+
+ client_fd = bind_fd;
+ if (!restricted && self->srv0.protocol.type == SOCK_STREAM &&
+ self->srv0.protocol.domain != AF_UNIX) {
+ client_fd = accept(bind_fd, NULL, 0);
+ ASSERT_LE(0, client_fd);
+ }
+
+ if (restricted) {
+ EXPECT_EQ(-1, read(client_fd, &buf, 1));
+ EXPECT_EQ(ENOTCONN, errno);
+ } else if (self->srv0.protocol.domain == AF_UNIX &&
+ self->srv0.protocol.type == SOCK_STREAM) {
+ EXPECT_EQ(-1, read(client_fd, &buf, 1));
+ EXPECT_EQ(EINVAL, errno);
+ } else {
+ EXPECT_EQ(1, read(client_fd, &buf, 1));
+ EXPECT_EQ('A', buf);
+ }
+
+ EXPECT_EQ(child, waitpid(child, &status, 0));
+ EXPECT_EQ(1, WIFEXITED(status));
+ EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ if (client_fd != bind_fd)
+ EXPECT_LE(0, close(client_fd));
+
+ EXPECT_EQ(0, close(bind_fd));
+}
+
TEST_F(protocol, sendmsg_stream)
{
int srv0_fd, tmp_fd, client_fd, res;
diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c
index f24f2c28f62e..2d37d0c06c06 100644
--- a/tools/testing/selftests/landlock/scoped_signal_test.c
+++ b/tools/testing/selftests/landlock/scoped_signal_test.c
@@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal)
_metadata->exit_code = KSFT_FAIL;
}
-enum thread_return {
- THREAD_INVALID = 0,
- THREAD_SUCCESS = 1,
- THREAD_ERROR = 2,
- THREAD_TEST_FAILED = 3,
-};
+/* clang-format off */
+#define THREAD_INVALID ((void *)0)
+#define THREAD_SUCCESS ((void *)1)
+#define THREAD_ERROR ((void *)2)
+#define THREAD_TEST_FAILED ((void *)3)
+/* clang-format on */
static void *thread_sync(void *arg)
{
@@ -262,15 +262,15 @@ static void *thread_sync(void *arg)
char buf;
if (read(pipe_read, &buf, 1) != 1)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
TEST(signal_scoping_thread_before)
{
pthread_t no_sandbox_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
@@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before)
EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
- EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
@@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before)
TEST(signal_scoping_thread_after)
{
pthread_t scoped_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
@@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after)
EXPECT_EQ(0, pthread_kill(scoped_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
- EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(scoped_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
@@ -327,20 +327,20 @@ void *thread_setuid(void *ptr)
char buf;
if (read(arg->pipe_read, &buf, 1) != 1)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
/* libc's setuid() should update all thread's credentials. */
if (getuid() != arg->new_uid)
- return (void *)THREAD_TEST_FAILED;
+ return THREAD_TEST_FAILED;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
TEST(signal_scoping_thread_setuid)
{
struct thread_setuid_args arg;
pthread_t no_sandbox_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int pipe_parent[2];
int prev_uid;
@@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid)
EXPECT_EQ(arg.new_uid, getuid());
EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
- EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
clear_cap(_metadata, CAP_SETUID);
@@ -400,6 +400,24 @@ static int setup_signal_handler(int signal)
return sigaction(SIGURG, &sa, NULL);
}
+/*
+ * MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB
+ * switch, so this function can be used for probing for its availability.
+ */
+static bool has_af_unix_oob(void)
+{
+ bool available = false;
+ int sp[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) {
+ available = (send(sp[0], ".", 1, MSG_OOB) == 1);
+ close(sp[0]);
+ close(sp[1]);
+ }
+
+ return available;
+}
+
/* clang-format off */
FIXTURE(fown) {};
/* clang-format on */
@@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket)
int pipe_parent[2], pipe_child[2];
pid_t child;
+ if (!has_af_unix_oob())
+ SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available");
+
memset(&server_address, 0, sizeof(server_address));
set_unix_address(&server_address, 0);
@@ -667,20 +688,20 @@ static void *thread_setown_scoped(void *arg)
ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (ruleset_fd < 0)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
landlock_restrict_self(ruleset_fd, 0)) {
close(ruleset_fd);
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
}
close(ruleset_fd);
/* Makes this process group own the SIGIO source. */
if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) ||
fcntl(fd, F_SETFL, O_ASYNC))
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
/*
@@ -702,7 +723,7 @@ TEST(sigio_to_pgid_self)
{
int trigger[2];
pthread_t thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int i;
drop_caps(_metadata);
@@ -722,7 +743,7 @@ TEST(sigio_to_pgid_self)
*/
ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped,
&trigger[0]));
- ASSERT_EQ(0, pthread_join(thread, (void **)&ret));
+ ASSERT_EQ(0, pthread_join(thread, &ret));
ASSERT_EQ(THREAD_SUCCESS, ret);
/* Fans SIGURG out to the process group. */
diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
index 08ad07500e8a..fb1c59d45567 100755
--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
@@ -736,6 +736,61 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "on bridge"; then
ret=1
fi
+if ip -net "$nsr1" link show tun0 > /dev/null 2>&1 &&
+ ip -net "$nsr2" link show tun0 > /dev/null 2>&1; then
+ ip -net "$nsr1" route change default via 192.168.100.2
+ ip -net "$nsr2" route change default via 192.168.100.1
+ ip -6 -net "$nsr1" route delete default
+ ip -6 -net "$nsr1" route add default via fee1:3::2
+ ip -6 -net "$nsr2" route delete default
+ 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 "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 "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "br0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl 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" accept'
+
+ ip netns exec "$nsr1" nft reset counters table inet filter >/dev/null
+
+ if test_tcp_forwarding "$ns1" "$ns2" 1 4 10.0.2.99 12345; then
+ check_counters "bridge + IPIP tunnel"
+ else
+ echo "FAIL: flow offload for ns1/ns2 with bridge + IPIP tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+ fi
+
+ if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+ check_counters "bridge + IP6IP6 tunnel"
+ else
+ echo "FAIL: flow offload for ns1/ns2 with bridge + IP6IP6 tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+ fi
+
+ ip -net "$nsr1" route change default via 192.168.10.2
+ ip -net "$nsr2" route change default via 192.168.10.1
+ ip -net "$ns2" route del default via 10.0.2.1
+ ip -6 -net "$nsr1" route delete default
+ ip -6 -net "$nsr1" route add default via fee1:2::2
+ ip -6 -net "$nsr2" route delete default
+ ip -6 -net "$nsr2" route add default via fee1:2::1
+ ip -6 -net "$ns2" route del default via dead:2::1
+else
+ echo "SKIP: bridge + tunnel flowtable regression (tun0 missing)"
+ [ "$ret" -eq 0 ] && ret=$ksft_skip
+fi
+
# Another test:
# Add bridge interface br0 to Router1, with NAT and VLAN.
diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile
index 5d2dffca0e91..3cfe90e0f34f 100644
--- a/tools/testing/selftests/sched_ext/Makefile
+++ b/tools/testing/selftests/sched_ext/Makefile
@@ -176,6 +176,7 @@ auto-test-targets := \
maybe_null \
minimal \
non_scx_kfunc_deny \
+ nohz_tick \
numa \
allowed_cpus \
peek_dsq \
diff --git a/tools/testing/selftests/sched_ext/nohz_tick.bpf.c b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c
new file mode 100644
index 000000000000..6998c5dd6bcb
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
+ *
+ * Exercise tick dependency transitions between infinite and finite slices.
+ */
+#include <scx/common.bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+const volatile s32 test_cpu;
+bool finite_phase;
+u64 nr_inf_running;
+u64 nr_finite_running;
+u64 nr_finite_ticks;
+
+UEI_DEFINE(uei);
+
+s32 BPF_STRUCT_OPS(nohz_tick_select_cpu, struct task_struct *p, s32 prev_cpu,
+ u64 wake_flags)
+{
+ return prev_cpu;
+}
+
+void BPF_STRUCT_OPS(nohz_tick_enqueue, struct task_struct *p, u64 enq_flags)
+{
+ u64 slice = finite_phase ? 1000000ULL : SCX_SLICE_INF;
+
+ scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, slice, enq_flags);
+ if (enq_flags & SCX_ENQ_LAST)
+ scx_bpf_kick_cpu(test_cpu, SCX_KICK_IDLE);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_running, struct task_struct *p)
+{
+ if (bpf_get_smp_processor_id() != test_cpu)
+ return;
+
+ if (finite_phase)
+ __sync_fetch_and_add(&nr_finite_running, 1);
+ else
+ __sync_fetch_and_add(&nr_inf_running, 1);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_tick, struct task_struct *p)
+{
+ if (bpf_get_smp_processor_id() == test_cpu && finite_phase)
+ __sync_fetch_and_add(&nr_finite_ticks, 1);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_exit, struct scx_exit_info *ei)
+{
+ UEI_RECORD(uei, ei);
+}
+
+SEC(".struct_ops.link")
+struct sched_ext_ops nohz_tick_ops = {
+ .select_cpu = (void *)nohz_tick_select_cpu,
+ .enqueue = (void *)nohz_tick_enqueue,
+ .running = (void *)nohz_tick_running,
+ .tick = (void *)nohz_tick_tick,
+ .exit = (void *)nohz_tick_exit,
+ .name = "nohz_tick",
+ .timeout_ms = 1000U,
+};
diff --git a/tools/testing/selftests/sched_ext/nohz_tick.c b/tools/testing/selftests/sched_ext/nohz_tick.c
new file mode 100644
index 000000000000..028f54391c2c
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/nohz_tick.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
+ *
+ * Validate that a finite-slice EXT task restarts the scheduler tick when it
+ * follows an infinite-slice EXT task and an idle interval on a NOHZ_FULL CPU.
+ */
+#define _GNU_SOURCE
+
+#include <bpf/bpf.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <scx/common.h>
+
+#include "nohz_tick.bpf.skel.h"
+#include "scx_test.h"
+
+#ifndef SCHED_EXT
+#define SCHED_EXT 7
+#endif
+
+#define MIN_FINITE_TICKS 3
+#define PHASE_TIMEOUT_MS 1000
+
+struct nohz_tick_ctx {
+ struct nohz_tick *skel;
+ cpu_set_t original_mask;
+ int test_cpu;
+};
+
+static int first_allowed_cpu(const cpu_set_t *mask, int first, int last)
+{
+ int cpu;
+
+ for (cpu = first; cpu <= last && cpu < CPU_SETSIZE; cpu++)
+ if (CPU_ISSET(cpu, mask))
+ return cpu;
+
+ return -1;
+}
+
+static int find_nohz_full_cpu(const cpu_set_t *allowed)
+{
+ char buf[4096], *cur, *end;
+ FILE *file;
+
+ file = fopen("/sys/devices/system/cpu/nohz_full", "r");
+ if (!file)
+ return -1;
+ if (!fgets(buf, sizeof(buf), file)) {
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ cur = buf;
+ while (*cur) {
+ long first, last;
+ int cpu;
+
+ while (*cur == ' ' || *cur == '\t' || *cur == ',')
+ cur++;
+ if (*cur < '0' || *cur > '9')
+ break;
+
+ errno = 0;
+ first = strtol(cur, &end, 10);
+ if (errno || end == cur || first < 0 || first >= CPU_SETSIZE)
+ return -1;
+ cur = end;
+ last = first;
+ if (*cur == '-') {
+ cur++;
+ errno = 0;
+ last = strtol(cur, &end, 10);
+ if (errno || end == cur || last < first)
+ return -1;
+ cur = end;
+ }
+
+ cpu = first_allowed_cpu(allowed, first, last);
+ if (cpu >= 0)
+ return cpu;
+ }
+
+ return -1;
+}
+
+static pid_t start_worker(int cpu)
+{
+ struct sched_param param = {};
+ cpu_set_t mask;
+ pid_t parent;
+ pid_t pid;
+
+ parent = getpid();
+ pid = fork();
+ if (pid != 0)
+ return pid;
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) || getppid() != parent)
+ _exit(1);
+
+ /*
+ * Become EXT before touching the target so it stays idle until wakeup.
+ */
+ if (sched_setscheduler(0, SCHED_EXT, &param))
+ _exit(1);
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+ if (sched_setaffinity(0, sizeof(mask), &mask))
+ _exit(1);
+
+ for (;;)
+ asm volatile("" ::: "memory");
+}
+
+static void stop_worker(pid_t pid)
+{
+ if (pid <= 0)
+ return;
+
+ kill(pid, SIGKILL);
+ waitpid(pid, NULL, 0);
+}
+
+static int pause_worker(pid_t pid)
+{
+ int status;
+
+ if (kill(pid, SIGSTOP))
+ return -errno;
+ if (waitpid(pid, &status, WUNTRACED) != pid)
+ return -errno;
+ if (!WIFSTOPPED(status))
+ return -ECHILD;
+
+ return 0;
+}
+
+static bool wait_for_counter(const u64 *counter, u64 value, int timeout_ms)
+{
+ int elapsed;
+
+ for (elapsed = 0; elapsed < timeout_ms; elapsed++) {
+ if (__atomic_load_n(counter, __ATOMIC_RELAXED) >= value)
+ return true;
+ usleep(1000);
+ }
+
+ return false;
+}
+
+static enum scx_test_status setup(void **ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx;
+ cpu_set_t controller_mask;
+ int cpu;
+
+ ctx = calloc(1, sizeof(*ctx));
+ SCX_FAIL_IF(!ctx, "Failed to allocate context");
+ if (sched_getaffinity(0, sizeof(ctx->original_mask),
+ &ctx->original_mask)) {
+ free(ctx);
+ SCX_FAIL("Failed to get affinity (%d)", errno);
+ }
+
+ cpu = find_nohz_full_cpu(&ctx->original_mask);
+ if (cpu < 0) {
+ fprintf(stderr, "SKIP: no allowed NOHZ_FULL CPU\n");
+ free(ctx);
+ return SCX_TEST_SKIP;
+ }
+
+ controller_mask = ctx->original_mask;
+ CPU_CLR(cpu, &controller_mask);
+ if (CPU_COUNT(&controller_mask) == 0) {
+ fprintf(stderr, "SKIP: no housekeeping CPU available\n");
+ free(ctx);
+ return SCX_TEST_SKIP;
+ }
+
+ ctx->test_cpu = cpu;
+ ctx->skel = nohz_tick__open();
+ if (!ctx->skel) {
+ free(ctx);
+ SCX_FAIL("Failed to open skeleton");
+ }
+
+ SCX_ENUM_INIT(ctx->skel);
+ ctx->skel->rodata->test_cpu = cpu;
+ ctx->skel->struct_ops.nohz_tick_ops->flags |= SCX_OPS_SWITCH_PARTIAL |
+ SCX_OPS_ENQ_LAST;
+ if (nohz_tick__load(ctx->skel)) {
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+ SCX_FAIL("Failed to load skeleton");
+ }
+
+ if (sched_setaffinity(0, sizeof(controller_mask), &controller_mask)) {
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+ SCX_FAIL("Failed to move controller off CPU %d (%d)", cpu, errno);
+ }
+
+ *ctx_ptr = ctx;
+ return SCX_TEST_PASS;
+}
+
+static enum scx_test_status run(void *ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx = ctx_ptr;
+ struct nohz_tick *skel = ctx->skel;
+ struct bpf_link *link = NULL;
+ enum scx_test_status status = SCX_TEST_FAIL;
+ pid_t finite_worker = -1;
+ pid_t inf_worker = -1;
+ u64 finite_running;
+ u64 finite_ticks;
+ int ret;
+
+ link = bpf_map__attach_struct_ops(skel->maps.nohz_tick_ops);
+ if (!link) {
+ SCX_ERR("Failed to attach scheduler");
+ goto out;
+ }
+
+ /*
+ * Establish SCX_RQ_CAN_STOP_TICK with an infinite-slice task.
+ */
+ inf_worker = start_worker(ctx->test_cpu);
+ if (inf_worker < 0) {
+ SCX_ERR("Failed to start infinite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_inf_running, 1,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Infinite-slice worker was not scheduled");
+ goto out;
+ }
+
+ /* Block without exiting so the rq retains the infinite-slice state. */
+ ret = pause_worker(inf_worker);
+ if (ret) {
+ SCX_ERR("Failed to stop infinite-slice worker (%d)", ret);
+ goto out;
+ }
+
+ /* Let the target enter idle with its tick stopped. */
+ usleep(100000);
+
+ /*
+ * The next EXT task receives a finite slice and must restart the tick.
+ */
+ __atomic_store_n(&skel->bss->finite_phase, true, __ATOMIC_RELEASE);
+ finite_worker = start_worker(ctx->test_cpu);
+ if (finite_worker < 0) {
+ SCX_ERR("Failed to start finite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_running, 1,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Finite-slice worker was not scheduled");
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_ticks, MIN_FINITE_TICKS,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Finite-slice worker received only %llu scheduler ticks",
+ (unsigned long long)skel->bss->nr_finite_ticks);
+ goto out;
+ }
+ stop_worker(finite_worker);
+ finite_worker = -1;
+
+ /*
+ * Leave the CPU idle after a finite-slice task. The next finite-slice
+ * task must restart the tick even though the slice type is unchanged.
+ */
+ usleep(100000);
+ finite_running = __atomic_load_n(&skel->bss->nr_finite_running,
+ __ATOMIC_RELAXED);
+ finite_ticks = __atomic_load_n(&skel->bss->nr_finite_ticks,
+ __ATOMIC_RELAXED);
+
+ finite_worker = start_worker(ctx->test_cpu);
+ if (finite_worker < 0) {
+ SCX_ERR("Failed to start second finite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_running,
+ finite_running + 1, PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Second finite-slice worker was not scheduled");
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_ticks,
+ finite_ticks + MIN_FINITE_TICKS,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Second finite-slice worker received only %llu scheduler ticks",
+ (unsigned long long)(skel->bss->nr_finite_ticks -
+ finite_ticks));
+ goto out;
+ }
+
+ if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) {
+ SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)",
+ (unsigned long long)skel->data->uei.kind,
+ (long long)skel->data->uei.exit_code);
+ goto out;
+ }
+
+ fprintf(stderr, "CPU %d received %llu finite-slice ticks\n",
+ ctx->test_cpu,
+ (unsigned long long)skel->bss->nr_finite_ticks);
+ status = SCX_TEST_PASS;
+out:
+ stop_worker(finite_worker);
+ stop_worker(inf_worker);
+ if (link)
+ bpf_link__destroy(link);
+ return status;
+}
+
+static void cleanup(void *ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx = ctx_ptr;
+
+ sched_setaffinity(0, sizeof(ctx->original_mask), &ctx->original_mask);
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+}
+
+struct scx_test nohz_tick = {
+ .name = "nohz_tick",
+ .description = "Verify finite EXT slices restart the NOHZ_FULL tick",
+ .setup = setup,
+ .run = run,
+ .cleanup = cleanup,
+};
+REGISTER_SCX_TEST(&nohz_tick)
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile
index 60a102538988..387bc6cc18f0 100644
--- a/tools/tracing/rtla/Makefile
+++ b/tools/tracing/rtla/Makefile
@@ -45,6 +45,9 @@ else
LIB_OUTPUT = $(CURDIR)/lib
endif
+LIB_CTYPE = $(LIB_OUTPUT)/ctype.o
+LIB_CTYPE_SRC = $(srctree)/tools/lib/ctype.c
+
LIB_STRING = $(LIB_OUTPUT)/string.o
LIB_STRING_SRC = $(srctree)/tools/lib/string.c
@@ -117,12 +120,12 @@ tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c
$(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o"
endif
-$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
- $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
+$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R)
+ $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
-static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
+static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R)
$(eval LDFLAGS += -static)
- $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
+ $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
rtla.%: fixdep FORCE
make -f $(srctree)/tools/build/Makefile.build dir=. $@
@@ -150,6 +153,9 @@ $(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) | $(LIB_OUTPUT)
$(LIB_STRING): $(LIB_STRING_SRC) | $(LIB_OUTPUT)
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
+$(LIB_CTYPE): $(LIB_CTYPE_SRC) | $(LIB_OUTPUT)
+ $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
+
libsubcmd-clean:
$(call QUIET_CLEAN, libsubcmd)
$(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c
index d0a8a6edbf0c..8c7f5e75b2ec 100644
--- a/tools/tracing/rtla/src/common.c
+++ b/tools/tracing/rtla/src/common.c
@@ -5,6 +5,7 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <sys/sysinfo.h>
#include "common.h"
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index e44c20c04961..45e784462ec6 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6069,25 +6069,19 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
gpa_t addr)
{
struct kvm_io_bus *bus;
- int dev_idx, srcu_idx;
- struct kvm_io_device *iodev = NULL;
+ int dev_idx;
- srcu_idx = srcu_read_lock(&kvm->srcu);
+ lockdep_assert_held(&kvm->srcu);
bus = kvm_get_bus_srcu(kvm, bus_idx);
if (!bus)
- goto out_unlock;
+ return NULL;
dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1);
if (dev_idx < 0)
- goto out_unlock;
-
- iodev = bus->range[dev_idx].dev;
-
-out_unlock:
- srcu_read_unlock(&kvm->srcu, srcu_idx);
+ return NULL;
- return iodev;
+ return bus->range[dev_idx].dev;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_get_dev);