]> exis.tech > repos - linux.git/commitdiff
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec... master
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Jul 2026 15:34:39 +0000 (08:34 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Jul 2026 15:34:39 +0000 (08:34 -0700)
Pull hwmon fixes from Guenter Roeck:

 - asus-ec-sensors: Add missed handle for ENOMEM, fix EC read
   intervals, and fix looping over banks while reading from EC

 - occ: validate poll response sensor blocks

 - pmbus/max34440: Block unsupported VIN and IIN limit registers

 - nzxt-kraken3, nzxt-smart2: gigabyte_waterforce, corsair-cpro,
   corsair-psu: Stop device IO before calling hid_hw_stop

* tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: occ: validate poll response sensor blocks
  hwmon: (asus-ec-sensors) add missed handle for ENOMEM
  hwmon: (asus-ec-sensors) fix EC read intervals
  hwmon: (asus-ec-sensors) fix looping over banks while reading from EC
  hwmon: (pmbus/max34440) block unsupported VIN and IIN limit registers
  hwmon: (nzxt-kraken3) Stop device IO before calling hid_hw_stop
  hwmon: (nzxt-smart2) Stop device IO before calling hid_hw_stop
  hwmon: (gigabyte_waterforce) Stop device IO before calling hid_hw_stop
  hwmon: (corsair-cpro) Stop device IO before calling hid_hw_stop
  hwmon: (corsair-psu) Stop device IO before calling hid_hw_stop

910 files changed:
.mailmap
CREDITS
Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
Documentation/ABI/testing/sysfs-kernel-mm-damon
Documentation/admin-guide/cgroup-v1/rdma.rst
Documentation/admin-guide/cgroup-v2.rst
Documentation/arch/arm64/cpu-hotplug.rst
Documentation/arch/riscv/hwprobe.rst
Documentation/crypto/libcrypto-blockcipher.rst
Documentation/crypto/libcrypto-hash.rst
Documentation/crypto/libcrypto-signature.rst
Documentation/crypto/libcrypto.rst
Documentation/devicetree/bindings/mmc/mtk-sd.yaml
Documentation/devicetree/bindings/riscv/extensions.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
Documentation/process/embargoed-hardware-issues.rst
Documentation/scheduler/sched-ext.rst
Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst
MAINTAINERS
Makefile
arch/arc/configs/axs101_defconfig
arch/arc/configs/axs103_defconfig
arch/arc/configs/axs103_smp_defconfig
arch/arc/configs/tb10x_defconfig
arch/arc/kernel/smp.c
arch/arm/mach-ixp4xx/Kconfig
arch/arm/mach-pxa/Kconfig
arch/arm64/boot/dts/nvidia/tegra234.dtsi
arch/arm64/boot/dts/nvidia/tegra264.dtsi
arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
arch/arm64/include/asm/kvm_nested.h
arch/arm64/include/asm/tlbbatch.h
arch/arm64/include/asm/tlbflush.h
arch/arm64/kernel/acpi.c
arch/arm64/kernel/fpsimd.c
arch/arm64/kernel/process.c
arch/arm64/kernel/smp.c
arch/arm64/kvm/at.c
arch/arm64/kvm/emulate-nested.c
arch/arm64/kvm/hyp/include/hyp/switch.h
arch/arm64/kvm/hyp/nvhe/ffa.c
arch/arm64/kvm/hyp/nvhe/pkvm.c
arch/arm64/kvm/hyp/nvhe/sys_regs.c
arch/arm64/kvm/hyp/pgtable.c
arch/arm64/kvm/inject_fault.c
arch/arm64/kvm/mmio.c
arch/arm64/kvm/nested.c
arch/arm64/kvm/pkvm.c
arch/arm64/kvm/vgic/vgic-its.c
arch/arm64/kvm/vgic/vgic.c
arch/arm64/mm/mmu.c
arch/arm64/tools/sysreg
arch/m68k/coldfire/m523x.c
arch/m68k/coldfire/m528x.c
arch/m68k/include/asm/page_mm.h
arch/powerpc/include/asm/preempt.h [deleted file]
arch/powerpc/include/asm/uaccess.h
arch/powerpc/kernel/dt_cpu_ftrs.c
arch/powerpc/kernel/time.c
arch/powerpc/lib/vmx-helper.c
arch/powerpc/platforms/85xx/common.c
arch/powerpc/platforms/cell/spufs/file.c
arch/powerpc/platforms/pseries/Kconfig
arch/powerpc/platforms/pseries/papr_platform_attributes.c
arch/riscv/Kconfig
arch/riscv/Kconfig.socs
arch/riscv/configs/defconfig
arch/riscv/include/asm/cacheflush.h
arch/riscv/include/asm/io.h
arch/riscv/include/asm/kvm_host.h
arch/riscv/kernel/entry.S
arch/riscv/kernel/machine_kexec.c
arch/riscv/kernel/sys_hwprobe.c
arch/riscv/kernel/vdso/Makefile
arch/riscv/kernel/vdso/rt_sigreturn.S
arch/riscv/kvm/aia.c
arch/riscv/kvm/gstage.c
arch/riscv/kvm/mmu.c
arch/riscv/kvm/vcpu.c
arch/riscv/kvm/vcpu_exit.c
arch/riscv/kvm/vcpu_onereg.c
arch/riscv/kvm/vcpu_pmu.c
arch/riscv/kvm/vcpu_sbi_fwft.c
arch/riscv/kvm/vcpu_vector.c
arch/riscv/mm/init.c
arch/s390/kernel/diag/diag310.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/kernel/vdso/note.S
arch/s390/kvm/dat.c
arch/s390/kvm/gmap.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/pci.c
arch/s390/lib/csum-partial.c
arch/s390/mm/mmap.c
arch/x86/boot/compressed/acpi.c
arch/x86/boot/early_serial_console.c
arch/x86/events/amd/brs.c
arch/x86/events/amd/core.c
arch/x86/events/amd/lbr.c
arch/x86/kernel/cpu/Makefile
arch/x86/kvm/irq.c
arch/x86/kvm/lapic.c
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/paging_tmpl.h
arch/x86/kvm/svm/sev.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/trace.h
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/tdx.c
arch/x86/kvm/vmx/vmx.h
arch/x86/video/video-common.c
arch/x86/virt/svm/sev.c
block/bio.c
block/blk-cgroup.c
block/blk-map.c
block/blk-mq.c
block/blk-wbt.c
block/blk-zoned.c
block/blk.h
block/elevator.c
block/error-injection.c
block/genhd.c
block/partitions/aix.c
crypto/Kconfig
drivers/accel/amdxdna/aie2_ctx.c
drivers/accel/amdxdna/aie2_message.c
drivers/accel/amdxdna/amdxdna_ctx.c
drivers/accel/amdxdna/amdxdna_gem.c
drivers/accel/amdxdna/amdxdna_gem.h
drivers/accel/amdxdna/amdxdna_pci_drv.c
drivers/accel/ivpu/ivpu_fw_log.c
drivers/accel/ivpu/ivpu_hw_btrs.c
drivers/android/binder.c
drivers/android/binder/allocation.rs
drivers/android/binder/error.rs
drivers/android/binder/freeze.rs
drivers/android/binder/node.rs
drivers/android/binder/process.rs
drivers/android/binder/rust_binder_events.c
drivers/android/binder/stats.rs
drivers/android/binder/thread.rs
drivers/android/binder/transaction.rs
drivers/ata/libata-core.c
drivers/ata/sata_dwc_460ex.c
drivers/block/drbd/drbd_receiver.c
drivers/block/loop.c
drivers/block/ublk_drv.c
drivers/block/xen-blkfront.c
drivers/bluetooth/bpa10x.c
drivers/bluetooth/btintel_pcie.c
drivers/bluetooth/btnxpuart.c
drivers/bluetooth/btqca.c
drivers/bluetooth/btrtl.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_qca.c
drivers/char/tpm/tpm-dev.c
drivers/char/tpm/tpmrm-dev.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/dibs/dibs_loopback.c
drivers/dma-buf/udmabuf.c
drivers/dpll/dpll_netlink.c
drivers/firmware/arm_ffa/driver.c
drivers/firmware/arm_scmi/Kconfig
drivers/firmware/arm_scmi/clock.c
drivers/firmware/arm_scmi/notify.c
drivers/gpio/gpio-dwapb.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-palmas.c
drivers/gpio/gpio-shared-proxy.c
drivers/gpio/gpiolib-shared.h
drivers/gpu/buddy.c
drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
drivers/gpu/drm/amd/amdgpu/soc21.c
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
drivers/gpu/drm/amd/amdkfd/kfd_events.c
drivers/gpu/drm/amd/amdkfd/kfd_priv.h
drivers/gpu/drm/amd/amdkfd/kfd_process.c
drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
drivers/gpu/drm/amd/amdkfd/kfd_queue.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
drivers/gpu/drm/amd/display/dc/core/dc.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
drivers/gpu/drm/amd/display/dc/link/link_detection.c
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h
drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c
drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gpusvm.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_panel_backlight_quirks.c
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_lt_phy.c
drivers/gpu/drm/i915/display/intel_psr.c
drivers/gpu/drm/i915/display/skl_universal_plane.c
drivers/gpu/drm/i915/display/skl_watermark.c
drivers/gpu/drm/i915/gem/i915_gem_context.c
drivers/gpu/drm/i915/gt/intel_engine_user.c
drivers/gpu/drm/i915/gt/intel_execlists_submission.c
drivers/gpu/drm/i915/gt/selftest_gt_pm.c
drivers/gpu/drm/imagination/pvr_context.c
drivers/gpu/drm/imagination/pvr_fw_trace.c
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
drivers/gpu/drm/panthor/panthor_device.c
drivers/gpu/drm/panthor/panthor_fw.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tests/drm_exec_test.c
drivers/gpu/drm/ttm/ttm_pool.c
drivers/gpu/drm/v3d/v3d_submit.c
drivers/gpu/drm/virtio/virtgpu_gem.c
drivers/gpu/drm/virtio/virtgpu_kms.c
drivers/gpu/drm/virtio/virtgpu_vq.c
drivers/gpu/drm/xe/tests/xe_pci.c
drivers/gpu/drm/xe/xe_bo.c
drivers/gpu/drm/xe/xe_bo.h
drivers/gpu/drm/xe/xe_bo_types.h
drivers/gpu/drm/xe/xe_device.c
drivers/gpu/drm/xe/xe_device_types.h
drivers/gpu/drm/xe/xe_dma_buf.c
drivers/gpu/drm/xe/xe_exec.c
drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
drivers/gpu/drm/xe/xe_guc_submit.c
drivers/gpu/drm/xe/xe_guc_types.h
drivers/gpu/drm/xe/xe_migrate.c
drivers/gpu/drm/xe/xe_migrate.h
drivers/gpu/drm/xe/xe_module.c
drivers/gpu/drm/xe/xe_module.h
drivers/gpu/drm/xe/xe_nvm.c
drivers/gpu/drm/xe/xe_pci.c
drivers/gpu/drm/xe/xe_pt.c
drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
drivers/gpu/drm/xe/xe_vm.c
drivers/gpu/drm/xe/xe_vm_madvise.c
drivers/gpu/drm/xe/xe_wopcm.c
drivers/gpu/host1x/bus.c
drivers/hid/bpf/hid_bpf_dispatch.c
drivers/hid/hid-appleir.c
drivers/hid/hid-core.c
drivers/hid/hid-letsketch.c
drivers/hid/hid-lg-g15.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-picolcd_core.c
drivers/hid/hid-sensor-hub.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-k1.c
drivers/i2c/busses/i2c-mlxbf.c
drivers/i2c/busses/i2c-mt65xx.c
drivers/iio/accel/bmc150-accel-core.c
drivers/iio/accel/kxsd9.c
drivers/iio/adc/Kconfig
drivers/iio/adc/lpc32xx_adc.c
drivers/iio/adc/nxp-sar-adc.c
drivers/iio/adc/spear_adc.c
drivers/iio/adc/ti-ads1119.c
drivers/iio/adc/ti-ads124s08.c
drivers/iio/common/st_sensors/st_sensors_core.c
drivers/iio/dac/mcp47feb02.c
drivers/iio/imu/adis_trigger.c
drivers/iio/imu/bmi160/bmi160_core.c
drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
drivers/iio/industrialio-event.c
drivers/iio/light/Kconfig
drivers/iio/light/al3010.c
drivers/iio/light/gp2ap002.c
drivers/iio/light/tsl2591.c
drivers/iio/orientation/hid-sensor-rotation.c
drivers/iio/pressure/mpl115.c
drivers/iio/temperature/Makefile
drivers/infiniband/core/cma.c
drivers/infiniband/core/mad.c
drivers/infiniband/core/verbs.c
drivers/infiniband/hw/erdma/erdma_qp.c
drivers/infiniband/hw/hns/hns_roce_hem.c
drivers/infiniband/hw/irdma/verbs.c
drivers/infiniband/hw/mana/wr.c
drivers/infiniband/hw/mlx5/wr.c
drivers/infiniband/sw/siw/siw_verbs.c
drivers/input/joystick/maplecontrol.c
drivers/input/keyboard/maple_keyb.c
drivers/input/mouse/maplemouse.c
drivers/input/touchscreen/mms114.c
drivers/md/dm-bufio.c
drivers/md/dm-era-target.c
drivers/md/dm-inlinecrypt.c
drivers/md/dm-integrity.c
drivers/md/dm-ioctl.c
drivers/md/dm-log.c
drivers/md/dm-pcache/dm_pcache.c
drivers/md/dm-stats.c
drivers/md/dm-thin-metadata.c
drivers/md/dm-verity-fec.c
drivers/md/dm-verity-fec.h
drivers/md/dm-verity-loadpin.c
drivers/md/dm-verity-target.c
drivers/md/dm-verity.h
drivers/md/dm.c
drivers/memstick/core/ms_block.c
drivers/mmc/core/block.c
drivers/mmc/core/mmc_test.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-of-dwcmshc.c
drivers/mmc/host/vub300.c
drivers/mtd/devices/mchp23k256.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/uclinux.c [new file with mode: 0644]
drivers/mtd/mtd_virt_concat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/mtdswap.c
drivers/mtd/nand/ecc-mtk.c
drivers/mtd/nand/onenand/onenand_samsung.c
drivers/mtd/nand/raw/Kconfig
drivers/mtd/nand/raw/fsl_ifc_nand.c
drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
drivers/mtd/nand/raw/lpc32xx_mlc.c
drivers/mtd/nand/raw/lpc32xx_slc.c
drivers/mtd/nand/raw/ndfc.c
drivers/mtd/nand/spi/core.c
drivers/net/amt.c
drivers/net/can/Kconfig
drivers/net/can/peak_canfd/peak_canfd.c
drivers/net/can/peak_canfd/peak_canfd_user.h
drivers/net/can/peak_canfd/peak_pciefd_main.c
drivers/net/can/sja1000/peak_pci.c
drivers/net/can/sja1000/peak_pcmcia.c
drivers/net/can/usb/esd_usb.c
drivers/net/can/usb/peak_usb/pcan_usb.c
drivers/net/can/usb/peak_usb/pcan_usb_core.c
drivers/net/can/usb/peak_usb/pcan_usb_core.h
drivers/net/can/usb/peak_usb/pcan_usb_fd.c
drivers/net/can/usb/peak_usb/pcan_usb_pro.c
drivers/net/can/usb/peak_usb/pcan_usb_pro.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/liquidio/octeon_device.h
drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c
drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c
drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
drivers/net/ethernet/microchip/vcap/vcap_api.c
drivers/net/ethernet/microchip/vcap/vcap_api.h
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c
drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
drivers/net/ethernet/microchip/vcap/vcap_api_private.h
drivers/net/ethernet/microsoft/mana/mana_en.c
drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
drivers/net/ethernet/qlogic/qede/qede_fp.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
drivers/net/macsec.c
drivers/net/mdio/Kconfig
drivers/net/ppp/ppp_generic.c
drivers/net/usb/lan78xx.c
drivers/net/usb/net1080.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intel/ipw2x00/libipw_rx.c
drivers/net/wireless/intersil/p54/txrx.c
drivers/net/wireless/marvell/libertas/firmware.c
drivers/net/wireless/marvell/libertas/tx.c
drivers/net/wireless/marvell/libertas_tf/main.c
drivers/net/wireless/marvell/mwifiex/cfg80211.c
drivers/net/wireless/marvell/mwifiex/join.c
drivers/net/wireless/ralink/rt2x00/rt2400pci.c
drivers/net/wireless/ralink/rt2x00/rt2500pci.c
drivers/net/wireless/ralink/rt2x00/rt2800pci.c
drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
drivers/net/wireless/ralink/rt2x00/rt61pci.c
drivers/net/wireless/rsi/rsi_91x_hal.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/virtual/mac80211_hwsim_main.c
drivers/platform/x86/amd/pmc/pmc.c
drivers/platform/x86/asus-armoury.h
drivers/platform/x86/bitland-mifs-wmi.c
drivers/pmdomain/imx/imx8m-blk-ctrl.c
drivers/pmdomain/imx/imx93-blk-ctrl.c
drivers/pmdomain/mediatek/mtk-pm-domains.c
drivers/regulator/core.c
drivers/regulator/ltc3676.c
drivers/regulator/mt6316-regulator.c
drivers/regulator/mt6363-regulator.c
drivers/reset/reset-imx7.c
drivers/reset/reset-sunxi.c
drivers/reset/spacemit/reset-spacemit-k3.c
drivers/s390/crypto/zcrypt_cex2a.c [deleted file]
drivers/s390/crypto/zcrypt_cex2a.h [deleted file]
drivers/s390/crypto/zcrypt_cex2c.c [deleted file]
drivers/s390/crypto/zcrypt_cex2c.h [deleted file]
drivers/scsi/bfa/bfa_fcs_lport.c
drivers/scsi/elx/efct/efct_hw.c
drivers/scsi/elx/efct/efct_unsol.c
drivers/scsi/hosts.c
drivers/scsi/hpsa.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/scsi_error.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_priv.h
drivers/scsi/sg.c
drivers/soc/tegra/fuse/tegra-apbmisc.c
drivers/spi/spi-cadence-quadspi.c
drivers/spi/spi-dw-dma.c
drivers/staging/rtl8723bs/core/rtw_ieee80211.c
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
drivers/staging/rtl8723bs/core/rtw_wlan_util.c
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
drivers/staging/rtl8723bs/os_dep/xmit_linux.c
drivers/staging/vme_user/vme_fake.c
drivers/staging/vme_user/vme_tsi148.c
drivers/staging/vme_user/vme_user.c
drivers/target/target_core_fabric_lib.c
drivers/target/target_core_internal.h
drivers/target/target_core_pr.c
drivers/tty/serial/8250/8250_mid.c
drivers/tty/serial/8250/8250_omap.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/max310x.c
drivers/tty/serial/msm_serial.c
drivers/tty/vt/keyboard.c
drivers/ufs/core/ufs_trace.h
drivers/usb/atm/ueagle-atm.c
drivers/usb/cdns3/cdnsp-mem.c
drivers/usb/class/cdc-acm.c
drivers/usb/common/ulpi.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-meson-g12a.c
drivers/usb/dwc3/gadget.c
drivers/usb/fotg210/fotg210-hcd.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_printer.c
drivers/usb/gadget/function/rndis.c
drivers/usb/gadget/udc/core.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/xhci-dbgcap.c
drivers/usb/host/xhci-dbgcap.h
drivers/usb/host/xhci-sideband.c
drivers/usb/host/xhci.c
drivers/usb/misc/chaoskey.c
drivers/usb/misc/idmouse.c
drivers/usb/misc/iowarrior.c
drivers/usb/misc/ldusb.c
drivers/usb/misc/legousbtower.c
drivers/usb/misc/usbio.c
drivers/usb/misc/uss720.c
drivers/usb/mtu3/mtu3_gadget.c
drivers/usb/serial/digi_acceleport.c
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/option.c
drivers/usb/storage/ene_ub6250.c
drivers/usb/storage/usb.c
drivers/usb/typec/anx7411.c
drivers/usb/typec/class.c
drivers/usb/typec/mux.c
drivers/usb/typec/mux/ps883x.c
drivers/usb/typec/tcpm/tcpci_rt1711h.c
drivers/usb/typec/tcpm/tcpm.c
drivers/usb/typec/ucsi/displayport.c
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
drivers/usb/typec/ucsi/ucsi_acpi.c
drivers/usb/typec/ucsi/ucsi_ccg.c
drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
drivers/usb/usbip/vudc.h
drivers/usb/usbip/vudc_dev.c
drivers/virtio/virtio_balloon.c
drivers/xen/xen-scsiback.c
fs/btrfs/backref.c
fs/btrfs/btrfs_inode.h
fs/btrfs/extent_io.c
fs/btrfs/extent_map.c
fs/btrfs/file-item.c
fs/btrfs/free-space-cache.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/lzo.c
fs/btrfs/print-tree.c
fs/btrfs/props.c
fs/btrfs/relocation.c
fs/btrfs/subpage.c
fs/btrfs/tree-checker.c
fs/erofs/ishare.c
fs/erofs/super.c
fs/erofs/zmap.c
fs/fat/fat.h
fs/fat/fat_test.c
fs/nfs/dir.c
fs/nfs/internal.h
fs/nfs/pnfs_nfs.c
fs/nfs/write.c
fs/nfsd/nfsctl.c
fs/ntfs/aops.c
fs/ntfs/attrib.c
fs/ntfs/attrlist.c
fs/ntfs/dir.c
fs/ntfs/index.c
fs/ntfs/inode.c
fs/ntfs/mft.c
fs/ntfs/namei.c
fs/proc/page.c
fs/resctrl/rdtgroup.c
fs/smb/client/cifs_fs_sb.h
fs/smb/client/cifsacl.c
fs/smb/client/cifsfs.c
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/cifssmb.c
fs/smb/client/connect.c
fs/smb/client/dfs_cache.c
fs/smb/client/file.c
fs/smb/client/inode.c
fs/smb/client/link.c
fs/smb/client/misc.c
fs/smb/client/readdir.c
fs/smb/client/reparse.c
fs/smb/client/smb1maperror.c
fs/smb/client/smb1maperror_test.c
fs/smb/client/smb1ops.c
fs/smb/client/smb2file.c
fs/smb/client/smb2maperror.c
fs/smb/client/smb2maperror_test.c
fs/smb/client/smb2misc.c
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c
fs/smb/client/smb2proto.h
fs/smb/common/fscc.h
fs/smb/server/auth.c
fs/smb/server/auth.h
fs/smb/server/mgmt/user_session.c
fs/smb/server/mgmt/user_session.h
fs/smb/server/oplock.c
fs/smb/server/server.c
fs/smb/server/smb2misc.c
fs/smb/server/smb2pdu.c
fs/smb/server/smb2pdu.h
fs/smb/server/smbacl.c
fs/smb/server/vfs_cache.h
fs/xfs/libxfs/xfs_defer.c
fs/xfs/scrub/cow_repair.c
fs/xfs/scrub/dirtree.c
fs/xfs/scrub/dqiterate.c
fs/xfs/scrub/inode_repair.c
fs/xfs/scrub/rgsuper.c
fs/xfs/scrub/rtbitmap.c
fs/xfs/scrub/rtrefcount.c
fs/xfs/scrub/rtrmap.c
fs/xfs/scrub/scrub.h
fs/xfs/scrub/trace.h
fs/xfs/scrub/xfarray.c
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_bmap_util.h
fs/xfs/xfs_log_cil.c
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_reflink.c
fs/xfs/xfs_sysfs.c
fs/xfs/xfs_sysfs.h
fs/xfs/xfs_trace.h
fs/xfs/xfs_zone_alloc.c
include/drm/drm_exec.h
include/drm/drm_utils.h
include/dt-bindings/power/fsl,imx93-power.h
include/dt-bindings/reset/altr,rst-mgr-s10.h
include/linux/arm_ffa.h
include/linux/btf.h
include/linux/can/dev/peak_canfd.h
include/linux/damon.h
include/linux/fs.h
include/linux/glob.h
include/linux/hid-sensor-hub.h
include/linux/ieee80211-eht.h
include/linux/inetdevice.h
include/linux/page_ext.h
include/linux/page_reporting.h
include/linux/skmsg.h
include/linux/sunrpc/clnt.h
include/linux/tracepoint.h
include/net/addrconf.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/cfg80211.h
include/net/gue.h
include/net/ip_vs.h
include/net/mana/mana.h
include/net/netfilter/nf_flow_table.h
include/net/sctp/structs.h
include/net/tc_act/tc_pedit.h
include/net/xfrm.h
include/scsi/scsi_device.h
include/scsi/scsi_host.h
include/soc/tegra/pmc.h
include/trace/events/memory-failure.h
include/uapi/linux/btrfs.h
io_uring/bpf-ops.c
io_uring/fs.c
io_uring/kbuf.c
io_uring/memmap.c
io_uring/msg_ring.c
io_uring/tw.c
io_uring/uring_cmd.c
kernel/audit.c
kernel/bpf/btf.c
kernel/bpf/verifier.c
kernel/cgroup/cpuset.c
kernel/events/core.c
kernel/exit.c
kernel/sched/ext/ext.c
kernel/sched/ext/internal.h
kernel/signal.c
kernel/time/posix-cpu-timers.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
kernel/trace/trace_eprobe.c
kernel/trace/trace_events_filter.c
kernel/trace/trace_events_synth.c
kernel/trace/trace_events_user.c
kernel/trace/trace_functions.c
kernel/trace/trace_osnoise.c
kernel/trace/trace_preemptirq.c
kernel/trace/trace_probe.c
kernel/trace/trace_remote.c
lib/bug.c
lib/crypto/Kconfig
lib/crypto/md5.c
lib/glob.c
lib/rhashtable.c
mm/compaction.c
mm/damon/core.c
mm/damon/lru_sort.c
mm/damon/modules-common.c
mm/damon/modules-common.h
mm/damon/ops-common.c
mm/damon/ops-common.h
mm/damon/paddr.c
mm/damon/reclaim.c
mm/damon/sysfs-common.c
mm/damon/sysfs-common.h
mm/damon/sysfs-schemes.c
mm/damon/sysfs.c
mm/damon/tests/core-kunit.h
mm/damon/tests/sysfs-kunit.h
mm/damon/tests/vaddr-kunit.h
mm/damon/vaddr.c
mm/filemap.c
mm/huge_memory.c
mm/kmemleak.c
mm/madvise.c
mm/mincore.c
mm/page_reporting.c
mm/page_vma_mapped.c
mm/shrinker.c
mm/shrinker_debug.c
mm/sparse-vmemmap.c
mm/userfaultfd.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/fragmentation.c
net/batman-adv/main.c
net/batman-adv/mesh-interface.c
net/batman-adv/mesh-interface.h
net/batman-adv/multicast_forw.c
net/batman-adv/translation-table.c
net/bluetooth/6lowpan.c
net/bluetooth/af_bluetooth.c
net/bluetooth/bnep/core.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sync.c
net/bluetooth/iso.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/msft.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bridge/netfilter/ebtables.c
net/can/bcm.c
net/can/isotp.c
net/can/j1939/transport.c
net/can/raw.c
net/core/bpf_sk_storage.c
net/core/dev.c
net/core/sock.c
net/core/sock_map.c
net/ethtool/netlink.h
net/ethtool/rss.c
net/ipv4/fib_trie.c
net/ipv4/igmp.c
net/ipv4/tcp_bpf.c
net/ipv4/tcp_ipv4.c
net/ipv6/mcast.c
net/ipv6/netfilter.c
net/ipv6/netfilter/ip6t_ah.c
net/ipv6/netfilter/ip6t_hbh.c
net/ipv6/netfilter/ip6t_rt.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/tcp_ipv6.c
net/ipv6/xfrm6_policy.c
net/iucv/af_iucv.c
net/llc/af_llc.c
net/llc/llc_conn.c
net/mac80211/cfg.c
net/mac80211/ibss.c
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/nan.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac802154/iface.c
net/mpls/af_mpls.c
net/netfilter/ipset/ip_set_hash_gen.h
net/netfilter/ipvs/ip_vs_app.c
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_proto_sctp.c
net/netfilter/ipvs/ip_vs_proto_tcp.c
net/netfilter/ipvs/ip_vs_proto_udp.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_conncount.c
net/netfilter/nf_conntrack_ecache.c
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_flow_table_ip.c
net/netfilter/nf_flow_table_offload.c
net/netfilter/nf_nat_sip.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink_cthelper.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue.c
net/netfilter/nft_lookup.c
net/netfilter/nft_set_rbtree.c
net/netfilter/xt_connmark.c
net/netfilter/xt_nat.c
net/netfilter/xt_physdev.c
net/netfilter/xt_rateest.c
net/netfilter/xt_u32.c
net/openvswitch/flow_netlink.c
net/sched/act_api.c
net/sched/act_pedit.c
net/sched/act_tunnel_key.c
net/sched/cls_api.c
net/sched/sch_cake.c
net/sched/sch_teql.c
net/sctp/sm_statefuns.c
net/smc/smc_cdc.c
net/sunrpc/clnt.c
net/sunrpc/xprtsock.c
net/tls/tls_sw.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/pmsr.c
net/wireless/scan.c
net/wireless/sme.c
net/xfrm/xfrm_device.c
net/xfrm/xfrm_iptfs.c
net/xfrm/xfrm_nat_keepalive.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
samples/damon/mtier.c
samples/ftrace/ftrace-ops.c
security/landlock/net.c
security/landlock/ruleset.h
security/landlock/task.c
security/selinux/hooks.c
sound/firewire/bebob/bebob.h
sound/firewire/dice/dice.h
sound/firewire/digi00x/digi00x.h
sound/firewire/fireface/ff.h
sound/firewire/fireworks/fireworks.h
sound/firewire/motu/motu.h
sound/firewire/oxfw/oxfw.h
sound/firewire/tascam/tascam.h
sound/hda/codecs/conexant.c
sound/hda/codecs/realtek/alc269.c
sound/hda/codecs/side-codecs/Kconfig
sound/hda/codecs/side-codecs/cs35l56_hda.c
sound/hda/core/regmap.c
sound/soc/amd/acp/Kconfig
sound/soc/amd/acp/Makefile
sound/soc/amd/acp/amd-acp70-acpi-match.c
sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
sound/soc/amd/ps/pci-ps.c
sound/soc/codecs/cs42l43-jack.c
sound/soc/codecs/rt712-sdca.c
sound/soc/codecs/tas2562.c
sound/soc/intel/boards/sof_sdw.c
sound/soc/meson/aiu-fifo-spdif.c
sound/soc/qcom/sc8280xp.c
sound/usb/caiaq/device.c
sound/usb/caiaq/input.c
sound/usb/mixer.c
sound/usb/quirks.c
tools/include/linux/overflow.h
tools/sched_ext/include/scx/cid.bpf.h
tools/testing/selftests/alsa/mixer-test.c
tools/testing/selftests/arm64/gcs/libc-gcs.c
tools/testing/selftests/arm64/pauth/pac.c
tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c [deleted file]
tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
tools/testing/selftests/bpf/prog_tests/verifier.c
tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
tools/testing/selftests/gpio/.gitignore
tools/testing/selftests/hid/Makefile
tools/testing/selftests/hid/hid_bpf.c
tools/testing/selftests/hid/progs/hid.c
tools/testing/selftests/hid/tests/test_multitouch.py
tools/testing/selftests/kvm/Makefile.kvm
tools/testing/selftests/kvm/arm64/mmio_sign_ext.c [new file with mode: 0644]
tools/testing/selftests/kvm/x86/sev_init2_tests.c
tools/testing/selftests/kvm/x86/sev_migrate_tests.c
tools/testing/selftests/kvm/x86/sev_smoke_test.c
tools/testing/selftests/landlock/net_test.c
tools/testing/selftests/landlock/scoped_signal_test.c
tools/testing/selftests/mm/hmm-tests.c
tools/testing/selftests/mm/ksft_process_madv.sh
tools/testing/selftests/mm/pagemap_ioctl.c
tools/testing/selftests/net/lib.sh
tools/testing/selftests/net/netfilter/nft_flowtable.sh
tools/testing/selftests/net/tcp_mmap.c
tools/testing/selftests/riscv/vector/validate_v_ptrace.c
tools/testing/selftests/rseq/Makefile
tools/testing/selftests/sched_ext/Makefile
tools/testing/selftests/sched_ext/nohz_tick.bpf.c [new file with mode: 0644]
tools/testing/selftests/sched_ext/nohz_tick.c [new file with mode: 0644]
tools/testing/selftests/user_events/abi_test.c
tools/testing/selftests/user_events/perf_test.c
tools/tracing/rtla/Makefile
tools/tracing/rtla/src/common.c
tools/usb/usbip/libsrc/usbip_common.c
tools/usb/usbip/libsrc/usbip_device_driver.c
tools/usb/usbip/libsrc/vhci_driver.c
tools/virtio/asm/percpu_types.h [new file with mode: 0644]
tools/virtio/linux/completion.h [new file with mode: 0644]
tools/virtio/linux/device.h
tools/virtio/linux/dma-mapping.h
tools/virtio/linux/mod_devicetable.h [new file with mode: 0644]
tools/virtio/linux/virtio_features.h [new file with mode: 0644]
virt/kvm/kvm_main.c

index e7e639aeb23cf22d754e61ecd6b9b73ef0693c45..501d858cb43227c6373ce5fdd8374c0a81136380 100644 (file)
--- 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>
@@ -642,7 +643,6 @@ Nicholas Piggin <npiggin@gmail.com> <npiggin@kernel.dk>
 Nicholas Piggin <npiggin@gmail.com> <npiggin@suse.de>
 Nicholas Piggin <npiggin@gmail.com> <nickpiggin@yahoo.com.au>
 Nicholas Piggin <npiggin@gmail.com> <piggin@cyberone.com.au>
-Nick Desaulniers <nick.desaulniers+lkml@gmail.com> <ndesaulniers@google.com>
 Nicolas Ferre <nicolas.ferre@microchip.com> <nicolas.ferre@atmel.com>
 Nicolas Pitre <nico@fluxnic.net> <nicolas.pitre@linaro.org>
 Nicolas Pitre <nico@fluxnic.net> <nico@linaro.org>
@@ -709,6 +709,10 @@ Qi Zheng <qi.zheng@linux.dev> <zhengqi.arch@bytedance.com>
 Quentin Monnet <qmo@kernel.org> <quentin.monnet@netronome.com>
 Quentin Monnet <qmo@kernel.org> <quentin@isovalent.com>
 Quentin Perret <qperret@qperret.net> <quentin.perret@arm.com>
+Radu Rendec <radu@rendec.net> <radu.rendec@ines.ro>
+Radu Rendec <radu@rendec.net> <rrendec@arista.com>
+Radu Rendec <radu@rendec.net> <radu.rendec@gmail.com>
+Radu Rendec <radu@rendec.net> <rrendec@redhat.com>
 Rae Moar <raemoar63@gmail.com> <rmoar@google.com>
 Rafael J. Wysocki <rjw@rjwysocki.net> <rjw@sisk.pl>
 Rajeev Nandan <quic_rajeevny@quicinc.com> <rajeevny@codeaurora.org>
@@ -813,6 +817,7 @@ Simon Wunderlich <sw@simonwunderlich.de> <simon.wunderlich@s2003.tu-chemnitz.de>
 Simon Wunderlich <sw@simonwunderlich.de> <simon.wunderlich@saxnet.de>
 Simon Wunderlich <sw@simonwunderlich.de> <simon@open-mesh.com>
 Simon Wunderlich <sw@simonwunderlich.de> <siwu@hrz.tu-chemnitz.de>
+SJ Park <sj@kernel.org>
 Sricharan Ramabadhran <quic_srichara@quicinc.com> <sricharan@codeaurora.org>
 Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@st.com>
 Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@linaro.org>
@@ -822,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 84793a967a0b2dcb1e54d6c5b5c6f308adf5e158..91c51c14e993adff64c850f4221a4380560c169f 100644 (file)
--- 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/
index 98a8376a83d29cf61437f4065b87a66b3fbca017..991765d84201e1e67d1c81651a182d350c7b9179 100644 (file)
@@ -22,7 +22,7 @@ Description:
 
                Reading this attribute gives the state of the DbC. It
                can be one of the following states: disabled, enabled,
-               initialized, connected or configured.
+               initialized, connected, configured or suspended.
 
 What:          /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
 Date:          March 2023
index b73e6bc28ea5fc707da9ffd3bbc4d6067c2363af..4fdec63a47d4aa6fff8da89aa53cc84b4a7ec9d6 100644 (file)
@@ -1,26 +1,26 @@
 what:          /sys/kernel/mm/damon/
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Interface for Data Access MONitoring (DAMON).  Contains files
                for controlling DAMON.  For more details on DAMON itself,
                please refer to Documentation/admin-guide/mm/damon/index.rst.
 
 What:          /sys/kernel/mm/damon/admin/
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Interface for privileged users of DAMON.  Contains files for
                controlling DAMON that aimed to be used by privileged users.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for controlling each DAMON worker thread (kdamond)
                named '0' to 'N-1' under the kdamonds/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/state
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing 'on' or 'off' to this file makes the kdamond starts or
                stops, respectively.  Reading the file returns the keywords
                based on the current status.  Writing 'commit' to this file
@@ -40,33 +40,33 @@ Description:        Writing 'on' or 'off' to this file makes the kdamond starts or
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/pid
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the pid of the kdamond if it is
                running.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/refresh_ms
 Date:          Jul 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the time interval for
                automatic DAMON status file contents update.  Writing '0'
                disables the update.  Reading this file returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/nr_contexts
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for controlling each DAMON context named '0' to
                'N-1' under the contexts/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/avail_operations
 Date:          Apr 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the available monitoring operations
                sets on the currently running kernel.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/operations
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a keyword for a monitoring operations set ('vaddr' for
                virtual address spaces monitoring, 'fvaddr' for fixed virtual
                address ranges monitoring, and 'paddr' for the physical address
@@ -79,42 +79,42 @@ Description:        Writing a keyword for a monitoring operations set ('vaddr' for
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/addr_unit
 Date:          Aug 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing an integer to this file sets the 'address unit'
                parameter of the given operations set of the context.  Reading
                the file returns the last-written 'address unit' value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/pause
 Date:          Mar 2026
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a boolean keyword to this file sets the 'pause' request
                parameter for the context.  Reading the file returns the
                last-written 'pause' value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/sample_us
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the sampling interval of the
                DAMON context in microseconds as the value.  Reading this file
                returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/aggr_us
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the aggregation interval of
                the DAMON context in microseconds as the value.  Reading this
                file returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/update_us
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the update interval of the
                DAMON context in microseconds as the value.  Reading this file
                returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/access_bp
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the monitoring intervals
                auto-tuning target DAMON-observed access events ratio within
                the given time interval (aggrs in same directory), in bp
@@ -122,7 +122,7 @@ Description:        Writing a value to this file sets the monitoring intervals
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/aggrs
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the time interval to achieve
                the monitoring intervals auto-tuning target DAMON-observed
                access events ratio (access_bp in same directory) within.
@@ -130,14 +130,14 @@ Description:      Writing a value to this file sets the time interval to achieve
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/min_sample_us
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the minimum value of
                auto-tuned sampling interval in microseconds.  Reading this
                file returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/max_sample_us
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the maximum value of
                auto-tuned sampling interval in microseconds.  Reading this
                file returns the value.
@@ -145,42 +145,42 @@ Description:      Writing a value to this file sets the maximum value of
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/nr_regions/min
 
 WDate:         Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the minimum number of
                monitoring regions of the DAMON context as the value.  Reading
                this file returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/nr_regions/max
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the maximum number of
                monitoring regions of the DAMON context as the value.  Reading
                this file returns the value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/nr_targets
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for controlling each DAMON target of the context
                named '0' to 'N-1' under the contexts/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/pid_target
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the pid of
                the target process if the context is for virtual address spaces
                monitoring, respectively.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/obsolete_target
 Date:          Oct 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                obsoleteness of the matching parameters commit destination
                target.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/nr_regions
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for setting each DAMON target memory region of the
                context named '0' to 'N-1' under the regions/ directory.  In
@@ -190,181 +190,181 @@ Description:    Writing a number 'N' to this file creates the number of
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/<R>/start
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the start
                address of the monitoring region.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/<R>/end
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the end
                address of the monitoring region.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/nr_schemes
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for controlling each DAMON-based operation scheme
                of the context named '0' to 'N-1' under the schemes/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/action
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the action
                of the scheme.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/target_nid
 Date:          Jun 2024
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Action's target NUMA node id.  Supported by only relevant
                actions.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/apply_interval_us
 Date:          Sep 2023
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a value to this file sets the action apply interval of
                the scheme in microseconds.  Reading this file returns the
                value.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/sz/min
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the minimum
                size of the scheme's target regions in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/sz/max
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the maximum
                size of the scheme's target regions in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/nr_accesses/min
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the manimum
                'nr_accesses' of the scheme's target regions.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/nr_accesses/max
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the maximum
                'nr_accesses' of the scheme's target regions.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/age/min
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the minimum
                'age' of the scheme's target regions.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/age/max
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the maximum
                'age' of the scheme's target regions.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/ms
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the time
                quota of the scheme in milliseconds.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/bytes
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the size
                quota of the scheme in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/effective_bytes
 Date:          Feb 2024
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading from this file gets the effective size quota of the
                scheme in bytes, which adjusted for the time quota and goals.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/reset_interval_ms
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the quotas
                charge reset interval of the scheme in milliseconds.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/nr_goals
 Date:          Nov 2023
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for setting automatic tuning of the scheme's
                aggressiveness named '0' to 'N-1' under the goals/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/target_metric
 Date:          Feb 2024
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the quota
                auto-tuning goal metric.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/target_value
 Date:          Nov 2023
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the target
                value of the goal metric.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/current_value
 Date:          Nov 2023
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the current
                value of the goal metric.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/nid
 Date:          Apr 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the nid
                parameter of the goal.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/path
 Date:          Oct 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the path
                parameter of the goal.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goal_tuner
 Date:          Mar 2026
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                goal-based effective quota auto-tuning algorithm to use.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/fail_charge_num
 Date:          Mar 2026
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                action-failed memory quota charging ratio numerator.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/fail_charge_denom
 Date:          Mar 2026
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                action-failed memory quota charging ratio denominator.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/sz_permil
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                under-quota limit regions prioritization weight for 'size' in
                permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/nr_accesses_permil
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                under-quota limit regions prioritization weight for
                'nr_accesses' in permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/age_permil
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the
                under-quota limit regions prioritization weight for 'age' in
                permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/metric
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the metric
                of the watermarks for the scheme.  The writable/readable
                keywords for this file are 'none' for disabling the watermarks
@@ -373,44 +373,44 @@ Description:      Writing to and reading from this file sets and gets the metric
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/interval_us
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the metric
                check interval of the watermarks for the scheme in
                microseconds.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/high
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the high
                watermark of the scheme in permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/mid
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the mid
                watermark of the scheme in permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/low
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the low
                watermark of the scheme in permil.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Directory for DAMON core layer-handled DAMOS filters.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/nr_filters
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for setting filters of the scheme named '0' to
                'N-1' under the core_filters/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/type
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the type of
                the memory of the interest.  'anon' for anonymous pages,
                'memcg' for specific memory cgroup, 'young' for young pages,
@@ -419,62 +419,62 @@ Description:      Writing to and reading from this file sets and gets the type of
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/memcg_path
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'memcg' is written to the 'type' file, writing to and
                reading from this file sets and gets the path to the memory
                cgroup of the interest.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/addr_start
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'addr' is written to the 'type' file, writing to or reading
                from this file sets or gets the start address of the address
                range for the filter.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/addr_end
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'addr' is written to the 'type' file, writing to or reading
                from this file sets or gets the end address of the address
                range for the filter.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/min
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'hugepage_size' is written to the 'type' file, writing to
                or reading from this file sets or gets the minimum size of the
                hugepage for the filter.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/max
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'hugepage_size' is written to the 'type' file, writing to
                or reading from this file sets or gets the maximum size of the
                hugepage for the filter.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/damon_target_idx
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   If 'target' is written to the 'type' file, writing to or
                reading from this file sets or gets the index of the DAMON
                monitoring target of the interest.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/matching
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing 'Y' or 'N' to this file sets whether the filter is for
                the memory of the 'type', or all except the 'type'.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/allow
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing 'Y' or 'N' to this file sets whether to allow or reject
                applying the scheme's action to the memory that satisfies the
                'type' and the 'matching' of the directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/ops_filters
 Date:          Feb 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Directory for DAMON operations set layer-handled DAMOS filters.
                Files under this directory works same to those of
                /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters
@@ -482,7 +482,7 @@ Description:        Directory for DAMON operations set layer-handled DAMOS filters.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/filters
 Date:          Dec 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Directory for DAMOS filters.  Files under this directory works
                same to those of
                /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/{core,ops}_filters
@@ -491,14 +491,14 @@ Description:      Directory for DAMOS filters.  Files under this directory works
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/nr_dests
 Date:          Jul 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number 'N' to this file creates the number of
                directories for setting action destinations of the scheme named
                '0' to 'N-1' under the dests/ directory.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/<D>/id
 Date:          Jul 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the id of
                the DAMOS action destination.  For DAMOS_MIGRATE_{HOT,COLD}
                actions, the destination node's node id can be written and
@@ -506,98 +506,98 @@ Description:      Writing to and reading from this file sets and gets the id of
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/<D>/weight
 Date:          Jul 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing to and reading from this file sets and gets the weight
                of the DAMOS action destination to select as the destination of
                each action among the destinations.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_tried
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the number of regions that the action
                of the scheme has tried to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_tried
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the total size of regions that the
                action of the scheme has tried to be applied in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_applied
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the number of regions that the action
                of the scheme has successfully applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_applied
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the total size of regions that the
                action of the scheme has successfully applied in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_ops_filter_passed
 Date:          Dec 2024
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the total size of memory that passed
                DAMON operations layer-handled filters of the scheme in bytes.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/qt_exceeds
 Date:          Mar 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the number of the exceed events of
                the scheme's quotas.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_snapshots
 Date:          Dec 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the total number of DAMON snapshots
                that the scheme has tried to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/max_nr_snapshots
 Date:          Dec 2025
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Writing a number to this file sets the upper limit of
                nr_snapshots that deactivates the scheme when the limit is
                reached or exceeded.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/total_bytes
 Date:          Jul 2023
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the total amount of memory that
                corresponding DAMON-based Operation Scheme's action has tried
                to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/start
 Date:          Oct 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the start address of a memory region
                that corresponding DAMON-based Operation Scheme's action has
                tried to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/end
 Date:          Oct 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the end address of a memory region
                that corresponding DAMON-based Operation Scheme's action has
                tried to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/nr_accesses
 Date:          Oct 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the 'nr_accesses' of a memory region
                that corresponding DAMON-based Operation Scheme's action has
                tried to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/age
 Date:          Oct 2022
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the 'age' of a memory region that
                corresponding DAMON-based Operation Scheme's action has tried
                to be applied.
 
 What:          /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/sz_filter_passed
 Date:          Dec 2024
-Contact:       SeongJae Park <sj@kernel.org>
+Contact:       SJ Park <sj@kernel.org>
 Description:   Reading this file returns the size of the memory in the region
                that passed DAMON operations layer-handled filters of the
                scheme in bytes.
index e69369b7252ea390d79c21294565709cd126296e..abddf34d266752575e6b4a6934cdc6c0dcf4d293 100644 (file)
@@ -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
index 993446ab66d0fbc4f801d8e0bd47ab720af50618..14b8c571c0d14251ac8befd6f67f2b42753ed6ca 100644 (file)
@@ -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
index 8fb438bf7781629bf02d6d7e436230dc491dd73c..7c3379b704aaf6419b0daa6a77cf772c9786d77f 100644 (file)
@@ -47,11 +47,12 @@ ever have can be described at boot. There are no power-domain considerations
 as such devices are emulated.
 
 CPU Hotplug on virtual systems is supported. It is distinct from physical
-CPU Hotplug as all resources are described as ``present``, but CPUs may be
-marked as disabled by firmware. Only the CPU's online/offline behaviour is
-influenced by firmware. An example is where a virtual machine boots with a
-single CPU, and additional CPUs are added once a cloud orchestrator deploys
-the workload.
+CPU Hotplug as all vCPU resources are statically described in the firmware
+configuration tables (e.g. MADT), meaning their maximum possible count is
+known at boot. However, vCPUs that are not enabled at boot are not marked
+as ``present`` by the kernel until they are hotplugged. An example is where
+a virtual machine boots with a single CPU, and additional CPUs are added
+once a cloud orchestrator deploys the workload.
 
 For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware.
 
@@ -60,16 +61,19 @@ brought online. Firmware can enforce its policy via PSCI's return codes. e.g.
 ``DENIED``.
 
 The ACPI tables must describe all the resources of the virtual machine. CPUs
-that firmware wishes to disable either from boot (or later) should not be
-``enabled`` in the MADT GICC structures, but should have the ``online capable``
-bit set, to indicate they can be enabled later. The boot CPU must be marked as
-``enabled``.  The 'always on' GICR structure must be used to describe the
-redistributors.
+that are hot-pluggable must have the ``online capable`` bit set and the
+``enabled`` bit cleared in the MADT GICC structures to indicate they can be
+enabled later. The boot CPU must be marked as ``enabled`` with its
+``online capable`` bit cleared. The 'always on' GICR structure must be used
+to describe the redistributors.
 
 CPUs described as ``online capable`` but not ``enabled`` can be set to enabled
 by the DSDT's Processor object's _STA method. On virtual systems the _STA method
-must always report the CPU as ``present``. Changes to the firmware policy can
-be notified to the OS via device-check or eject-request.
+must always set the ``ACPI_STA_DEVICE_PRESENT`` bit, while toggling the
+``ACPI_STA_DEVICE_ENABLED`` bit to reflect its plug status. The kernel will
+then dynamically mark the vCPU as ``present`` within the OS when the
+``ACPI_STA_DEVICE_ENABLED`` bit becomes set during hot-add. Changes to the
+firmware policy can be notified to the OS via device-check or eject-request.
 
 CPUs described as ``enabled`` in the static table, should not have their _STA
 modified dynamically by firmware. Soft-restart features such as kexec will
index c420a8349bc6811573051154bc9c64617f3d7464..d9928641deb9931a1962cd3770a13441076d4216 100644 (file)
@@ -82,121 +82,121 @@ The following keys are defined:
     version 1.0 of the RISC-V Vector extension manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBA`: The Zba address generation extension is
-       supported, as defined in version 1.0 of the Bit-Manipulation ISA
-       extensions.
+    supported, as defined in version 1.0 of the Bit-Manipulation ISA
+    extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBB`: The Zbb extension is supported, as defined
-       in version 1.0 of the Bit-Manipulation ISA extensions.
+    in version 1.0 of the Bit-Manipulation ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBS`: The Zbs extension is supported, as defined
-       in version 1.0 of the Bit-Manipulation ISA extensions.
+    in version 1.0 of the Bit-Manipulation ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZICBOZ`: The Zicboz extension is supported, as
-       ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
+    ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBC` The Zbc extension is supported, as defined
-       in version 1.0 of the Bit-Manipulation ISA extensions.
+    in version 1.0 of the Bit-Manipulation ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBKB` The Zbkb extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBKC` The Zbkc extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZBKX` The Zbkx extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKND` The Zknd extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKNE` The Zkne extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKNH` The Zknh extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKSED` The Zksed extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKSH` The Zksh extension is supported, as
-       defined in version 1.0 of the Scalar Crypto ISA extensions.
+    defined in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZKT` The Zkt extension is supported, as defined
-       in version 1.0 of the Scalar Crypto ISA extensions.
+    in version 1.0 of the Scalar Crypto ISA extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVBB`: The Zvbb extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVBC`: The Zvbc extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKB`: The Zvkb extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKG`: The Zvkg extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKNED`: The Zvkned extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKNHA`: The Zvknha extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKNHB`: The Zvknhb extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKSED`: The Zvksed extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKSH`: The Zvksh extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVKT`: The Zvkt extension is supported as
-       defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
+    defined in version 1.0 of the RISC-V Cryptography Extensions Volume II.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZFH`: The Zfh extension version 1.0 is supported
-       as defined in the RISC-V ISA manual.
+    as defined in the RISC-V ISA manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZFHMIN`: The Zfhmin extension version 1.0 is
-       supported as defined in the RISC-V ISA manual.
+    supported as defined in the RISC-V ISA manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZIHINTNTL`: The Zihintntl extension version 1.0
-       is supported as defined in the RISC-V ISA manual.
+    is supported as defined in the RISC-V ISA manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVFH`: The Zvfh extension is supported as
-       defined in the RISC-V Vector manual starting from commit e2ccd0548d6c
-       ("Remove draft warnings from Zvfh[min]").
+    defined in the RISC-V Vector manual starting from commit e2ccd0548d6c
+    ("Remove draft warnings from Zvfh[min]").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVFHMIN`: The Zvfhmin extension is supported as
-       defined in the RISC-V Vector manual starting from commit e2ccd0548d6c
-       ("Remove draft warnings from Zvfh[min]").
+    defined in the RISC-V Vector manual starting from commit e2ccd0548d6c
+    ("Remove draft warnings from Zvfh[min]").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZFA`: The Zfa extension is supported as
-       defined in the RISC-V ISA manual starting from commit 056b6ff467c7
-       ("Zfa is ratified").
+    defined in the RISC-V ISA manual starting from commit 056b6ff467c7
+    ("Zfa is ratified").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZTSO`: The Ztso extension is supported as
-       defined in the RISC-V ISA manual starting from commit 5618fb5a216b
-       ("Ztso is now ratified.")
+    defined in the RISC-V ISA manual starting from commit 5618fb5a216b
+    ("Ztso is now ratified.")
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZACAS`: The Zacas extension is supported as
-       defined in the Atomic Compare-and-Swap (CAS) instructions manual starting
-       from commit 5059e0ca641c ("update to ratified").
+    defined in the Atomic Compare-and-Swap (CAS) instructions manual starting
+    from commit 5059e0ca641c ("update to ratified").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZICNTR`: The Zicntr extension version 2.0
-       is supported as defined in the RISC-V ISA manual.
+    is supported as defined in the RISC-V ISA manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZICOND`: The Zicond extension is supported as
-       defined in the RISC-V Integer Conditional (Zicond) operations extension
-       manual starting from commit 95cf1f9 ("Add changes requested by Ved
-       during signoff")
+    defined in the RISC-V Integer Conditional (Zicond) operations extension
+    manual starting from commit 95cf1f9 ("Add changes requested by Ved
+    during signoff")
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZIHINTPAUSE`: The Zihintpause extension is
-       supported as defined in the RISC-V ISA manual starting from commit
-       d8ab5c78c207 ("Zihintpause is ratified").
+    supported as defined in the RISC-V ISA manual starting from commit
+    d8ab5c78c207 ("Zihintpause is ratified").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZIHPM`: The Zihpm extension version 2.0
-       is supported as defined in the RISC-V ISA manual.
+    is supported as defined in the RISC-V ISA manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVE32X`: The Vector sub-extension Zve32x is
     supported, as defined by version 1.0 of the RISC-V Vector extension manual.
@@ -214,84 +214,89 @@ The following keys are defined:
     supported, as defined by version 1.0 of the RISC-V Vector extension manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZIMOP`: The Zimop May-Be-Operations extension is
-       supported as defined in the RISC-V ISA manual starting from commit
-       58220614a5f ("Zimop is ratified/1.0").
+    supported as defined in the RISC-V ISA manual starting from commit
+    58220614a5f ("Zimop is ratified/1.0").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCA`: The Zca extension part of Zc* standard
-       extensions for code size reduction, as ratified in commit 8be3419c1c0
-       ("Zcf doesn't exist on RV64 as it contains no instructions") of
-       riscv-code-size-reduction.
+    extensions for code size reduction, as ratified in commit 8be3419c1c0
+    ("Zcf doesn't exist on RV64 as it contains no instructions") of
+    riscv-code-size-reduction.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCB`: The Zcb extension part of Zc* standard
-       extensions for code size reduction, as ratified in commit 8be3419c1c0
-       ("Zcf doesn't exist on RV64 as it contains no instructions") of
-       riscv-code-size-reduction.
+    extensions for code size reduction, as ratified in commit 8be3419c1c0
+    ("Zcf doesn't exist on RV64 as it contains no instructions") of
+    riscv-code-size-reduction.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCD`: The Zcd extension part of Zc* standard
-       extensions for code size reduction, as ratified in commit 8be3419c1c0
-       ("Zcf doesn't exist on RV64 as it contains no instructions") of
-       riscv-code-size-reduction.
+    extensions for code size reduction, as ratified in commit 8be3419c1c0
+    ("Zcf doesn't exist on RV64 as it contains no instructions") of
+    riscv-code-size-reduction.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCF`: The Zcf extension part of Zc* standard
-       extensions for code size reduction, as ratified in commit 8be3419c1c0
-       ("Zcf doesn't exist on RV64 as it contains no instructions") of
-       riscv-code-size-reduction.
+    extensions for code size reduction, as ratified in commit 8be3419c1c0
+    ("Zcf doesn't exist on RV64 as it contains no instructions") of
+    riscv-code-size-reduction.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCMOP`: The Zcmop May-Be-Operations extension is
-       supported as defined in the RISC-V ISA manual starting from commit
-       c732a4f39a4 ("Zcmop is ratified/1.0").
+    supported as defined in the RISC-V ISA manual starting from commit
+    c732a4f39a4 ("Zcmop is ratified/1.0").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZAWRS`: The Zawrs extension is supported as
-       ratified in commit 98918c844281 ("Merge pull request #1217 from
-       riscv/zawrs") of riscv-isa-manual.
+    ratified in commit 98918c844281 ("Merge pull request #1217 from
+    riscv/zawrs") of riscv-isa-manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZAAMO`: The Zaamo extension is supported as
-       defined in the in the RISC-V ISA manual starting from commit e87412e621f1
-       ("integrate Zaamo and Zalrsc text (#1304)").
+    defined in the in the RISC-V ISA manual starting from commit e87412e621f1
+    ("integrate Zaamo and Zalrsc text (#1304)").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZALASR`: The Zalasr extension is supported as
-       frozen at commit 194f0094 ("Version 0.9 for freeze") of riscv-zalasr.
+    frozen at commit 194f0094 ("Version 0.9 for freeze") of riscv-zalasr.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZALRSC`: The Zalrsc extension is supported as
-       defined in the in the RISC-V ISA manual starting from commit e87412e621f1
-       ("integrate Zaamo and Zalrsc text (#1304)").
+    defined in the in the RISC-V ISA manual starting from commit e87412e621f1
+    ("integrate Zaamo and Zalrsc text (#1304)").
 
   * :c:macro:`RISCV_HWPROBE_EXT_SUPM`: The Supm extension is supported as
-       defined in version 1.0 of the RISC-V Pointer Masking extensions.
+    defined in version 1.0 of the RISC-V Pointer Masking extensions.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZFBFMIN`: The Zfbfmin extension is supported as
-       defined in the RISC-V ISA manual starting from commit 4dc23d6229de
-       ("Added Chapter title to BF16").
+    defined in the RISC-V ISA manual starting from commit 4dc23d6229de
+    ("Added Chapter title to BF16").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVFBFMIN`: The Zvfbfmin extension is supported as
-       defined in the RISC-V ISA manual starting from commit 4dc23d6229de
-       ("Added Chapter title to BF16").
+    defined in the RISC-V ISA manual starting from commit 4dc23d6229de
+    ("Added Chapter title to BF16").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZVFBFWMA`: The Zvfbfwma extension is supported as
-       defined in the RISC-V ISA manual starting from commit 4dc23d6229de
-       ("Added Chapter title to BF16").
+    defined in the RISC-V ISA manual starting from commit 4dc23d6229de
+    ("Added Chapter title to BF16").
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZICBOM`: The Zicbom extension is supported, as
-       ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
+    ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZABHA`: The Zabha extension is supported as
-       ratified in commit 49f49c842ff9 ("Update to Rafified state") of
-       riscv-zabha.
+    ratified in commit 49f49c842ff9 ("Update to Rafified state") of
+    riscv-zabha.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZICBOP`: The Zicbop extension is supported, as
-       ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
+    ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZILSD`: The Zilsd extension is supported as
-       defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
-       load/store pair for RV32 with the main manual") of the riscv-isa-manual.
+    defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
+    load/store pair for RV32 with the main manual") of the riscv-isa-manual.
 
   * :c:macro:`RISCV_HWPROBE_EXT_ZCLSD`: The Zclsd extension is supported as
-       defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
-       load/store pair for RV32 with the main manual") of the riscv-isa-manual.
+    defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
+    load/store pair for RV32 with the main manual") of the riscv-isa-manual.
+
+  * :c:macro:`RISCV_HWPROBE_EXT_ZICFILP`: The Zicfilp extension is supported,
+    as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI)
+    extensions specification, ratified in commit 302a2d45c243
+    ("Update build-pdf.yml") of riscv-cfi.
 
 * :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: Deprecated.  Returns similar values to
-     :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was
-     mistakenly classified as a bitmask rather than a value.
+  :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was
+  mistakenly classified as a bitmask rather than a value.
 
 * :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`: An enum value describing
   the performance of misaligned scalar native word accesses on the selected set
@@ -326,7 +331,7 @@ The following keys are defined:
 * :c:macro:`RISCV_HWPROBE_KEY_TIME_CSR_FREQ`: Frequency (in Hz) of `time CSR`.
 
 * :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF`: An enum value describing the
-     performance of misaligned vector accesses on the selected set of processors.
+  performance of misaligned vector accesses on the selected set of processors.
 
   * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN`: The performance of misaligned
     vector accesses is unknown.
@@ -348,7 +353,7 @@ The following keys are defined:
   * MIPS
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XMIPSEXECTL`: The xmipsexectl vendor
-        extension is supported in the MIPS ISA extensions spec.
+      extension is supported in the MIPS ISA extensions spec.
 
 * :c:macro:`RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0`: A bitmask containing the
   thead vendor extensions that are compatible with the
@@ -357,8 +362,8 @@ The following keys are defined:
   * T-HEAD
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR`: The xtheadvector vendor
-        extension is supported in the T-Head ISA extensions spec starting from
-       commit a18c801634 ("Add T-Head VECTOR vendor extension. ").
+      extension is supported in the T-Head ISA extensions spec starting from
+      commit a18c801634 ("Add T-Head VECTOR vendor extension. ").
 
 * :c:macro:`RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE`: An unsigned int which
   represents the size of the Zicbom block in bytes.
@@ -370,20 +375,20 @@ The following keys are defined:
   * SIFIVE
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVQMACCDOD`: The Xsfqmaccdod vendor
-        extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication
-       Extensions Specification.
+      extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication
+      Extensions Specification.
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVQMACCQOQ`: The Xsfqmaccqoq vendor
-        extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication
-       Instruction Extensions Specification.
+      extension is supported in version 1.1 of SiFive Int8 Matrix Multiplication
+      Instruction Extensions Specification.
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVFNRCLIPXFQF`: The Xsfvfnrclipxfqf
-        vendor extension is supported in version 1.0 of SiFive FP32-to-int8 Ranged
-       Clip Instructions Extensions Specification.
+      vendor extension is supported in version 1.0 of SiFive FP32-to-int8 Ranged
+      Clip Instructions Extensions Specification.
 
     * :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XSFVFWMACCQQQ`: The Xsfvfwmaccqqq
-        vendor extension is supported in version 1.0 of Matrix Multiply Accumulate
-       Instruction Extensions Specification.
+      vendor extension is supported in version 1.0 of Matrix Multiply Accumulate
+      Instruction Extensions Specification.
 
 * :c:macro:`RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE`: An unsigned int which
   represents the size of the Zicbop block in bytes.
@@ -391,3 +396,8 @@ The following keys are defined:
 * :c:macro:`RISCV_HWPROBE_KEY_IMA_EXT_1`: A bitmask containing additional
   extensions that are compatible with the
   :c:macro:`RISCV_HWPROBE_BASE_BEHAVIOR_IMA`: base system behavior.
+
+  * :c:macro:`RISCV_HWPROBE_EXT_ZICFISS`: The Zicfiss extension is supported,
+    as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI)
+    extensions specification, ratified in commit 302a2d45c243
+    ("Update build-pdf.yml") of riscv-cfi.
index dd5ce2f8b5151c16fab657d37de6abb94f072813..fd85e27fab8d3b7eebaaf8baa89825ac875fa599 100644 (file)
@@ -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
index 4248e6fdc9527cfb645926b159c80aa66a37c826..fa4c54236af6e5aa7f7de06739f49f6e20b9570a 100644 (file)
@@ -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
index e80d59fa51b6aaf9a31dcbcca54e22ec742c809c..2a6dc793f0de877173aa7a82fb915038cbc902fb 100644 (file)
@@ -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
index a1557d45b0e5a779d654156ebc970820c8bc7cb0..0733e603d2295c19faa86b4bf2138120b38d4e65 100644 (file)
@@ -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.
index eb3755bdfdf7e001602c3eb870898275085df3e6..a4d032224dcee26329123c1923929ef27de3efe8 100644 (file)
@@ -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:
index 2b0a8a93bb21445e40ec106bdff71f4daf9563f6..5ffc40d599c0225818f8c3020a4a2d108449e437 100644 (file)
@@ -457,6 +457,13 @@ properties:
             merged in the riscv-isa-manual by commit dbc79cf28a2 ("Initial seed
             of zc.adoc to src tree.").
 
+        - const: zclsd
+          description:
+            The Zclsd extension implements the compressed (16-bit) version of the
+            Load/Store Pair for RV32. As with Zilsd, this extension was ratified
+            in commit f88abf1 ("Integrating load/store pair for RV32 with the
+            main manual") of riscv-isa-manual.
+
         - const: zcmop
           description:
             The standard Zcmop extension version 1.0, as ratified in commit
@@ -487,6 +494,22 @@ properties:
             in commit 64074bc ("Update version numbers for Zfh/Zfinx") of
             riscv-isa-manual.
 
+        - const: zicbom
+          description:
+            The standard Zicbom extension for base cache management operations as
+            ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
+
+        - const: zicbop
+          description:
+            The standard Zicbop extension for cache-block prefetch instructions
+            as ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of
+            riscv-CMOs.
+
+        - const: zicboz
+          description:
+            The standard Zicboz extension for cache-block zeroing as ratified
+            in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
+
         - const: ziccamoa
           description:
             The standard Ziccamoa extension for main memory (cacheability and
@@ -514,6 +537,66 @@ properties:
             guarantee on LR/SC sequences, as ratified in commit b1d806605f87
             ("Updated to ratified state.") of the riscv profiles specification.
 
+        - const: zicfilp
+          description: |
+            The standard Zicfilp extension for enforcing forward edge
+            control-flow integrity as ratified in commit 3f8e450 ("merge
+            pull request #227 from ved-rivos/0709") of riscv-cfi
+            github repo.
+
+        - const: zicfiss
+          description: |
+            The standard Zicfiss extension for enforcing backward edge
+            control-flow integrity as ratified in commit 3f8e450 ("merge
+            pull request #227 from ved-rivos/0709") of riscv-cfi
+            github repo.
+
+        - const: zicntr
+          description:
+            The standard Zicntr extension for base counters and timers, as
+            ratified in the 20191213 version of the unprivileged ISA
+            specification.
+
+        - const: zicond
+          description:
+            The standard Zicond extension for conditional arithmetic and
+            conditional-select/move operations as ratified in commit 95cf1f9
+            ("Add changes requested by Ved during signoff") of riscv-zicond.
+
+        - const: zicsr
+          description: |
+            The standard Zicsr extension for control and status register
+            instructions, as ratified in the 20191213 version of the
+            unprivileged ISA specification.
+
+            This does not include Chapter 10, "Counters", which documents
+            special case read-only CSRs, that were moved into the Zicntr and
+            Zihpm extensions after the ratification of the 20191213 version of
+            the unprivileged specification.
+
+        - const: zifencei
+          description:
+            The standard Zifencei extension for instruction-fetch fence, as
+            ratified in the 20191213 version of the unprivileged ISA
+            specification.
+
+        - const: zihintntl
+          description:
+            The standard Zihintntl extension for non-temporal locality hints, as
+            ratified in commit 0dc91f5 ("Zihintntl is ratified") of the
+            riscv-isa-manual.
+
+        - const: zihintpause
+          description:
+            The standard Zihintpause extension for pause hints, as ratified in
+            commit d8ab5c7 ("Zihintpause is ratified") of the riscv-isa-manual.
+
+        - const: zihpm
+          description:
+            The standard Zihpm extension for hardware performance counters, as
+            ratified in the 20191213 version of the unprivileged ISA
+            specification.
+
         - const: zilsd
           description:
             The standard Zilsd extension which provides support for aligned
@@ -521,12 +604,10 @@ properties:
             encodings, as ratified in commit f88abf1 ("Integrating
             load/store pair for RV32 with the main manual") of riscv-isa-manual.
 
-        - const: zclsd
+        - const: zimop
           description:
-            The Zclsd extension implements the compressed (16-bit) version of the
-            Load/Store Pair for RV32. As with Zilsd, this extension was ratified
-            in commit f88abf1 ("Integrating load/store pair for RV32 with the
-            main manual") of riscv-isa-manual.
+            The standard Zimop extension version 1.0, as ratified in commit
+            58220614a5f ("Zimop is ratified/1.0") of the riscv-isa-manual.
 
         - const: zk
           description:
@@ -590,87 +671,6 @@ properties:
             in version 1.0 of RISC-V Cryptography Extensions Volume I
             specification.
 
-        - const: zicbom
-          description:
-            The standard Zicbom extension for base cache management operations as
-            ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
-
-        - const: zicbop
-          description:
-            The standard Zicbop extension for cache-block prefetch instructions
-            as ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of
-            riscv-CMOs.
-
-        - const: zicboz
-          description:
-            The standard Zicboz extension for cache-block zeroing as ratified
-            in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
-
-        - const: zicfilp
-          description: |
-            The standard Zicfilp extension for enforcing forward edge
-            control-flow integrity as ratified in commit 3f8e450 ("merge
-            pull request #227 from ved-rivos/0709") of riscv-cfi
-            github repo.
-
-        - const: zicfiss
-          description: |
-            The standard Zicfiss extension for enforcing backward edge
-            control-flow integrity as ratified in commit 3f8e450 ("merge
-            pull request #227 from ved-rivos/0709") of riscv-cfi
-            github repo.
-
-        - const: zicntr
-          description:
-            The standard Zicntr extension for base counters and timers, as
-            ratified in the 20191213 version of the unprivileged ISA
-            specification.
-
-        - const: zicond
-          description:
-            The standard Zicond extension for conditional arithmetic and
-            conditional-select/move operations as ratified in commit 95cf1f9
-            ("Add changes requested by Ved during signoff") of riscv-zicond.
-
-        - const: zicsr
-          description: |
-            The standard Zicsr extension for control and status register
-            instructions, as ratified in the 20191213 version of the
-            unprivileged ISA specification.
-
-            This does not include Chapter 10, "Counters", which documents
-            special case read-only CSRs, that were moved into the Zicntr and
-            Zihpm extensions after the ratification of the 20191213 version of
-            the unprivileged specification.
-
-        - const: zifencei
-          description:
-            The standard Zifencei extension for instruction-fetch fence, as
-            ratified in the 20191213 version of the unprivileged ISA
-            specification.
-
-        - const: zihintpause
-          description:
-            The standard Zihintpause extension for pause hints, as ratified in
-            commit d8ab5c7 ("Zihintpause is ratified") of the riscv-isa-manual.
-
-        - const: zihintntl
-          description:
-            The standard Zihintntl extension for non-temporal locality hints, as
-            ratified in commit 0dc91f5 ("Zihintntl is ratified") of the
-            riscv-isa-manual.
-
-        - const: zihpm
-          description:
-            The standard Zihpm extension for hardware performance counters, as
-            ratified in the 20191213 version of the unprivileged ISA
-            specification.
-
-        - const: zimop
-          description:
-            The standard Zimop extension version 1.0, as ratified in commit
-            58220614a5f ("Zimop is ratified/1.0") of the riscv-isa-manual.
-
         - const: ztso
           description:
             The standard Ztso extension for total store ordering, as ratified
@@ -809,18 +809,18 @@ properties:
             instructions, as ratified in commit 56ed795 ("Update
             riscv-crypto-spec-vector.adoc") of riscv-crypto.
 
-        - const: zvksh
-          description: |
-            The standard Zvksh extension for ShangMi suite: SM3 secure hash
-            instructions, as ratified in commit 56ed795 ("Update
-            riscv-crypto-spec-vector.adoc") of riscv-crypto.
-
         - const: zvksg
           description:
             The standard Zvksg extension for ShangMi algorithm suite with GCM
             instructions, as ratified in commit 56ed795 ("Update
             riscv-crypto-spec-vector.adoc") of riscv-crypto.
 
+        - const: zvksh
+          description: |
+            The standard Zvksh extension for ShangMi suite: SM3 secure hash
+            instructions, as ratified in commit 56ed795 ("Update
+            riscv-crypto-spec-vector.adoc") of riscv-crypto.
+
         - const: zvkt
           description:
             The standard Zvkt extension for vector data-independent execution
index 2eed2277511f8950601976aff815ce35caffe945..4988e7ed6e34b01f2b661986899d351faa2db46a 100644 (file)
@@ -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
index e5e65e226a02df860d81d84d749f2bb72f699f42..d8682ff2e82c599f09a927e2d4b7a93bff056156 100644 (file)
@@ -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
index 5c42b2b323ee415ac26cf60fc94edd7587cfe2e2..aea31fbdad376ac812a58c7e81f9873aeed38b47 100644 (file)
@@ -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
index d5f22b5cf0210b8484c2d08343dfca9b0d7f8d02..9fedd80532e27675e8937e5a83d2741e3c25d760 100644 (file)
@@ -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
index 15f38622b98b904c4107f4800cd1a4159191fda9..dae440ecab5903e972887229372ec7335e801944 100644 (file)
@@ -23,6 +23,7 @@ properties:
           - const: qcom,sdm845-sndcard
       - items:
           - enum:
+              - qcom,eliza-sndcard
               - qcom,kaanapali-sndcard
               - qcom,sm8550-sndcard
               - qcom,sm8650-sndcard
index 34e00848e0dace14423b3babf1c86840f60f053f..d07f16c3c7b82a414a816d063853cd5c8d51c95e 100644 (file)
@@ -308,7 +308,7 @@ an involved disclosed party. The current ambassadors list:
 
   Google       Kees Cook <keescook@chromium.org>
 
-  LLVM         Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
+  LLVM         Nick Desaulniers <ndesaulniers@google.com>
   ============= ========================================================
 
 If you want your organization to be added to the ambassadors list, please
index 4b1ffd03f5165868afbab1b053267b12ac1f9e52..2771ea4cc14af98bb6f452191f6bd73ee4679450 100644 (file)
@@ -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``.
 
index 9d444b9c46d39c801cc761400801b9eca88bbae1..7d4d694967c7369141b41186343c0443e28d41f2 100644 (file)
@@ -287,7 +287,7 @@ revelada involucrada. La lista de embajadores actuales:
 
   Google       Kees Cook <keescook@chromium.org>
 
-  LLVM         Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
+  LLVM         Nick Desaulniers <ndesaulniers@google.com>
   ============= ========================================================
 
 Si quiere que su organización se añada a la lista de embajadores, por
index 4a8b0fd665ce2447c3e89784b142d998f7384b95..1ab8736850ea37ef0b6a91a13bcd011f4f0a9818 100644 (file)
@@ -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*
@@ -6342,7 +6343,7 @@ F:        .clang-format
 
 CLANG/LLVM BUILD SUPPORT
 M:     Nathan Chancellor <nathan@kernel.org>
-R:     Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
+R:     Nick Desaulniers <ndesaulniers@google.com>
 R:     Bill Wendling <morbo@google.com>
 R:     Justin Stitt <justinstitt@google.com>
 L:     llvm@lists.linux.dev
@@ -7124,7 +7125,7 @@ W:        https://docs.dasharo.com/
 F:     drivers/platform/x86/dasharo-acpi.c
 
 DAMON
-M:     SeongJae Park <sj@kernel.org>
+M:     SJ Park <sj@kernel.org>
 L:     damon@lists.linux.dev
 L:     linux-mm@kvack.org
 S:     Maintained
@@ -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>
@@ -17204,6 +17206,7 @@ R:      Liam R. Howlett <liam@infradead.org>
 R:     Vlastimil Babka <vbabka@kernel.org>
 R:     Harry Yoo <harry@kernel.org>
 R:     Jann Horn <jannh@google.com>
+R:     Lance Yang <lance.yang@linux.dev>
 L:     linux-mm@kvack.org
 S:     Maintained
 F:     include/linux/rmap.h
@@ -17254,6 +17257,7 @@ R:      Ryan Roberts <ryan.roberts@arm.com>
 R:     Dev Jain <dev.jain@arm.com>
 R:     Barry Song <baohua@kernel.org>
 R:     Lance Yang <lance.yang@linux.dev>
+R:     Usama Arif <usama.arif@linux.dev>
 L:     linux-mm@kvack.org
 S:     Maintained
 W:     http://www.linux-mm.org
@@ -20194,7 +20198,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
@@ -20405,7 +20409,7 @@ F:      kernel/padata.c
 
 PAGE CACHE
 M:     Matthew Wilcox (Oracle) <willy@infradead.org>
-R:     Jan Kara <jack@suse.cz>
+M:     Jan Kara <jack@suse.cz>
 L:     linux-fsdevel@vger.kernel.org
 L:     linux-mm@kvack.org
 S:     Supported
@@ -23312,7 +23316,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
@@ -23571,7 +23575,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
@@ -26525,7 +26529,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
@@ -28054,6 +28058,7 @@ F:      include/dt-bindings/usb/
 F:     include/linux/usb.h
 F:     include/linux/usb/
 F:     include/uapi/linux/usb/
+F:     rust/kernel/usb.rs
 
 USB TYPEC BUS FOR ALTERNATE MODES
 M:     Heikki Krogerus <heikki.krogerus@linux.intel.com>
index b4035d3cef266db763ba6610ed722a439f03db8d..11539c3fd405cc964281588efb004a1920c689f0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 7
 PATCHLEVEL = 2
 SUBLEVEL = 0
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc4
 NAME = Baby Opossum Posse
 
 # *DOCUMENTATION*
index f930396d9daef973484d3de22a63c4fb08395536..870e5291b7dbca93599c16ffb198172a868ba05c 100644 (file)
@@ -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
index 6b779dee5ea04d17f77b15395d2ed5c285ac1da1..d45e4d3359986878b24360b3d91b2eb452507aae 100644 (file)
@@ -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
index a89b50d5369d3c6ee40b46a87665dcbc4b5387da..f986c0205f132d7f0d7d3c9efe1d8860bb02f7a8 100644 (file)
@@ -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
index 865fbc19ef031a51c6a52025e3e9a5b7aa7041b3..6e396a9ddb8b2781a83a88b5895de790f70d3b84 100644 (file)
@@ -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
index b2f2c59279a6799ad89daf3ce709b8d010915920..2d99dffed0ce5c099191d76e6571a1c67a874196 100644 (file)
@@ -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))
index cb46802f5ce52ceb089a5f777fb2f36d40eb9cad..7f812020e0820bbad3db744d14f245c130f75be2 100644 (file)
@@ -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.
index 66e26990e2c8d3ba70e516988901584d80ed35e4..c478fb8a6f788d1d7ac70dae7afbe070e73a0f1b 100644 (file)
@@ -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
index 8e0c51e496e20d6510f0224b2486d7081c04b6bf..820670dd6042674778e64c0cb477964904218b30 100644 (file)
                #size-cells = <0>;
 
                cpu0_0: cpu@0 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x00000>;
 
                };
 
                cpu0_1: cpu@100 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x00100>;
 
                };
 
                cpu0_2: cpu@200 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x00200>;
 
                };
 
                cpu0_3: cpu@300 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x00300>;
 
                };
 
                cpu1_0: cpu@10000 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x10000>;
 
                };
 
                cpu1_1: cpu@10100 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x10100>;
 
                };
 
                cpu1_2: cpu@10200 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x10200>;
 
                };
 
                cpu1_3: cpu@10300 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x10300>;
 
                };
 
                cpu2_0: cpu@20000 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x20000>;
 
                };
 
                cpu2_1: cpu@20100 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x20100>;
 
                };
 
                cpu2_2: cpu@20200 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x20200>;
 
                };
 
                cpu2_3: cpu@20300 {
-                       compatible = "arm,cortex-a78";
+                       compatible = "arm,cortex-a78ae";
                        device_type = "cpu";
                        reg = <0x20300>;
 
index 2d8e7e37830ff20619b4060c7c5cc9a370e34447..2d2cb1a3d95cb2841b71461d4dca52965877fa64 100644 (file)
                };
 
                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>,
                        d-cache-sets = <256>;
                };
 
-               cpu1: cpu@1 {
+               cpu1: cpu@10000 {
                        compatible = "arm,neoverse-v3ae";
                        device_type = "cpu";
                        reg = <0x10000>;
index d2b3fc08954a1c008d1249cf32c3985dc350f96f..0ab303863155e1cad1df23fcddc389e9eabfbb02 100644 (file)
                        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;
+               };
        };
 };
 
index cdf3e8422ea126daf83bd6fd03d283e9c4c0cc76..012d711034d17215ed01e0042829082aec175c1c 100644 (file)
@@ -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,
index 6297631532e59f49de44d53ecdeb22944c39d0b3..767f35ea62b39cb02fe87d118a7d3979f5ca69d3 100644 (file)
@@ -2,17 +2,11 @@
 #ifndef _ARCH_ARM64_TLBBATCH_H
 #define _ARCH_ARM64_TLBBATCH_H
 
-#include <linux/cpumask.h>
-
 struct arch_tlbflush_unmap_batch {
-#ifdef CONFIG_ARM64_ERRATUM_4193714
        /*
-        * Track CPUs that need SME DVMSync on completion of this batch.
-        * Otherwise, the arm64 HW can do tlb shootdown, so we don't need to
-        * record cpumask for sending IPI
+        * For arm64, HW can do TLB shootdown, so we don't need to record a
+        * cpumask for sending IPIs.
         */
-       cpumask_var_t cpumask;
-#endif
 };
 
 #endif /* _ARCH_ARM64_TLBBATCH_H */
index d52ac8c17190da490b97ce2f8ea2ea42744cb648..e0e84332f51be9f964bc3e413b66f346fe6421de 100644 (file)
@@ -82,6 +82,8 @@ static inline unsigned long get_trans_granule(void)
 
 #ifdef CONFIG_ARM64_ERRATUM_4193714
 
+extern cpumask_t sme_active_cpus;
+
 void sme_do_dvmsync(const struct cpumask *mask);
 
 static inline void sme_dvmsync(struct mm_struct *mm)
@@ -92,42 +94,12 @@ static inline void sme_dvmsync(struct mm_struct *mm)
        sme_do_dvmsync(mm_cpumask(mm));
 }
 
-static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch,
-                                          struct mm_struct *mm)
+static inline void sme_dvmsync_batch(void)
 {
        if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
                return;
 
-       /*
-        * Order the mm_cpumask() read after the hardware DVMSync.
-        */
-       dsb(ish);
-       if (cpumask_empty(mm_cpumask(mm)))
-               return;
-
-       /*
-        * Allocate the batch cpumask on first use. Fall back to an immediate
-        * IPI for this mm in case of failure.
-        */
-       if (!cpumask_available(batch->cpumask) &&
-           !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) {
-               sme_do_dvmsync(mm_cpumask(mm));
-               return;
-       }
-
-       cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm));
-}
-
-static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
-{
-       if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
-               return;
-
-       if (!cpumask_available(batch->cpumask))
-               return;
-
-       sme_do_dvmsync(batch->cpumask);
-       cpumask_clear(batch->cpumask);
+       sme_do_dvmsync(&sme_active_cpus);
 }
 
 #else
@@ -135,11 +107,7 @@ static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
 static inline void sme_dvmsync(struct mm_struct *mm)
 {
 }
-static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch,
-                                          struct mm_struct *mm)
-{
-}
-static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
+static inline void sme_dvmsync_batch(void)
 {
 }
 
@@ -285,11 +253,11 @@ static inline void __tlbi_sync_s1ish(struct mm_struct *mm)
        sme_dvmsync(mm);
 }
 
-static inline void __tlbi_sync_s1ish_batch(struct arch_tlbflush_unmap_batch *batch)
+static inline void __tlbi_sync_s1ish_batch(void)
 {
        dsb(ish);
        __repeat_tlbi_sync(vale1is, 0);
-       sme_dvmsync_batch(batch);
+       sme_dvmsync_batch();
 }
 
 static inline void __tlbi_sync_s1ish_kernel(void)
@@ -434,7 +402,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
  */
 static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
 {
-       __tlbi_sync_s1ish_batch(batch);
+       __tlbi_sync_s1ish_batch();
 }
 
 /*
@@ -722,7 +690,6 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b
 
        __flush_tlb_range(&vma, start, end, PAGE_SIZE, 3,
                          TLBF_NOWALKCACHE | TLBF_NOSYNC);
-       sme_dvmsync_add_pending(batch, mm);
 }
 
 static inline bool __pte_flags_need_flush(ptval_t oldval, ptval_t newval)
index 5891f92c203541f3fd4f9a5e01e41dd6e41c7a31..681aa2bbc3995f27ab5d00b96f0bb3f65d3d4ed5 100644 (file)
@@ -448,12 +448,14 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 apci_id,
                return *pcpu;
        }
 
+       set_cpu_present(*pcpu, true);
        return 0;
 }
 EXPORT_SYMBOL(acpi_map_cpu);
 
 int acpi_unmap_cpu(int cpu)
 {
+       set_cpu_present(cpu, false);
        return 0;
 }
 EXPORT_SYMBOL(acpi_unmap_cpu);
index 25dc5afe9ba0c8923acda6f815541d5bd984cef7..e7f1682a3059b5d083a4d62c611e9e2e781797ad 100644 (file)
@@ -1355,6 +1355,7 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs)
  * SME/CME erratum handling.
  */
 static cpumask_t sme_dvmsync_cpus;
+cpumask_t sme_active_cpus;
 
 /*
  * These helpers are only called from non-preemptible contexts, so
@@ -1368,13 +1369,15 @@ void sme_set_active(void)
                return;
 
        cpumask_set_cpu(cpu, mm_cpumask(current->mm));
+       cpumask_set_cpu(cpu, &sme_active_cpus);
 
        /*
         * A subsequent (post ERET) SME access may use a stale address
         * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for
-        * the completion of cpumask_set_cpu() above as it appears in program
-        * order before the SME access. The post-TLBI+DSB read of mm_cpumask()
-        * will lead to the IPI being issued.
+        * the completion of the cpumask_set_cpu() operations above as they
+        * appear in program order before the SME access. The post-TLBI+DSB
+        * read of mm_cpumask() or sme_active_cpus will lead to the IPI being
+        * issued.
         *
         * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3
         */
@@ -1392,6 +1395,7 @@ void sme_clear_active(void)
         * completed on entering EL1.
         */
        cpumask_clear_cpu(cpu, mm_cpumask(current->mm));
+       cpumask_clear_cpu(cpu, &sme_active_cpus);
 }
 
 static void sme_dvmsync_ipi(void *unused)
index 033643cd4e5eded6c13564ae1807c810dd7cb9ee..581f80e9b9b730041f5653080b8cc0f7b37e4b75 100644 (file)
@@ -341,41 +341,8 @@ void flush_thread(void)
        flush_gcs();
 }
 
-#ifdef CONFIG_ARM64_ERRATUM_4193714
-
-static void arch_dup_tlbbatch_mask(struct task_struct *dst)
-{
-       /*
-        * Clear the inherited cpumask with memset() to cover both cases where
-        * cpumask_var_t is a pointer or an array. It will be allocated lazily
-        * in sme_dvmsync_add_pending() if CPUMASK_OFFSTACK=y.
-        */
-       if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
-               memset(&dst->tlb_ubc.arch.cpumask, 0,
-                      sizeof(dst->tlb_ubc.arch.cpumask));
-}
-
-static void arch_release_tlbbatch_mask(struct task_struct *tsk)
-{
-       if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
-               free_cpumask_var(tsk->tlb_ubc.arch.cpumask);
-}
-
-#else
-
-static void arch_dup_tlbbatch_mask(struct task_struct *dst)
-{
-}
-
-static void arch_release_tlbbatch_mask(struct task_struct *tsk)
-{
-}
-
-#endif /* CONFIG_ARM64_ERRATUM_4193714 */
-
 void arch_release_task_struct(struct task_struct *tsk)
 {
-       arch_release_tlbbatch_mask(tsk);
        fpsimd_release_task(tsk);
 }
 
@@ -391,8 +358,6 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 
        *dst = *src;
 
-       arch_dup_tlbbatch_mask(dst);
-
        /*
         * Drop stale reference to src's sve_state and convert dst to
         * non-streaming FPSIMD mode.
index d46022f7207540c260736f06b15ac69699f3fdb7..cdcdd160e5b69614b9688db502de766de1dc9b07 100644 (file)
@@ -535,23 +535,13 @@ void arch_unregister_cpu(int cpu)
 {
        acpi_handle acpi_handle = acpi_get_processor_handle(cpu);
        struct cpu *c = &per_cpu(cpu_devices, cpu);
-       acpi_status status;
        unsigned long long sta;
-
-       if (!acpi_handle) {
-               pr_err_once("Removing a CPU without associated ACPI handle\n");
-               return;
-       }
+       acpi_status status;
 
        status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta);
-       if (ACPI_FAILURE(status))
-               return;
-
-       /* For now do not allow anything that looks like physical CPU HP */
-       if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) {
+       if (!ACPI_FAILURE(status) &&
+           cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT))
                pr_err_once("Changing CPU present bit is not supported\n");
-               return;
-       }
 
        unregister_cpu(c);
 }
@@ -566,6 +556,11 @@ struct acpi_madt_generic_interrupt *acpi_cpu_get_madt_gicc(int cpu)
 }
 EXPORT_SYMBOL_GPL(acpi_cpu_get_madt_gicc);
 
+static bool acpi_cpu_is_present(int cpu)
+{
+       return acpi_cpu_get_madt_gicc(cpu)->flags & ACPI_MADT_ENABLED;
+}
+
 /*
  * acpi_map_gic_cpu_interface - parse processor MADT entry
  *
@@ -670,6 +665,10 @@ static void __init acpi_parse_and_init_cpus(void)
                early_map_cpu_to_node(i, acpi_numa_get_nid(i));
 }
 #else
+static bool acpi_cpu_is_present(int cpu)
+{
+       return false;
+}
 #define acpi_parse_and_init_cpus(...)  do { } while (0)
 #endif
 
@@ -814,7 +813,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
                if (err)
                        continue;
 
-               set_cpu_present(cpu, true);
+               if (acpi_disabled || acpi_cpu_is_present(cpu))
+                       set_cpu_present(cpu, true);
                numa_store_cpu_info(cpu);
        }
 }
index b8ded434c63f95d18be85cfb1ab8fd3e5848ead8..640f2dc00a8bad7cc9d94364e77e9919de958654 100644 (file)
 #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)
index e688bc5139c1ff45a57f5f66f973a6d237be0b83..3c82f392845d14a607622f41e1e8207563d0320b 100644 (file)
@@ -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);
 }
index 18131e395e24c325dbc5b30ec2ff27664f190b98..4bf624a49591dfec58f04f6c84a17620bb190266 100644 (file)
@@ -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)
index 1af722771178a186f9c0ceb7ecf46a09a800d691..a327c2bbb6b64d529ea9afa05ddf9d299a183f94 100644 (file)
@@ -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
index 3b2c4fbc34d8ee4f887e979953f06aafd6692db0..24d6f164129ac63bd3d4845f872970c3e4063793 100644 (file)
@@ -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 */
index 8c3fbb413a06d7e2ebe8f994458734a6135f853d..b1411fb541397e0cdc471cab51d53ef73d364385 100644 (file)
@@ -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,
index 91a7dfad668660fc0de62e888334dce1adb301b6..b74dd5ce1efd371212802653bde050323dda03e7 100644 (file)
@@ -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;
 }
 
index 89982bd3345f6deccf6a18fd80f1cad123128426..d6c4fc16f87953e0b4cadbcb8d2551818e48c651 100644 (file)
@@ -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;
        }
index e2285ed8c91de6ecc7db57124b70195478f81563..d1c3a352d5a22e4b6055eeefc52fb988ad9edced 100644 (file)
@@ -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);
        }
 
index fb54f6dad995c958f0f763e1f50b8a9c7e501a57..dfb96edbdc43c666ec90ec326cb645cac9c1a55c 100644 (file)
@@ -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;
index 053e4f733e4bed6e37ef21facbd3076612fcada3..428723b1b0f5c7ed7aa40d31d32a44570469ee59 100644 (file)
@@ -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);
        }
index 4477f870c7b36e868355538c220ca06fa66a0ae2..740b39875728d68823d584cf1a4fb0210b6df3f9 100644 (file)
@@ -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);
index 5a4768d8cd4f313e304c7c2fb95916e4b2977930..ccb7e3a90cd07a9f84202ce65600ea6ceef27524 100644 (file)
@@ -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);
index f2be501468ce5a4a336921b743b4eb4472253096..a25d8beacc8310d0dd08dfa0d43633bf0eb1b3dd 100644 (file)
@@ -1515,7 +1515,13 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr,
                        if (free_mapped) {
                                /* CONT blocks are not supported in the vmemmap */
                                WARN_ON(pmd_cont(pmd));
-                               flush_tlb_kernel_range(addr, addr + PMD_SIZE);
+                               /*
+                                * Invalidating a block entry requires just
+                                * a single overlapping TLB invalidation,
+                                * so limit the range of the flush to a single
+                                * page.
+                                */
+                               flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
                                free_hotplug_page_range(pmd_page(pmd),
                                                        PMD_SIZE, altmap);
                        }
@@ -1545,7 +1551,8 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr,
                if (pud_leaf(pud)) {
                        pud_clear(pudp);
                        if (free_mapped) {
-                               flush_tlb_kernel_range(addr, addr + PUD_SIZE);
+                               /* See comment in unmap_hotplug_pmd_range(). */
+                               flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
                                free_hotplug_page_range(pud_page(pud),
                                                        PUD_SIZE, altmap);
                        }
index bc1788b1662b766132c1b0b8d3d31881902f6139..7cb61aca3797faa6722d55e1e59ebb4d8db1cb28 100644 (file)
@@ -1806,7 +1806,7 @@ Res0      15:8
 UnsignedEnum   7:4     BWE
        0b0000  NI
        0b0001  FEAT_BWE
-       0b0002  FEAT_BWE2
+       0b0010  FEAT_BWE2
 EndEnum
 UnsignedEnum   3:0     STEP
        0b0000  NI
index 11d7423ef6463303ff65000420ba9013b034980c..ec04c32bb03c1695404ddd393a69f73ab83abbfd 100644 (file)
@@ -79,7 +79,7 @@ static void __init m523x_i2c_init(void)
 static void __init m523x_fec_init(void)
 {
        /* Set multi-function pins to ethernet use */
-       mcf_write8(read8(MCFGPIO_PAR_FECI2C) | 0xf0, MCFGPIO_PAR_FECI2C);
+       mcf_write8(mcf_read8(MCFGPIO_PAR_FECI2C) | 0xf0, MCFGPIO_PAR_FECI2C);
 }
 
 /***************************************************************************/
index b244c9ba40a700a72b652c2922bb34a4d0df01aa..3383b1ba106a28143bb34cc8d2d9bd713f7fc6e5 100644 (file)
@@ -113,11 +113,11 @@ void wildfiremod_halt(void)
        mcf_write16(read16(MCFGPIO_PEPAR) & ~(1 << (5 * 2)), MCFGPIO_PEPAR);
 
        /* Make portE.5 an output */
-       mcf_write8(read8(MCFGPIO_PDDR_E) | (1 << 5), MCFGPIO_PDDR_E);
+       mcf_write8(mcf_read8(MCFGPIO_PDDR_E) | (1 << 5), MCFGPIO_PDDR_E);
 
        /* Now toggle portE.5 from low to high */
-       mcf_write8(read8(MCFGPIO_PODR_E) & ~(1 << 5), MCFGPIO_PODR_E);
-       mcf_write8(read8(MCFGPIO_PODR_E) | (1 << 5), MCFGPIO_PODR_E);
+       mcf_write8(mcf_read8(MCFGPIO_PODR_E) & ~(1 << 5), MCFGPIO_PODR_E);
+       mcf_write8(mcf_read8(MCFGPIO_PODR_E) | (1 << 5), MCFGPIO_PODR_E);
 
        printk(KERN_EMERG "Failed to hibernate. Halting!\n");
 }
index ed782609ca413f4445cdce925b3b5ef8f47bdd18..0971a0651d490b43393fed14c7c66803dc6d62cc 100644 (file)
@@ -55,10 +55,12 @@ static inline void clear_page(void *page)
 #define clear_user_page(addr, vaddr, page)     \
        do {    clear_page(addr);               \
                flush_dcache_page(page);        \
+               (void)(vaddr);                  \
        } while (0)
 #define copy_user_page(to, from, vaddr, page)  \
        do {    copy_page(to, from);            \
                flush_dcache_page(page);        \
+               (void)(vaddr);                  \
        } while (0)
 
 extern unsigned long m68k_memoffset;
diff --git a/arch/powerpc/include/asm/preempt.h b/arch/powerpc/include/asm/preempt.h
deleted file mode 100644 (file)
index 000e2b9..0000000
+++ /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 */
index 7b8c56962c3111cbab086d2a3fc4241e52169623..49039074b33fd5a111cb4bd1c1071c92bf4427c7 100644 (file)
@@ -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);
index 3af6c06af02f31f6927ee18866d208fcff8fc51c..e5853daa6a486ade5c41797824365662dada015f 100644 (file)
@@ -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;
        }
 }
 
index 3460d1a5a97c2abfce40404f53d835110b2fe789..11145c40183dd1218fbbb170eb71831b4a272d32 100644 (file)
@@ -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();
 }
 
 /*
index 57e897b60db86c1237befb2fbd38574732a27799..cc9fb72cb4ebed9249a9305da5f611d13eec8aac 100644 (file)
@@ -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;
 }
index 757811155587db40a68b7c22b7b56f72941cbea2..c11deb2f50ed45a7b36c352f4a61f71066993d7e 100644 (file)
@@ -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", },
        {},
 };
 
index f6de8c1169d5bfc794c6cca535cf29c7fd417610..de7494748fecd673dc9aadffe86a0e87bdcb0363 100644 (file)
@@ -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);
index f7052b131a4c5e342b455d12cb41a62c1dc3a743..74910ce3a541c346e15f2deace0eb1bc912edcdb 100644 (file)
@@ -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.
index c6159870de0e11bf18919e52d2b5c94ea2a7a08f..9c3758aa54c6e4401b4d2e3d427c8d8edc8eac4d 100644 (file)
@@ -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;
        }
 
index c0a6992933e411f745ef113df84a00ff7cf4a800..f7028caaeae06e093cb2b7e2f4733dfaf01af6ac 100644 (file)
@@ -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
index c174ac0ec46b47c27c5b3cd8c9fbf75dd1161a91..429e0758930687c742c4dd09603e71e90dc096a2 100644 (file)
@@ -84,6 +84,12 @@ config ARCH_THEAD
        help
          This enables support for the RISC-V based T-HEAD SoCs.
 
+config ARCH_ULTRARISC
+       bool "UltraRISC RISC-V SoCs"
+       help
+         This enables support for UltraRISC SoC platform hardware,
+         including boards based on the UR-DP1000.
+
 config ARCH_VIRT
        bool "QEMU Virt Machine"
        select POWER_RESET
index 74ba5acc12a403284c773bf923b6137c18e99c1b..ed605b5e3162da197c9636e1604c58964f8db737 100644 (file)
@@ -33,6 +33,7 @@ CONFIG_SOC_STARFIVE=y
 CONFIG_ARCH_SUNXI=y
 CONFIG_ARCH_TENSTORRENT=y
 CONFIG_ARCH_THEAD=y
+CONFIG_ARCH_ULTRARISC=y
 CONFIG_ARCH_VIRT=y
 CONFIG_ARCH_CANAAN=y
 CONFIG_SMP=y
index 8cfe59483a8f28c29b35e54fa2584e6bfb58760f..c2b0a2928f06743e59485e165b9edf976b84212e 100644 (file)
@@ -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)
index 09bb5f57a9d3460ae0e8798eda692d67bd8acf4e..92d5f831f3495a9555c2f9a7566390c39911c4cd 100644 (file)
@@ -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>
 
index 60017ceec9d2afe166cdea9d4ad2ecde4831a0ff..e2d5808169e44de49ae56925ebf3c17e4c7a2dde 100644 (file)
@@ -209,13 +209,13 @@ struct kvm_vcpu_arch {
        /*
         * VCPU interrupts
         *
-        * We have a lockless approach for tracking pending VCPU interrupts
-        * implemented using atomic bitops. The irqs_pending bitmap represent
-        * pending interrupts whereas irqs_pending_mask represent bits changed
-        * in irqs_pending. Our approach is modeled around multiple producer
-        * and single consumer problem where the consumer is the VCPU itself.
+        * The irqs_pending bitmap represents pending interrupts whereas
+        * irqs_pending_mask represents bits changed in irqs_pending. Updates
+        * to these bitmaps are serialized so vcpu interrupt sync/flush cannot
+        * drop a newly injected interrupt while syncing guest-visible HVIP.
         */
 #define KVM_RISCV_VCPU_NR_IRQS 64
+       raw_spinlock_t irqs_pending_lock;
        DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
        DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
 
index 08df724e13b9dbcfb5f429fe07515039fc2e5900..d799c4e56f8049a0c695d913f8bf021a64440bad 100644 (file)
@@ -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
index 2306ce3e5f229ffe57faf04d457bdf58fcb423c9..738df176ff6f16b7997ee404cda40e09c2e17111 100644 (file)
@@ -41,6 +41,9 @@ machine_kexec_prepare(struct kimage *image)
                if (image->segment[i].memsz <= sizeof(fdt))
                        continue;
 
+               if (!image->segment[i].buf)
+                       continue;
+
                if (image->file_mode)
                        memcpy(&fdt, image->segment[i].buf, sizeof(fdt));
                else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt)))
index 1659d31fd288fc296d711c111e8d1a2e2fc8026a..caf6762427c87e1c1b5ec86146a46553d3cdf216 100644 (file)
@@ -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;
index a842dc034571da7ae24628ce95dee6be12a9bed7..43ee881f6c6fd1f60ca464331dd44e1c6fe05521 100644 (file)
@@ -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)
index e82987dc37394b6463409d4c4ff0bb23c951843b..d6f96b1abe409b7526a6c3ad943e83da00b65b44 100644 (file)
@@ -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
index bafb009c5ce55972abcf845e31b769252a904cd9..9a653b4ad40a5dca0bd33728d0a7f7214a752e37 100644 (file)
@@ -53,12 +53,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu)
        struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
        unsigned long mask, val;
 
+       lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
+
        if (!kvm_riscv_aia_available())
                return;
 
-       if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) {
-               mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0);
-               val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask;
+       mask = vcpu->arch.irqs_pending_mask[1];
+       if (mask) {
+               vcpu->arch.irqs_pending_mask[1] = 0;
+               val = vcpu->arch.irqs_pending[1] & mask;
 
                csr->hviph &= ~mask;
                csr->hviph |= val;
@@ -69,6 +72,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
 {
        struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
 
+       lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
+
        if (kvm_riscv_aia_available())
                csr->vsieh = ncsr_read(CSR_VSIEH);
 }
@@ -77,13 +82,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
 bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
 {
        unsigned long seip;
+#ifdef CONFIG_32BIT
+       unsigned long flags;
+       bool pending;
+#endif
 
        if (!kvm_riscv_aia_available())
                return false;
 
 #ifdef CONFIG_32BIT
-       if (READ_ONCE(vcpu->arch.irqs_pending[1]) &
-           (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask)))
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       pending = vcpu->arch.irqs_pending[1] &
+                 (vcpu->arch.aia_context.guest_csr.vsieh &
+                  upper_32_bits(mask));
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+       if (pending)
                return true;
 #endif
 
@@ -207,6 +221,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
 {
        struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
        unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);
+#ifdef CONFIG_32BIT
+       unsigned long flags;
+#endif
 
        if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
                return -ENOENT;
@@ -219,8 +236,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
                ((unsigned long *)csr)[reg_num] = val;
 
 #ifdef CONFIG_32BIT
-               if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph))
-                       WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0);
+               if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) {
+                       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+                       vcpu->arch.irqs_pending_mask[1] = 0;
+                       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock,
+                                                  flags);
+               }
 #endif
        }
 
index c4c3b79567f10d09a00cc2947e67dd140c49b30f..b0474fcf065aa2c5bb63e69a01dddf2625ec518d 100644 (file)
@@ -5,11 +5,13 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/cpufeature.h>
 #include <linux/errno.h>
 #include <linux/kvm_host.h>
 #include <linux/module.h>
 #include <linux/pgtable.h>
 #include <asm/kvm_gstage.h>
+#include <asm/hwcap.h>
 
 #ifdef CONFIG_64BIT
 unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3;
@@ -171,8 +173,10 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,
        }
 
        if (pte_val(*ptep) != pte_val(map->pte)) {
+               bool was_invalid = !pte_val(*ptep);
                set_pte(ptep, map->pte);
-               if (gstage_pte_leaf(ptep))
+               if (gstage_pte_leaf(ptep) &&
+                   !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)))
                        gstage_tlb_flush(gstage, current_level, map->addr);
        }
 
index 082f9b26173387db5b423801e85fc37d2efd1f6d..8a0aa5e0e216eac07426279baabf21d15c6509e6 100644 (file)
@@ -41,6 +41,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
        pgprot_t prot;
        unsigned long pfn;
        phys_addr_t addr, end;
+       unsigned long pgd_levels = kvm->arch.pgd_levels;
        struct kvm_mmu_memory_cache pcache = {
                .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0,
                .gfp_zero = __GFP_ZERO,
@@ -63,7 +64,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
                if (!writable)
                        map.pte = pte_wrprotect(map.pte);
 
-               ret = kvm_mmu_topup_memory_cache(&pcache, kvm->arch.pgd_levels);
+               ret = __kvm_mmu_topup_memory_cache(&pcache, pgd_levels, pgd_levels);
                if (ret)
                        goto out;
 
index cf6e231e76e254ad3c94753488ae43670c2359ea..977e36ab83d3f53c6185493b90a7baa053cb0086 100644 (file)
@@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu,
 
 static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
 {
+       unsigned long flags;
        bool loaded;
 
        /**
@@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
 
        kvm_riscv_vcpu_aia_reset(vcpu);
 
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
        bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
        bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
 
        kvm_riscv_vcpu_pmu_reset(vcpu);
 
@@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 
        /* Setup VCPU hfence queue */
        spin_lock_init(&vcpu->arch.hfence_lock);
+       raw_spin_lock_init(&vcpu->arch.irqs_pending_lock);
 
        spin_lock_init(&vcpu->arch.reset_state.lock);
 
@@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
 {
        struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
        unsigned long mask, val;
+       unsigned long flags;
 
-       if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) {
-               mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0);
-               val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask;
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+
+       mask = vcpu->arch.irqs_pending_mask[0];
+       if (mask) {
+               vcpu->arch.irqs_pending_mask[0] = 0;
+               val = vcpu->arch.irqs_pending[0] & mask;
 
                csr->hvip &= ~mask;
                csr->hvip |= val;
@@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
 
        /* Flush AIA high interrupts */
        kvm_riscv_vcpu_aia_flush_interrupts(vcpu);
+
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
 }
 
 void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
 {
        unsigned long hvip;
+       unsigned long flags;
        struct kvm_vcpu_arch *v = &vcpu->arch;
        struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
 
@@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
 
        /* Sync-up HVIP.VSSIP bit changes does by Guest */
        hvip = ncsr_read(CSR_HVIP);
+
+       raw_spin_lock_irqsave(&v->irqs_pending_lock, flags);
+
        if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) {
                if (hvip & (1UL << IRQ_VS_SOFT)) {
-                       if (!test_and_set_bit(IRQ_VS_SOFT,
-                                             v->irqs_pending_mask))
-                               set_bit(IRQ_VS_SOFT, v->irqs_pending);
+                       if (!__test_and_set_bit(IRQ_VS_SOFT,
+                                               v->irqs_pending_mask))
+                               __set_bit(IRQ_VS_SOFT, v->irqs_pending);
                } else {
-                       if (!test_and_set_bit(IRQ_VS_SOFT,
-                                             v->irqs_pending_mask))
-                               clear_bit(IRQ_VS_SOFT, v->irqs_pending);
+                       if (!__test_and_set_bit(IRQ_VS_SOFT,
+                                               v->irqs_pending_mask))
+                               __clear_bit(IRQ_VS_SOFT, v->irqs_pending);
                }
        }
 
        /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */
        if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) {
                if (!(hvip & (1UL << IRQ_PMU_OVF)) &&
-                   !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
-                       clear_bit(IRQ_PMU_OVF, v->irqs_pending);
+                   !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
+                       __clear_bit(IRQ_PMU_OVF, v->irqs_pending);
        }
 
        /* Sync-up AIA high interrupts */
        kvm_riscv_vcpu_aia_sync_interrupts(vcpu);
 
+       raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags);
+
        /* Sync-up timer CSRs */
        kvm_riscv_vcpu_timer_sync(vcpu);
 }
 
 int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
 {
+       unsigned long flags;
+
        /*
         * We only allow VS-mode software, timer, and external
         * interrupts when irq is one of the local interrupts
@@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
            irq != IRQ_PMU_OVF)
                return -EINVAL;
 
-       set_bit(irq, vcpu->arch.irqs_pending);
-       smp_mb__before_atomic();
-       set_bit(irq, vcpu->arch.irqs_pending_mask);
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       __set_bit(irq, vcpu->arch.irqs_pending);
+       __set_bit(irq, vcpu->arch.irqs_pending_mask);
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
 
        kvm_vcpu_kick(vcpu);
 
@@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
 
 int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
 {
+       unsigned long flags;
+
        /*
         * We only allow VS-mode software, timer, counter overflow and external
         * interrupts when irq is one of the local interrupts
@@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
            irq != IRQ_PMU_OVF)
                return -EINVAL;
 
-       clear_bit(irq, vcpu->arch.irqs_pending);
-       smp_mb__before_atomic();
-       set_bit(irq, vcpu->arch.irqs_pending_mask);
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+       __clear_bit(irq, vcpu->arch.irqs_pending);
+       __set_bit(irq, vcpu->arch.irqs_pending_mask);
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
 
        return 0;
 }
 
 bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
 {
+       unsigned long flags;
        unsigned long ie;
+       bool ret;
 
+       raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
        ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
                << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
        ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
                (unsigned long)mask;
-       if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie)
-               return true;
+       ret = vcpu->arch.irqs_pending[0] & ie;
+       raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
 
        /* Check AIA high interrupts */
-       return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
+       if (!ret)
+               ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
+
+       return ret;
 }
 
 void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu)
index 0bb0c51e3c89078056892e3177fd3bdcfaeb5019..6c8530b9f29ed8556c28efb5e68e20f576a3ebf7 100644 (file)
@@ -38,6 +38,25 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
                        return kvm_riscv_vcpu_mmio_store(vcpu, run,
                                                         fault_addr,
                                                         trap->htinst);
+               case EXC_INST_GUEST_PAGE_FAULT: {
+                       /*
+                        * No memslot backs this GPA and an instruction fetch
+                        * cannot be emulated as MMIO. On bare metal a fetch
+                        * from an unbacked physical address raises an
+                        * instruction access fault, so reflect that back to
+                        * the guest.
+                        */
+                       struct kvm_cpu_trap inst_trap = {
+                               .sepc   = trap->sepc,
+                               .scause = EXC_INST_ACCESS,
+                               .stval  = trap->stval,
+                               .htval  = 0,
+                               .htinst = 0,
+                       };
+
+                       kvm_riscv_vcpu_trap_redirect(vcpu, &inst_trap);
+                       return 1;
+               }
                default:
                        return -EOPNOTSUPP;
                };
index bb920e8923c930e55f885085965669233cc28a6e..99b9107b1ac1808e7e5cbd9ef4d6d192f0c0d1f5 100644 (file)
@@ -50,19 +50,13 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu,
                reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK;
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOM))
-                       return -ENOENT;
-               reg_val = riscv_cbom_block_size;
+               reg_val = (kvm_riscv_isa_check_host(ZICBOM)) ? 0 : riscv_cbom_block_size;
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOZ))
-                       return -ENOENT;
-               reg_val = riscv_cboz_block_size;
+               reg_val = (kvm_riscv_isa_check_host(ZICBOZ)) ? 0 : riscv_cboz_block_size;
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOP))
-                       return -ENOENT;
-               reg_val = riscv_cbop_block_size;
+               reg_val = (kvm_riscv_isa_check_host(ZICBOP)) ? 0 : riscv_cbop_block_size;
                break;
        case KVM_REG_RISCV_CONFIG_REG(mvendorid):
                reg_val = vcpu->arch.mvendorid;
@@ -144,21 +138,15 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu,
                }
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOM))
-                       return -ENOENT;
-               if (reg_val != riscv_cbom_block_size)
+               if (reg_val && reg_val != riscv_cbom_block_size)
                        return -EINVAL;
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOZ))
-                       return -ENOENT;
-               if (reg_val != riscv_cboz_block_size)
+               if (reg_val && reg_val != riscv_cboz_block_size)
                        return -EINVAL;
                break;
        case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size):
-               if (kvm_riscv_isa_check_host(ZICBOP))
-                       return -ENOENT;
-               if (reg_val != riscv_cbop_block_size)
+               if (reg_val && reg_val != riscv_cbop_block_size)
                        return -EINVAL;
                break;
        case KVM_REG_RISCV_CONFIG_REG(mvendorid):
@@ -298,6 +286,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
 {
        struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
        unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
+       unsigned long flags;
 
        if (reg_num >= regs_max)
                return -ENOENT;
@@ -311,8 +300,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
 
        ((unsigned long *)csr)[reg_num] = reg_val;
 
-       if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
-               WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0);
+       if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
+               raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+               vcpu->arch.irqs_pending_mask[0] = 0;
+               raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+       }
 
        return 0;
 }
@@ -614,20 +606,6 @@ static int copy_config_reg_indices(const struct kvm_vcpu *vcpu,
                u64 size;
                u64 reg;
 
-               /*
-                * Avoid reporting config reg if the corresponding extension
-                * was not available.
-                */
-               if (i == KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) &&
-                   kvm_riscv_isa_check_host(ZICBOM))
-                       continue;
-               else if (i == KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) &&
-                        kvm_riscv_isa_check_host(ZICBOZ))
-                       continue;
-               else if (i == KVM_REG_RISCV_CONFIG_REG(zicbop_block_size) &&
-                        kvm_riscv_isa_check_host(ZICBOP))
-                       continue;
-
                size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64;
                reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CONFIG | i;
 
index bb46dcbfb24da7521b896a206d3f0e35863b3f0c..2025b664961c80b6c180518f6dacc71c9a241221 100644 (file)
@@ -586,7 +586,7 @@ int kvm_riscv_vcpu_pmu_ctr_start(struct kvm_vcpu *vcpu, unsigned long ctr_base,
                }
        }
        /* Start the counters that have been configured and requested by the guest */
-       for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
+       for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) {
                pmc_index = array_index_nospec(i + ctr_base,
                                               RISCV_KVM_MAX_COUNTERS);
                if (!test_bit(pmc_index, kvpmu->pmc_in_use))
@@ -658,7 +658,7 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
        }
 
        /* Stop the counters that have been configured and requested by the guest */
-       for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
+       for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) {
                pmc_index = array_index_nospec(i + ctr_base,
                                               RISCV_KVM_MAX_COUNTERS);
                if (!test_bit(pmc_index, kvpmu->pmc_in_use))
index ab39ac464ffd817035e4938131562bc80a4ecf88..1342adb3180c6f1a5b730db321506ae24489c543 100644 (file)
@@ -327,9 +327,11 @@ static int kvm_sbi_fwft_set(struct kvm_vcpu *vcpu, u32 feature,
        if (conf->flags & SBI_FWFT_SET_FLAG_LOCK)
                return SBI_ERR_DENIED_LOCKED;
 
-       conf->flags = flags;
+       ret = conf->feature->set(vcpu, conf, false, value);
+       if (ret == SBI_SUCCESS)
+               conf->flags = flags;
 
-       return conf->feature->set(vcpu, conf, false, value);
+       return ret;
 }
 
 static int kvm_sbi_fwft_get(struct kvm_vcpu *vcpu, unsigned long feature,
index 62d2fb77bb9b931681d5901e37befd974a271fbc..3708616e2c327e5b4fbf8d8a5c8d155629a2d4aa 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/kvm_host.h>
+#include <linux/nospec.h>
 #include <linux/uaccess.h>
 #include <asm/cpufeature.h>
 #include <asm/kvm_isa.h>
@@ -129,11 +130,20 @@ static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
                        return -ENOENT;
                }
        } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
+               unsigned long reg_offset;
+
                if (reg_size != vlenb)
                        return -EINVAL;
                WARN_ON(!cntx->vector.datap);
-               *reg_addr = cntx->vector.datap +
-                           (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
+               /*
+                * The reg_num is derived from the userspace-provided ONE_REG
+                * id. Sanitize it with array_index_nospec() to prevent
+                * speculative out-of-bounds access to the vector register
+                * buffer (32 vector registers: v0..v31).
+                */
+               reg_offset = array_index_nospec(
+                               reg_num - KVM_REG_RISCV_VECTOR_REG(0), 32);
+               *reg_addr = cntx->vector.datap + reg_offset * vlenb;
        } else {
                return -ENOENT;
        }
index 5b1b3c88b4d130f893e110ef76676a845bf76f5f..3e450890be07e42831ffa24254466352bb0a08b6 100644 (file)
@@ -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);
index f411562aa7f6b9fb1abde27db6ee0303dd7af93b..c68ec9d28513dbb5410430bfcd7998478388208f 100644 (file)
@@ -190,17 +190,18 @@ static int memtop_get_stride_len(unsigned long *res)
 static int memtop_get_page_count(unsigned long *res, unsigned long level)
 {
        static unsigned long memtop_pages[DIAG310_LEVELMAX];
-       unsigned long pages;
+       unsigned long pages, idx;
        int rc;
 
        if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN)
                return -EINVAL;
-       pages = READ_ONCE(memtop_pages[level - 1]);
+       idx = array_index_nospec(level - 1, ARRAY_SIZE(memtop_pages));
+       pages = READ_ONCE(memtop_pages[idx]);
        if (!pages) {
                rc = diag310_get_memtop_size(&pages, level);
                if (rc)
                        return rc;
-               WRITE_ONCE(memtop_pages[level - 1], pages);
+               WRITE_ONCE(memtop_pages[idx], pages);
        }
        *res = pages;
        return 0;
index 7aa655664eccb0c1e59b42f2d9f49998ae52b2c5..2076ac22e2c4973dece621246646c5e554298319 100644 (file)
@@ -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];
                        }
                }
index db19d0680a0afdf34b85eddf418a7e822dcad0a7..d215781f26b5864c3831aa776cc4f338d0488016 100644 (file)
@@ -4,6 +4,7 @@
  * Here we can supply some information useful to userland.
  */
 
+#include <linux/build-salt.h>
 #include <linux/uts.h>
 #include <linux/version.h>
 #include <linux/elfnote.h>
@@ -11,3 +12,5 @@
 ELFNOTE_START(Linux, 0, "a")
        .long LINUX_VERSION_CODE
 ELFNOTE_END
+
+BUILD_SALT
index 5f1960ec982d045829a92abcc4a84d921ae3e9c7..ed4259d17629543b95a9691aea62f7075b2fe5aa 100644 (file)
@@ -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;
index 298fbaecec28d543a0ef07708cd32912d7b14924..8abb4f55b306ba30018ce98a465fc4ca2032a93f 100644 (file)
@@ -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;
                                }
                        }
index 23c817595e28d5cc1c84995fbdf6e62e3eeba149..150b5dd2170e2fe9b1d23bed79242f3eaf6fba38 100644 (file)
@@ -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);
index 5b075c38998e31d32993068852bbb1a843d35776..720bb58cabe2e90efba75c361534e77e01b37c5b 100644 (file)
@@ -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;
 }
 
index 458abd9bac70256ca0bcdda48b9b6cbdf2efa4b9..9d74ceff136c54bb8841dae29dd9fc840a8b4883 100644 (file)
@@ -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);
index 2a222a7e14f48d7fef8dff00ccbd8688a5cbe459..ef7bfc87758c83e507fd8a8aa896f9c9a81b64a4 100644 (file)
@@ -64,7 +64,7 @@ static inline unsigned long mmap_base(unsigned long rnd,
        return PAGE_ALIGN(STACK_TOP - gap - rnd);
 }
 
-static int get_align_mask(struct file *filp, unsigned long flags)
+static unsigned long get_align_mask(struct file *filp, unsigned long flags)
 {
        if (filp && is_file_hugepages(filp))
                return huge_page_mask_align(filp);
index f196b1d1ddf867b62161cb0528872526c82a693f..aed27604c11f0bbf9efde51b96486fd931d72442 100644 (file)
@@ -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
index 023bf1c3de8b7a1d1d5b22c9dd1c9312ed07ad70..5b83beab89e15085dc4d9ab8828c177881d3f95e 100644 (file)
@@ -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);
index 06f35a6b58a5b228996df99ba3bb62a2bc28cf82..dc564688f3d73a094fbb1202d5867e6fdc063ca3 100644 (file)
@@ -259,13 +259,13 @@ void amd_brs_disable_all(void)
                amd_brs_disable();
 }
 
-static bool amd_brs_match_plm(struct perf_event *event, u64 to)
+static bool amd_brs_match_plm(struct perf_event *event, u64 from, u64 to)
 {
        int type = event->attr.branch_sample_type;
        int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV;
        int plm_u = PERF_SAMPLE_BRANCH_USER;
 
-       if (!(type & plm_k) && kernel_ip(to))
+       if (!(type & plm_k) && (kernel_ip(to) || kernel_ip(from)))
                return 0;
 
        if (!(type & plm_u) && !kernel_ip(to))
@@ -338,11 +338,11 @@ void amd_brs_drain(void)
                 */
                to = (u64)(((s64)to << shift) >> shift);
 
-               if (!amd_brs_match_plm(event, to))
-                       continue;
-
                rdmsrq(brs_from(brs_idx), from);
 
+               if (!amd_brs_match_plm(event, from, to))
+                       continue;
+
                perf_clear_branch_entry_bitfields(br+nr);
 
                br[nr].from = from;
index 6569048a8c1cc26c9da5dafa08acfefbc7bef6dd..a787409f5a6252e0e01cedd37674b807071c055f 100644 (file)
@@ -754,13 +754,11 @@ static void amd_pmu_enable_event(struct perf_event *event)
        x86_pmu_enable_event(event);
 }
 
-static void amd_pmu_enable_all(int added)
+static void __amd_pmu_enable_all(void)
 {
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        int idx;
 
-       amd_brs_enable_all();
-
        for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) {
                /* only activate events which are marked as active */
                if (!test_bit(idx, cpuc->active_mask))
@@ -775,6 +773,12 @@ static void amd_pmu_enable_all(int added)
        }
 }
 
+static void amd_pmu_enable_all(int added)
+{
+       amd_brs_enable_all();
+       __amd_pmu_enable_all();
+}
+
 static void amd_pmu_v2_enable_event(struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
@@ -1561,7 +1565,7 @@ static inline void amd_pmu_reload_virt(void)
                 * set global enable bits once again
                 */
                amd_pmu_v2_disable_all();
-               amd_pmu_enable_all(0);
+               __amd_pmu_enable_all();
                amd_pmu_v2_enable_all(0);
                return;
        }
index 5b437dc8e4ce242bba34a57320bf1d0490ecfaae..9d9c961989d51cc2b24c601febfc3b3fbfb3f194 100644 (file)
@@ -127,7 +127,8 @@ static void amd_pmu_lbr_filter(void)
                }
 
                /* If type does not correspond, then discard */
-               if (type == X86_BR_NONE || (br_sel & type) != type) {
+               if (type == X86_BR_NONE || (br_sel & type) != type ||
+                   (!(br_sel & X86_BR_KERNEL) && kernel_ip(cpuc->lbr_entries[i].from))) {
                        cpuc->lbr_entries[i].from = 0;  /* mark invalid */
                        compress = true;
                }
index d2e8a849f180575a6c5233613be75214a492c264..5b1070ec85d946c9cb9ebfa6d92a550ded261a3c 100644 (file)
@@ -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
 
index 8c62c6d4d5c173c7f0a88748b18f690fa538f52f..cb8ac4b9b0d748eb4d161913eb9d7621c49ce825 100644 (file)
@@ -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);
 
index 6f30bbdddb5aa963b9984849b6a35deb55cb4867..48b019114c1960faf22501ba91d2b0c71d0ccc44 100644 (file)
@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
        if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
                ktimer->expired_tscdeadline = ktimer->tscdeadline;
 
-       if (!from_timer_fn && apic->apicv_active) {
+       if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
                WARN_ON(kvm_get_running_vcpu() != vcpu);
                kvm_apic_inject_pending_timer_irqs(apic);
                return;
@@ -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);
index 234d0a95abf534193e8285e61dfb7e0c56ba19ad..22cf222d30339e31c185e273ca8d7d5361ba18c6 100644 (file)
@@ -2642,6 +2642,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
                         */
                        if (tdp_enabled && invalid_list &&
                            child->role.guest_mode &&
+                           !child->root_count &&
                            !atomic_long_read(&child->parent_ptes.val))
                                return kvm_mmu_prepare_zap_page(kvm, child,
                                                                invalid_list);
@@ -4852,16 +4853,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
        if (r != RET_PF_CONTINUE)
                return r;
 
-       r = RET_PF_RETRY;
        write_lock(&vcpu->kvm->mmu_lock);
 
-       if (is_page_fault_stale(vcpu, fault))
-               goto out_unlock;
-
        r = make_mmu_pages_available(vcpu);
        if (r)
                goto out_unlock;
 
+       if (is_page_fault_stale(vcpu, fault)) {
+               r = RET_PF_RETRY;
+               goto out_unlock;
+       }
+
        r = direct_map(vcpu, fault);
 
 out_unlock:
@@ -7574,7 +7576,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
 static void mmu_destroy_caches(void)
 {
        kmem_cache_destroy(pte_list_desc_cache);
+       pte_list_desc_cache = NULL;
        kmem_cache_destroy(mmu_page_header_cache);
+       mmu_page_header_cache = NULL;
 }
 
 static void kvm_wake_nx_recovery_thread(struct kvm *kvm)
index df3ae0c7ec2c30fbf4e3784172c5598d292aa11b..1ba840a73b7ac9b314037b9e7ca0481e27a301f5 100644 (file)
@@ -864,15 +864,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
        }
 #endif
 
-       r = RET_PF_RETRY;
        write_lock(&vcpu->kvm->mmu_lock);
 
-       if (is_page_fault_stale(vcpu, fault))
-               goto out_unlock;
-
        r = make_mmu_pages_available(vcpu);
        if (r)
                goto out_unlock;
+
+       if (is_page_fault_stale(vcpu, fault)) {
+               r = RET_PF_RETRY;
+               goto out_unlock;
+       }
+
        r = FNAME(fetch)(vcpu, fault, &walker);
 
 out_unlock:
index 4272293478766b535dae946b8a02c2366aad0363..944aaea6501f2f0699c29f25409cae020fdf62ff 100644 (file)
@@ -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;
index 4d2bacd00ec4f5162cef0f636852546e3f752d97..d0971685034b92319a4bdfa2ca33657cca5dfb6d 100644 (file)
@@ -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;
index 0db25bba17f6e82ab59f6a49230c6e5d98605701..93de876c318ce581136b1123786986455a907218 100644 (file)
@@ -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]" : "")
 );
index 6957bb6f5cf7ebe2d7b4c9a34db5d36c36b6fd0e..ddf6df7bee93b224f505a38644b4b4f6759dd9b2 100644 (file)
@@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
 static void free_nested(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
+       struct vmcs *shadow_vmcs;
 
        if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
                vmx_switch_vmcs(vcpu, &vmx->vmcs01);
@@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
        vmx->nested.current_vmptr = INVALID_GPA;
        if (enable_shadow_vmcs) {
                vmx_disable_shadow_vmcs(vmx);
-               vmcs_clear(vmx->vmcs01.shadow_vmcs);
-               free_vmcs(vmx->vmcs01.shadow_vmcs);
+
+               /*
+                * Keep the pointer visible until after VMCLEAR, so migration
+                * can clear an active shadow VMCS on the old CPU.
+                */
+               shadow_vmcs = vmx->vmcs01.shadow_vmcs;
+               vmcs_clear(shadow_vmcs);
                vmx->vmcs01.shadow_vmcs = NULL;
+               free_vmcs(shadow_vmcs);
        }
        kfree(vmx->nested.cached_vmcs12);
        vmx->nested.cached_vmcs12 = NULL;
@@ -582,6 +589,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 +601,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 +3140,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 +3665,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 +3684,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 +3768,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 +4986,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);
 
        /*
index 989ab29b8c6fb97c1c5ef78243b4c26b3f390fbf..545b03d9d10b818e32e79706e47a6daa05562b2e 100644 (file)
@@ -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;
        }
index de9de0d2016cafb93b07898b437c0d64da694549..dc8517f15bc4639d7f0dcde4aca47f58ed6ddc0c 100644 (file)
@@ -159,6 +159,13 @@ struct nested_vmx {
        bool has_preemption_timer_deadline;
        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
index 152789f00fcda96078b5a109dafe21a8be8410eb..8ed82fff7638ed128ea5c2d3800f9c91e28f33fb 100644 (file)
@@ -43,21 +43,26 @@ bool video_is_primary_device(struct device *dev)
        if (!pci_is_display(pdev))
                return false;
 
-       if (pdev == vga_default_device())
-               return true;
-
 #ifdef CONFIG_SCREEN_INFO
        numres = screen_info_resources(si, res, ARRAY_SIZE(res));
-       for (i = 0; i < numres; ++i) {
-               if (!(res[i].flags & IORESOURCE_MEM))
-                       continue;
+       if (numres > 0) {
+               for (i = 0; i < numres; ++i) {
+                       if (!(res[i].flags & IORESOURCE_MEM))
+                               continue;
+
+                       if (pci_find_resource(pdev, &res[i]))
+                               return true;
+               }
 
-               if (pci_find_resource(pdev, &res[i]))
-                       return true;
+               return false;
        }
 #endif
 
-       return false;
+       /*
+        * No framebuffer was set up by the firmware/bootloader, so fall back
+        * to the default VGA device.
+        */
+       return pdev == vga_default_device();
 }
 EXPORT_SYMBOL(video_is_primary_device);
 
index 8bcdce98f6dce4c2b5d0e5ca3be6f020d60d2d03..cff285d8ad8e3e09bef9dba663e7c1acef2942a9 100644 (file)
@@ -536,6 +536,8 @@ int snp_prepare(void)
                goto unlock;
        }
 
+       wbinvd_on_all_cpus();
+
        /*
         * MtrrFixDramModEn is not shared between threads on a core,
         * therefore it must be set on all CPUs prior to enabling SNP.
index f2a5f4d0a9672b622be37a05d78497880c86cd52..6a2f6fc3413e895d8bfdbcbb72822e2a03f1874f 100644 (file)
@@ -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;
 }
 
 /**
index d2a1f5903f248a7a6b6b938ffc050de9cc00afdb..d9676126c5b5daf10ea39e97decf799c77d7a500 100644 (file)
@@ -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:
index 768549f19f97ec1da849a1eabf62c8575d40e303..d1d6bbe0ecf1f963196ed7406b715e697084e25f 100644 (file)
@@ -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);
index 88cb5acc4f39e9c9a7412de72c5ab31a58113640..2c850330a32bc6b0ba1bc7cbe3a3597ef76762cf 100644 (file)
@@ -5218,6 +5218,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
                         struct io_comp_batch *iob, unsigned int flags)
 {
        int ret;
+       unsigned long timeout = jiffies + 2;
 
        do {
                ret = q->mq_ops->poll(hctx, iob);
@@ -5228,7 +5229,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
                if (ret < 0 || (flags & BLK_POLL_ONESHOT))
                        break;
                cpu_relax();
-       } while (!need_resched());
+       } while (!need_resched() && time_before(jiffies, timeout));
 
        return 0;
 }
index dcc2438ca16dc77508cc722594139fc63dc0f3b2..953d400fd0137baed5f2f32a7dc08869fd39c596 100644 (file)
@@ -813,6 +813,21 @@ static void wbt_queue_depth_changed(struct rq_qos *rqos)
        wbt_update_limits(RQWB(rqos));
 }
 
+static bool wbt_set_lat_changed(struct request_queue *q, u64 val)
+{
+       struct rq_qos *rqos = wbt_rq_qos(q);
+       struct rq_wb *rwb;
+
+       if (!rqos)
+               return true;
+
+       rwb = RQWB(rqos);
+       if (rwb->min_lat_nsec != val)
+               return true;
+
+       return rwb_enabled(rwb) != !!val;
+}
+
 static void wbt_exit(struct rq_qos *rqos)
 {
        struct rq_wb *rwb = RQWB(rqos);
@@ -1005,8 +1020,12 @@ int wbt_set_lat(struct gendisk *disk, s64 val)
        else if (val >= 0)
                val *= 1000ULL;
 
-       if (wbt_get_min_lat(q) == val)
+       mutex_lock(&disk->rqos_state_mutex);
+       if (!wbt_set_lat_changed(q, val)) {
+               mutex_unlock(&disk->rqos_state_mutex);
                goto out;
+       }
+       mutex_unlock(&disk->rqos_state_mutex);
 
        blk_mq_quiesce_queue(q);
 
index bea817f3de560296efbe96b1445c3dc19c30dc73..ca30caec838e73c5cdd8bc34114d8d80046f6021 100644 (file)
@@ -1923,11 +1923,20 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
        if (!disk->zone_wplugs_pool)
                goto free_hash;
 
-       disk->zone_wplugs_wq =
-               alloc_workqueue("%s_zwplugs", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
-                               pool_size, disk->disk_name);
-       if (!disk->zone_wplugs_wq)
-               goto destroy_pool;
+       /*
+        * We may already have a zone write plug workqueue as this function may
+        * be called after disk_free_zone_resources(), which does not destroy
+        * the workqueue (the zone write plugs workqueue is destroyed at
+        * disk_release() time).
+        */
+       if (!disk->zone_wplugs_wq) {
+               disk->zone_wplugs_wq =
+                       alloc_workqueue("%s_zwplugs",
+                                       WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+                                       pool_size, disk->disk_name);
+               if (!disk->zone_wplugs_wq)
+                       goto destroy_pool;
+       }
 
        disk->zone_wplugs_worker =
                kthread_create(disk_zone_wplugs_worker, disk,
@@ -1935,15 +1944,12 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
        if (IS_ERR(disk->zone_wplugs_worker)) {
                ret = PTR_ERR(disk->zone_wplugs_worker);
                disk->zone_wplugs_worker = NULL;
-               goto destroy_wq;
+               goto destroy_pool;
        }
        wake_up_process(disk->zone_wplugs_worker);
 
        return 0;
 
-destroy_wq:
-       destroy_workqueue(disk->zone_wplugs_wq);
-       disk->zone_wplugs_wq = NULL;
 destroy_pool:
        mempool_destroy(disk->zone_wplugs_pool);
        disk->zone_wplugs_pool = NULL;
@@ -1999,7 +2005,7 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
        kfree_rcu_mightsleep(zones_cond);
 }
 
-void disk_free_zone_resources(struct gendisk *disk)
+static void disk_free_zone_resources(struct gendisk *disk)
 {
        if (disk->zone_wplugs_worker) {
                kthread_stop(disk->zone_wplugs_worker);
@@ -2007,10 +2013,8 @@ void disk_free_zone_resources(struct gendisk *disk)
        }
        WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list));
 
-       if (disk->zone_wplugs_wq) {
-               destroy_workqueue(disk->zone_wplugs_wq);
-               disk->zone_wplugs_wq = NULL;
-       }
+       if (disk->zone_wplugs_wq)
+               drain_workqueue(disk->zone_wplugs_wq);
 
        disk_destroy_zone_wplugs_hash_table(disk);
 
@@ -2020,6 +2024,16 @@ void disk_free_zone_resources(struct gendisk *disk)
        disk->nr_zones = 0;
 }
 
+void disk_release_zone_resources(struct gendisk *disk)
+{
+       if (disk->zone_wplugs_wq) {
+               destroy_workqueue(disk->zone_wplugs_wq);
+               disk->zone_wplugs_wq = NULL;
+       }
+
+       disk_free_zone_resources(disk);
+}
+
 struct blk_revalidate_zone_args {
        struct gendisk  *disk;
        u8              *zones_cond;
index 25af8ac5ef0f77c09b4d747827b275c32e9f2972..eaac05815cb036cba753d060270fa1acc2be928b 100644 (file)
@@ -528,7 +528,7 @@ static inline void ioc_clear_queue(struct request_queue *q)
 
 #ifdef CONFIG_BLK_DEV_ZONED
 void disk_init_zone_resources(struct gendisk *disk);
-void disk_free_zone_resources(struct gendisk *disk);
+void disk_release_zone_resources(struct gendisk *disk);
 static inline bool bio_zone_write_plugging(struct bio *bio)
 {
        return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING);
@@ -581,7 +581,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
 static inline void disk_init_zone_resources(struct gendisk *disk)
 {
 }
-static inline void disk_free_zone_resources(struct gendisk *disk)
+static inline void disk_release_zone_resources(struct gendisk *disk)
 {
 }
 static inline bool bio_zone_write_plugging(struct bio *bio)
@@ -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)
index 3bcd37c2aa34012c9c60ef0167c56fe9b82d7a2c..2161b6eea680ca28f396788d0fb7f142144b67e3 100644 (file)
@@ -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)
index cfb83138960c866825837a47c1f4e3b7d4289ccd..e14bc4b723efb0038024c80ad100b131fbfc2e74 100644 (file)
@@ -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();
index f84b6a355b574af88c870938a8c69aeb9f8f44ad..df2c3c69b467d47ff6aed269355a38fd7d7be33b 100644 (file)
@@ -407,10 +407,6 @@ static void add_disk_final(struct gendisk *disk)
        struct device *ddev = disk_to_dev(disk);
 
        if (!(disk->flags & GENHD_FL_HIDDEN)) {
-               /* Make sure the first partition scan will be proceed */
-               if (get_capacity(disk) && disk_has_partscan(disk))
-                       set_bit(GD_NEED_PART_SCAN, &disk->state);
-
                bdev_add(disk->part0, ddev->devt);
                if (get_capacity(disk))
                        disk_scan_partitions(disk, BLK_OPEN_READ);
@@ -1300,7 +1296,7 @@ static void disk_release(struct device *dev)
 
        disk_release_events(disk);
        kfree(disk->random);
-       disk_free_zone_resources(disk);
+       disk_release_zone_resources(disk);
        xa_destroy(&disk->part_tbl);
 
        kobject_put(&disk->queue_kobj);
index f3c4174e003e9d6e049657a7b7d1eb91db500fe8..689837deba279b1817f69edd2ce4246604e13564 100644 (file)
@@ -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;
index f1e37219527316ed0e90dfea3d7763606c56ccb4..b61401bd3ef6e87168a05ba14e1e0b1e79f539ba 100644 (file)
@@ -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)
 
index 54486960cbf5e688bceff79b7919864d9077456a..101f324ee1787ffa2c707fd2e341c3b65cb925a0 100644 (file)
@@ -875,7 +875,7 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size
        if (!hwctx->cus)
                return -ENOMEM;
 
-       ret = amdxdna_pm_resume_get_locked(xdna);
+       ret = amdxdna_pm_resume_get(xdna);
        if (ret)
                goto free_cus;
 
@@ -900,13 +900,16 @@ free_cus:
 static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
 {
        struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
+       struct amdxdna_dev *xdna = hwctx->client->xdna;
 
        if (!out_fence) {
-               XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
+               XDNA_ERR(xdna, "Failed to get fence");
                return;
        }
 
+       mutex_unlock(&xdna->dev_lock);
        dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
+       mutex_lock(&xdna->dev_lock);
        dma_fence_put(out_fence);
 }
 
@@ -1039,7 +1042,7 @@ again:
        found = false;
        down_write(&xdna->notifier_lock);
        list_for_each_entry(mapp, &abo->mem.umap_list, node) {
-               if (mapp->invalid) {
+               if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) {
                        found = true;
                        break;
                }
@@ -1050,11 +1053,9 @@ again:
                up_write(&xdna->notifier_lock);
                return 0;
        }
-       kref_get(&mapp->refcnt);
+
        up_write(&xdna->notifier_lock);
 
-       XDNA_DBG(xdna, "populate memory range %lx %lx",
-                mapp->vma->vm_start, mapp->vma->vm_end);
        mm = mapp->notifier.mm;
        if (!mmget_not_zero(mm)) {
                amdxdna_umap_put(mapp);
@@ -1221,10 +1222,6 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx,
        u64 addr;
        int ret;
 
-       ret = amdxdna_pm_resume_get_locked(xdna);
-       if (ret)
-               return ret;
-
        addr = amdxdna_obj_dma_addr(heap);
        ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
                                addr, heap->mem.size);
@@ -1233,7 +1230,5 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx,
                         hwctx->name, heap->mem.size, ret);
        }
 
-       amdxdna_pm_suspend_put(xdna);
-
        return ret;
 }
index c4b364801cc0456a153e15e26ef413e7f0f39303..dfe0fbdf066d2c6b00c6d4e01e54706c32d4af97 100644 (file)
@@ -840,7 +840,7 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = {
 static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
                              size_t *size, u32 *msg_op)
 {
-       struct amdxdna_dev *xdna = cmd_abo->client->xdna;
+       struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev);
        int ret;
        u32 op;
 
@@ -874,7 +874,7 @@ static int
 aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
                       size_t *size, u32 *cmd_op)
 {
-       struct amdxdna_dev *xdna = cmd_abo->client->xdna;
+       struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev);
        int ret;
        u32 op;
 
index 855da8c79a1cdcdf6df00f703d3a98f8e4189b43..31a414c3f0d967b92f08d4882eab24b4aae42c03 100644 (file)
@@ -310,6 +310,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d
        if (!drm_dev_enter(dev, &idx))
                return -ENODEV;
 
+       mutex_lock(&xdna->client_lock);
        mutex_lock(&xdna->dev_lock);
        hwctx = xa_erase(&client->hwctx_xa, args->handle);
        if (!hwctx) {
@@ -328,6 +329,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d
        XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle);
 out:
        mutex_unlock(&xdna->dev_lock);
+       mutex_unlock(&xdna->client_lock);
        drm_dev_exit(idx);
        return ret;
 }
@@ -382,16 +384,27 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
                return -EINVAL;
        }
 
-       guard(mutex)(&xdna->dev_lock);
+       ret = amdxdna_pm_resume_get(xdna);
+       if (ret) {
+               XDNA_ERR(xdna, "Resume failed, ret %d", ret);
+               goto free_buf;
+       }
+
+       mutex_lock(&xdna->client_lock);
+       mutex_lock(&xdna->dev_lock);
        hwctx = xa_load(&client->hwctx_xa, args->handle);
        if (!hwctx) {
                XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle);
                ret = -EINVAL;
-               goto free_buf;
+               goto unlock;
        }
 
        ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size);
 
+unlock:
+       mutex_unlock(&xdna->dev_lock);
+       mutex_unlock(&xdna->client_lock);
+       amdxdna_pm_suspend_put(xdna);
 free_buf:
        kfree(buf);
        return ret;
@@ -412,16 +425,27 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
        if (!gobj)
                return -EINVAL;
 
+       ret = amdxdna_pm_resume_get(xdna);
+       if (ret) {
+               XDNA_ERR(xdna, "Resume failed, ret %d", ret);
+               goto put_obj;
+       }
+
        abo = to_xdna_obj(gobj);
-       guard(mutex)(&xdna->dev_lock);
+       mutex_lock(&xdna->client_lock);
+       mutex_lock(&xdna->dev_lock);
        hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
        if (!hwctx) {
                ret = -EINVAL;
-               goto put_obj;
+               goto unlock;
        }
 
        ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
 
+unlock:
+       mutex_unlock(&xdna->dev_lock);
+       mutex_unlock(&xdna->client_lock);
+       amdxdna_pm_suspend_put(xdna);
 put_obj:
        drm_gem_object_put(gobj);
        return ret;
@@ -448,9 +472,7 @@ static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx)
                        break;
                }
 
-               mutex_unlock(&client->mm_lock);
                ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap);
-               mutex_lock(&client->mm_lock);
                if (ret) {
                        amdxdna_gem_unpin(heap);
                        drm_gem_object_put(to_gobj(heap));
@@ -469,18 +491,26 @@ int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwc
        unsigned long hwctx_id;
        int ret;
 
-       guard(mutex)(&client->mm_lock);
+       ret = amdxdna_pm_resume_get_locked(client->xdna);
+       if (ret)
+               return ret;
 
-       if (hwctx)
-               return amdxdna_hwctx_expand_heap(hwctx);
+       mutex_lock(&client->mm_lock);
 
-       amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
+       if (hwctx) {
                ret = amdxdna_hwctx_expand_heap(hwctx);
-               if (ret)
-                       return ret;
+       } else {
+               amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
+                       ret = amdxdna_hwctx_expand_heap(hwctx);
+                       if (ret)
+                               break;
+               }
        }
+       mutex_unlock(&client->mm_lock);
 
-       return 0;
+       amdxdna_pm_suspend_put(client->xdna);
+
+       return ret;
 }
 
 static void
@@ -547,6 +577,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
        amdxdna_arg_bos_put(job);
        amdxdna_gem_put_obj(job->cmd_bo);
        dma_fence_put(job->fence);
+       mmdrop(job->mm);
 }
 
 int amdxdna_cmd_submit(struct amdxdna_client *client,
@@ -560,6 +591,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
        int ret, idx;
 
        XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt);
+
+       if (!xdna->dev_info->ops->cmd_submit)
+               return -EOPNOTSUPP;
+
        job = kzalloc_flex(*job, bos, arg_bo_cnt);
        if (!job)
                return -ENOMEM;
@@ -573,6 +608,16 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
                        ret = -EINVAL;
                        goto free_job;
                }
+       } else if (!drv_cmd) {
+               /*
+                * Only internal driver commands (drv_cmd != NULL) may omit a
+                * command BO. A user command submission with the invalid handle
+                * would leave job->cmd_bo NULL and later fault when the scheduler
+                * dereferences it in amdxdna_cmd_set_state().
+                */
+               XDNA_DBG(xdna, "Command BO handle required for user submission");
+               ret = -EINVAL;
+               goto free_job;
        }
 
        ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
@@ -598,6 +643,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
 
        job->hwctx = hwctx;
        job->mm = current->mm;
+       mmgrab(job->mm);
 
        job->fence = amdxdna_fence_create(hwctx);
        if (!job->fence) {
@@ -632,6 +678,8 @@ put_bos:
 cmd_put:
        amdxdna_gem_put_obj(job->cmd_bo);
 free_job:
+       if (job->mm)
+               mmdrop(job->mm);
        kfree(job);
        return ret;
 }
index 891112c2cddfc5b46ab7c31301a0751fac7a84c5..4628a27872656fae56241d889d3074bda0a51a44 100644 (file)
@@ -198,6 +198,7 @@ amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo)
  */
 void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo)
 {
+       struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
        struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
        int ret;
 
@@ -210,7 +211,7 @@ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo)
        if (!abo->mem.kva) {
                ret = drm_gem_vmap(to_gobj(abo), &map);
                if (ret)
-                       XDNA_ERR(abo->client->xdna, "Vmap bo failed, ret %d", ret);
+                       XDNA_ERR(xdna, "Vmap bo failed, ret %d", ret);
                else
                        abo->mem.kva = map.vaddr;
        }
@@ -254,7 +255,7 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni,
 
        xdna = to_xdna_dev(to_gobj(abo)->dev);
        XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d",
-                mapp->vma->vm_start, mapp->vma->vm_end, abo->type);
+                mapp->range.start, mapp->range.end, abo->type);
 
        if (!mmu_notifier_range_blockable(range))
                return false;
@@ -284,15 +285,23 @@ static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = {
        .invalidate = amdxdna_hmm_invalidate,
 };
 
+static inline bool compare_range(struct amdxdna_umap *mapp,
+                                struct mm_struct *mm,
+                                unsigned long start, unsigned long end)
+{
+       return (!mapp->unmapped && mapp->notifier.mm == mm &&
+               mapp->range.start == start && mapp->range.end == end);
+}
+
 static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo,
                                   struct vm_area_struct *vma)
 {
        struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
        struct amdxdna_umap *mapp;
 
-       down_read(&xdna->notifier_lock);
+       down_write(&xdna->notifier_lock);
        list_for_each_entry(mapp, &abo->mem.umap_list, node) {
-               if (!vma || mapp->vma == vma) {
+               if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) {
                        if (!mapp->unmapped) {
                                queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work);
                                mapp->unmapped = true;
@@ -301,19 +310,16 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo,
                                break;
                }
        }
-       up_read(&xdna->notifier_lock);
+       up_write(&xdna->notifier_lock);
 }
 
 static void amdxdna_umap_release(struct kref *ref)
 {
        struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt);
        struct amdxdna_gem_obj *abo = mapp->abo;
-       struct vm_area_struct *vma = mapp->vma;
        struct amdxdna_dev *xdna;
 
        mmu_interval_notifier_remove(&mapp->notifier);
-       if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping)
-               mapping_clear_unevictable(vma->vm_file->f_mapping);
 
        xdna = to_xdna_dev(to_gobj(mapp->abo)->dev);
        down_write(&xdna->notifier_lock);
@@ -346,15 +352,30 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo,
        unsigned long len = vma->vm_end - vma->vm_start;
        unsigned long addr = vma->vm_start;
        struct amdxdna_umap *mapp;
-       u32 nr_pages;
+       unsigned long nr_pages;
        int ret;
 
-       if (!amdxdna_pasid_on(abo->client)) {
+       /*
+        * When PASID is off, amdxdna_gem_obj_open() called amdxdna_dma_map_bo()
+        * and mem.dma_addr is valid; use the DMA address directly and skip HMM.
+        * Avoid dereferencing abo->client which may be NULL (cleared in close())
+        * while internal kernel references are still held.
+        */
+       if (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) {
                /* Need to set uva for heap uva validation */
                abo->mem.uva = addr;
                return 0;
        }
 
+       down_read(&xdna->notifier_lock);
+       list_for_each_entry(mapp, &abo->mem.umap_list, node) {
+               if (compare_range(mapp, current->mm, addr, addr + len)) {
+                       up_read(&xdna->notifier_lock);
+                       return 0;
+               }
+       }
+       up_read(&xdna->notifier_lock);
+
        mapp = kzalloc_obj(*mapp);
        if (!mapp)
                return -ENOMEM;
@@ -380,13 +401,10 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo,
        mapp->range.start = vma->vm_start;
        mapp->range.end = vma->vm_end;
        mapp->range.default_flags = HMM_PFN_REQ_FAULT;
-       mapp->vma = vma;
        mapp->abo = abo;
        kref_init(&mapp->refcnt);
 
        INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work);
-       if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping)
-               mapping_set_unevictable(vma->vm_file->f_mapping);
 
        down_write(&xdna->notifier_lock);
        if (list_empty(&abo->mem.umap_list))
@@ -527,6 +545,7 @@ static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struc
 
 close_vma:
        vma->vm_ops->close(vma);
+       return ret;
 put_obj:
        drm_gem_object_put(gobj);
        return ret;
@@ -652,8 +671,11 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi
        /* No need to set up dma addr mapping in PASID mode. */
        if (!amdxdna_pasid_on(abo->client)) {
                ret = amdxdna_dma_map_bo(xdna, abo);
-               if (ret)
+               if (ret) {
+                       abo->open_ref--;
+                       abo->client = NULL;
                        return ret;
+               }
        }
 
        amdxdna_gem_add_bo_usage(abo);
index a3e44c7a23952e562eacd4a18c6e1ddc07f38a05..1e90e32bf3cd79d952c035fd44a83d84779ea2d0 100644 (file)
@@ -12,7 +12,6 @@
 #include "amdxdna_pci_drv.h"
 
 struct amdxdna_umap {
-       struct vm_area_struct           *vma;
        struct mmu_interval_notifier    notifier;
        struct hmm_range                range;
        struct work_struct              hmm_unreg_work;
@@ -89,12 +88,19 @@ u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo);
 
 static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
 {
-       return amdxdna_gem_dev_addr(abo) - abo->client->xdna->dev_info->dev_mem_base;
+       return amdxdna_gem_dev_addr(abo) - to_xdna_dev(to_gobj(abo)->dev)->dev_info->dev_mem_base;
 }
 
 static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo)
 {
-       return amdxdna_pasid_on(abo->client) ? amdxdna_gem_uva(abo) : abo->mem.dma_addr;
+       /*
+        * amdxdna_gem_obj_open() calls amdxdna_dma_map_bo() only when PASID is
+        * off, leaving mem.dma_addr at AMDXDNA_INVALID_ADDR when PASID is on.
+        * Avoid dereferencing abo->client, which is cleared to NULL by
+        * amdxdna_gem_obj_close() while internal kernel references remain.
+        */
+       return (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) ?
+               abo->mem.dma_addr : amdxdna_gem_uva(abo);
 }
 
 void amdxdna_umap_put(struct amdxdna_umap *mapp);
index e94d8290a80738ed11a574d8ea46c7ecab576eab..bb339e6414169f822b6aabffc1897a3e2babca3b 100644 (file)
@@ -109,11 +109,16 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
 {
        struct amdxdna_dev *xdna = to_xdna_dev(ddev);
        struct amdxdna_client *client;
+       int ret;
 
        client = kzalloc_obj(*client);
        if (!client)
                return -ENOMEM;
 
+       ret = init_srcu_struct(&client->hwctx_srcu);
+       if (ret)
+               goto free_client;
+
        client->pid = pid_nr(rcu_access_pointer(filp->pid));
        client->xdna = xdna;
        client->pasid = IOMMU_PASID_INVALID;
@@ -125,13 +130,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
                        XDNA_WARN(xdna, "PASID not available for pid %d", client->pid);
                        if (!amdxdna_use_carveout(xdna)) {
                                XDNA_ERR(xdna, "PASID unavailable and carveout not configured");
-                               kfree(client);
-                               return -EINVAL;
+                               ret = -EINVAL;
+                               goto cleanup_srcu;
                        }
                }
        }
        mmgrab(client->mm);
-       init_srcu_struct(&client->hwctx_srcu);
        xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC);
        xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC);
        drm_mm_init(&client->dev_heap_mm, xdna->dev_info->dev_mem_base,
@@ -149,6 +153,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
 
        XDNA_DBG(xdna, "pid %d opened", client->pid);
        return 0;
+
+cleanup_srcu:
+       cleanup_srcu_struct(&client->hwctx_srcu);
+free_client:
+       kfree(client);
+       return ret;
 }
 
 static void amdxdna_client_cleanup(struct amdxdna_client *client)
@@ -373,7 +383,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (ret)
                return ret;
 
-       drmm_mutex_init(ddev, &xdna->dev_lock);
+       ret = drmm_mutex_init(ddev, &xdna->dev_lock);
+       if (ret)
+               return ret;
+
        init_rwsem(&xdna->notifier_lock);
        INIT_LIST_HEAD(&xdna->client_list);
        pci_set_drvdata(pdev, xdna);
index 275baf844b562ce088ca948ba3420ec96eef343e..716467aa315670535ad704f65f0a17b3d9820ea1 100644 (file)
@@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off
                ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
                return -EINVAL;
        }
+       if (log->size < log->header_size) {
+               ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
+               return -EINVAL;
+       }
        if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
                ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
                return -EINVAL;
index dac935164e11b00c28c920c862e31108e1f9fa7c..a17c829adb89a6732bda34352f4aef0815879b44 100644 (file)
@@ -927,7 +927,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev)
 
 static void diagnose_failure_lnl(struct ivpu_device *vdev)
 {
-       u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
+       u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
 
        if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
                ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
index ec0ab4f285301452b1b90a2b3b43e2cc9893284e..8f2ef1bd539f18a2d5346749bd6b1a62f0fec271 100644 (file)
@@ -1658,7 +1658,20 @@ static void binder_txn_latency_free(struct binder_transaction *t)
 
 static void binder_free_transaction(struct binder_transaction *t)
 {
-       struct binder_proc *target_proc = t->to_proc;
+       struct binder_thread *target_thread;
+       struct binder_proc *target_proc;
+
+       spin_lock(&t->lock);
+       target_proc = t->to_proc;
+       target_thread = t->to_thread;
+       /*
+        * Pin target_thread to keep target_proc alive. Undelivered
+        * transactions with !target_thread are safe, as target_proc
+        * can only be the current context there.
+        */
+       if (target_thread)
+               atomic_inc(&target_thread->tmp_ref);
+       spin_unlock(&t->lock);
 
        if (target_proc) {
                binder_inner_proc_lock(target_proc);
@@ -1672,6 +1685,10 @@ static void binder_free_transaction(struct binder_transaction *t)
                        t->buffer->transaction = NULL;
                binder_inner_proc_unlock(target_proc);
        }
+
+       if (target_thread)
+               binder_thread_dec_tmpref(target_thread);
+
        if (trace_binder_txn_latency_free_enabled())
                binder_txn_latency_free(t);
        /*
@@ -3080,6 +3097,7 @@ static void binder_transaction(struct binder_proc *proc,
        int t_debug_id = atomic_inc_return(&binder_last_id);
        ktime_t t_start_time = ktime_get();
        struct lsm_context lsmctx = { };
+       size_t lsmctx_aligned_size = 0;
        LIST_HEAD(sgc_head);
        LIST_HEAD(pf_head);
        const void __user *user_buffer = (const void __user *)
@@ -3346,7 +3364,6 @@ static void binder_transaction(struct binder_proc *proc,
 
        if (target_node && target_node->txn_security_ctx) {
                u32 secid;
-               size_t added_size;
 
                security_cred_getsecid(proc->cred, &secid);
                ret = security_secid_to_secctx(secid, &lsmctx);
@@ -3358,9 +3375,9 @@ static void binder_transaction(struct binder_proc *proc,
                        return_error_line = __LINE__;
                        goto err_get_secctx_failed;
                }
-               added_size = ALIGN(lsmctx.len, sizeof(u64));
-               extra_buffers_size += added_size;
-               if (extra_buffers_size < added_size) {
+               lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64));
+               extra_buffers_size += lsmctx_aligned_size;
+               if (extra_buffers_size < lsmctx_aligned_size) {
                        binder_txn_error("%d:%d integer overflow of extra_buffers_size\n",
                                thread->pid, proc->pid);
                        return_error = BR_FAILED_REPLY;
@@ -3397,7 +3414,7 @@ static void binder_transaction(struct binder_proc *proc,
                size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
                                    ALIGN(tr->offsets_size, sizeof(void *)) +
                                    ALIGN(extra_buffers_size, sizeof(void *)) -
-                                   ALIGN(lsmctx.len, sizeof(u64));
+                                   lsmctx_aligned_size;
 
                t->security_ctx = t->buffer->user_data + buf_offset;
                err = binder_alloc_copy_to_buffer(&target_proc->alloc,
@@ -3452,7 +3469,7 @@ static void binder_transaction(struct binder_proc *proc,
        off_end_offset = off_start_offset + tr->offsets_size;
        sg_buf_offset = ALIGN(off_end_offset, sizeof(void *));
        sg_buf_end_offset = sg_buf_offset + extra_buffers_size -
-               ALIGN(lsmctx.len, sizeof(u64));
+               lsmctx_aligned_size;
        off_min = 0;
        for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
             buffer_offset += sizeof(binder_size_t)) {
index b7b05e72970a200eabe279460e67ef325b5f2df7..ea5846e4da16a6ac67e6e8d63dceafa371c1bf98 100644 (file)
@@ -259,7 +259,7 @@ impl Drop for Allocation {
 
             if let Some(offsets) = info.offsets.clone() {
                 let view = AllocationView::new(self, offsets.start);
-                for i in offsets.step_by(size_of::<usize>()) {
+                for i in offsets.step_by(size_of::<u64>()) {
                     if view.cleanup_object(i).is_err() {
                         pr_warn!("Error cleaning up object at offset {}\n", i)
                     }
@@ -420,7 +420,8 @@ impl<'a> AllocationView<'a> {
     }
 
     fn cleanup_object(&self, index_offset: usize) -> Result {
-        let offset = self.alloc.read(index_offset)?;
+        let offset = self.alloc.read::<u64>(index_offset)?;
+        let offset: usize = offset.try_into().map_err(|_| EINVAL)?;
         let header = self.read::<BinderObjectHeader>(offset)?;
         match header.type_ {
             BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => {
index 45d85d4c281597f6ff168b4f802371d8e707746f..1296072c35d96968343e279f5fcc89c3ea1d31dc 100644 (file)
@@ -73,20 +73,17 @@ impl fmt::Debug for BinderError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.reply {
             BR_FAILED_REPLY => match self.source.as_ref() {
-                Some(source) => f
-                    .debug_struct("BR_FAILED_REPLY")
-                    .field("source", source)
-                    .finish(),
+                Some(source) => source.fmt(f),
                 None => f.pad("BR_FAILED_REPLY"),
             },
             BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"),
             BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"),
             BR_TRANSACTION_PENDING_FROZEN => f.pad("BR_TRANSACTION_PENDING_FROZEN"),
             BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"),
-            _ => f
-                .debug_struct("BinderError")
-                .field("reply", &self.reply)
-                .finish(),
+            _ => match self.source.as_ref() {
+                Some(source) => source.fmt(f),
+                None => self.reply.fmt(f),
+            },
         }
     }
 }
index 53b60035639aa9e41c2c6a450953df949a3ce354..f4df14568b25243f4479b719a535bce7dcbaaa2b 100644 (file)
@@ -154,10 +154,17 @@ impl DeliverToRead for FreezeMessage {
 }
 
 impl FreezeListener {
-    pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
+    /// Called when this freeze listener is cleared abnormally.
+    ///
+    /// This occurs either because the process exited or because the process dropped its last
+    /// refcount on the node ref without explicitly removing the freeze listener first.
+    ///
+    /// The returned `KVVec` is just a value that should be dropped outside of the lock.
+    pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec<Arc<Process>> {
         if !self.is_clearing {
-            self.node.remove_freeze_listener(proc);
+            return self.node.remove_freeze_listener(proc);
         }
+        KVVec::new()
     }
 }
 
index 69f757ff7461e0fb6b2b36b5ade1deb53c0c9339..c10148e9069f3ba8ef53c5540dd811ee668a6805 100644 (file)
@@ -682,12 +682,13 @@ impl Node {
         }
     }
 
-    pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) {
-        let _unused_capacity;
+    pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> {
         let mut guard = self.owner.inner.lock();
         let inner = self.inner.access_mut(&mut guard);
         let len = inner.freeze_list.len();
-        inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p));
+        inner
+            .freeze_list
+            .retain(|proc| !core::ptr::eq::<Process>(&**proc, p));
         if len == inner.freeze_list.len() {
             pr_warn!(
                 "Could not remove freeze listener for {}\n",
@@ -695,8 +696,9 @@ impl Node {
             );
         }
         if inner.freeze_list.is_empty() {
-            _unused_capacity = mem::take(&mut inner.freeze_list);
+            return mem::take(&mut inner.freeze_list);
         }
+        KVVec::new()
     }
 
     pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc<Process>] {
index 96b8440ceac62d1ab3d15b5b539d6753b72059c0..cdd1a9079726673190a53e005f4cb1d391133ca3 100644 (file)
@@ -900,7 +900,11 @@ impl Process {
     pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult<NodeRef> {
         // When handle is zero, try to get the context manager.
         if handle == 0 {
-            Ok(self.ctx.get_manager_node(true)?)
+            let node_ref = self.ctx.get_manager_node(true)?;
+            if core::ptr::eq(self, &*node_ref.node.owner) {
+                return Err(EINVAL.into());
+            }
+            Ok(node_ref)
         } else {
             Ok(self.get_node_from_handle(handle, true)?)
         }
@@ -942,6 +946,8 @@ impl Process {
 
         // To preserve original binder behaviour, we only fail requests where the manager tries to
         // increment references on itself.
+        let _to_free_freeze_listener;
+        let _to_free_freeze_listener_cleanup;
         let mut refs = self.node_refs.lock();
         if let Some(info) = refs.by_handle.get_mut(&handle) {
             if info.node_ref().update(inc, strong) {
@@ -957,6 +963,14 @@ impl Process {
                 unsafe { info.node_ref2().node.remove_node_info(info) };
 
                 let id = info.node_ref().node.global_id();
+
+                if let Some(freeze) = *info.freeze() {
+                    if let Some(fl) = refs.freeze_listeners.remove(&freeze) {
+                        _to_free_freeze_listener_cleanup = fl.on_process_cleanup(&self);
+                        _to_free_freeze_listener = fl;
+                    }
+                }
+
                 refs.by_handle.remove(&handle);
                 refs.by_node.remove(&id);
                 refs.handle_is_present.release_id(handle as usize);
@@ -1380,7 +1394,7 @@ impl Process {
         // Clean up freeze listeners.
         let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners);
         for listener in freeze_listeners.values() {
-            listener.on_process_exit(&self);
+            listener.on_process_cleanup(&self);
         }
         drop(freeze_listeners);
 
index 488b1470060cc43f24345e9c32336134f96b0da0..5792aa59cc82507b8fd1933ea8893b7cea031af2 100644 (file)
@@ -28,6 +28,9 @@ const char * const binder_command_strings[] = {
        "BC_DEAD_BINDER_DONE",
        "BC_TRANSACTION_SG",
        "BC_REPLY_SG",
+       "BC_REQUEST_FREEZE_NOTIFICATION",
+       "BC_CLEAR_FREEZE_NOTIFICATION",
+       "BC_FREEZE_NOTIFICATION_DONE",
 };
 
 const char * const binder_return_strings[] = {
@@ -51,7 +54,9 @@ const char * const binder_return_strings[] = {
        "BR_FAILED_REPLY",
        "BR_FROZEN_REPLY",
        "BR_ONEWAY_SPAM_SUSPECT",
-       "BR_TRANSACTION_PENDING_FROZEN"
+       "BR_TRANSACTION_PENDING_FROZEN",
+       "BR_FROZEN_BINDER",
+       "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
 };
 
 #define CREATE_TRACE_POINTS
index ab75e9561cbf4d302d6c3667cdb74b8bbb8a5d30..ec81dc7747db8dc07de646f8ea86ab822f6c6661 100644 (file)
@@ -8,8 +8,8 @@ use crate::defs::*;
 use kernel::sync::atomic::{ordering::Relaxed, Atomic};
 use kernel::{ioctl::_IOC_NR, seq_file::SeqFile, seq_print};
 
-const BC_COUNT: usize = _IOC_NR(BC_REPLY_SG) as usize + 1;
-const BR_COUNT: usize = _IOC_NR(BR_TRANSACTION_PENDING_FROZEN) as usize + 1;
+const BC_COUNT: usize = _IOC_NR(BC_FREEZE_NOTIFICATION_DONE) as usize + 1;
+const BR_COUNT: usize = _IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) as usize + 1;
 
 pub(crate) static GLOBAL_STATS: BinderStats = BinderStats::new();
 
index 97d5f31e8fe3c51d6e0a600fdbcc5b5d2d3a70f8..3b85208139410d8bfea5817a55fc61f5bbea7d8b 100644 (file)
@@ -495,9 +495,16 @@ impl Thread {
         Ok(())
     }
 
+    pub(crate) fn clear_extended_error(&self, debug_id: usize) {
+        self.inner.lock().extended_error = ExtendedError::new(debug_id as u32, BR_OK, 0);
+    }
+
     pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result {
         let mut writer = data.writer();
-        let ee = self.inner.lock().extended_error;
+        let mut inner = self.inner.lock();
+        let ee = inner.extended_error;
+        inner.extended_error = ExtendedError::new(0, BR_OK, 0);
+        drop(inner);
         writer.write(&ee)?;
         Ok(())
     }
@@ -1109,7 +1116,10 @@ impl Thread {
             inner.pop_transaction_to_reply(thread.as_ref())
         } {
             let reply = Err(BR_DEAD_REPLY);
-            if !transaction.from.deliver_single_reply(reply, &transaction) {
+            if !transaction
+                .from
+                .deliver_single_reply(reply, &transaction, None)
+            {
                 break;
             }
 
@@ -1121,8 +1131,9 @@ impl Thread {
         &self,
         reply: Result<DLArc<Transaction>, u32>,
         transaction: &DArc<Transaction>,
+        extended_error: Option<ExtendedError>,
     ) {
-        if self.deliver_single_reply(reply, transaction) {
+        if self.deliver_single_reply(reply, transaction, extended_error) {
             transaction.from.unwind_transaction_stack();
         }
     }
@@ -1136,6 +1147,7 @@ impl Thread {
         &self,
         reply: Result<DLArc<Transaction>, u32>,
         transaction: &DArc<Transaction>,
+        extended_error: Option<ExtendedError>,
     ) -> bool {
         if let Ok(transaction) = &reply {
             crate::trace::trace_transaction(true, transaction, Some(&self.task));
@@ -1152,6 +1164,12 @@ impl Thread {
                 return true;
             }
 
+            if let Some(ee) = extended_error {
+                if inner.extended_error.command == BR_OK {
+                    inner.extended_error = ee;
+                }
+            }
+
             match reply {
                 Ok(work) => {
                     inner.push_work(work);
@@ -1222,6 +1240,9 @@ impl Thread {
         info.buffers_size = td.buffers_size as usize;
         // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
         info.target_handle = unsafe { td.transaction_data.target.handle };
+
+        info.debug_id = super::next_debug_id();
+
         Ok(())
     }
 
@@ -1230,6 +1251,8 @@ impl Thread {
         let mut info = TransactionInfo::zeroed();
         self.read_transaction_info(cmd, reader, &mut info)?;
 
+        self.clear_extended_error(info.debug_id);
+
         let ret = if info.is_reply {
             self.reply_inner(&mut info)
         } else if info.is_oneway() {
@@ -1239,23 +1262,21 @@ impl Thread {
         };
 
         if let Err(err) = ret {
-            if err.reply != BR_TRANSACTION_COMPLETE {
-                info.reply = err.reply;
-            }
-
             self.push_return_work(err.reply);
-            if let Some(source) = &err.source {
-                info.errno = source.to_errno();
+            if err.reply != BR_TRANSACTION_COMPLETE {
                 info.reply = err.reply;
+                if let Some(source) = &err.source {
+                    info.errno = source.to_errno();
 
-                {
-                    let mut ee = self.inner.lock().extended_error;
-                    ee.command = err.reply;
-                    ee.param = source.to_errno();
+                    {
+                        let mut inner = self.inner.lock();
+                        inner.extended_error =
+                            ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
+                    }
                 }
 
                 pr_warn!(
-                    "{}:{} transaction to {} failed: {source:?}",
+                    "{}:{} transaction to {} failed: {err:?}",
                     info.from_pid,
                     info.from_tid,
                     info.to_pid
@@ -1320,18 +1341,24 @@ impl Thread {
             let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
             let reply = Transaction::new_reply(self, process, info, allow_fds)?;
             self.inner.lock().push_work(completion);
-            orig.from.deliver_reply(Ok(reply), &orig);
+            orig.from.deliver_reply(Ok(reply), &orig, None);
             Ok(())
         })()
         .map_err(|mut err| {
             // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let
             // the sender know that the transaction has completed (with an error in this case).
+
             pr_warn!(
-                "Failure {:?} during reply - delivering BR_FAILED_REPLY to sender.",
-                err
+                "{}:{} reply to {} failed: {err:?}",
+                info.from_pid,
+                info.from_tid,
+                info.to_pid
             );
-            let reply = Err(BR_FAILED_REPLY);
-            orig.from.deliver_reply(reply, &orig);
+
+            let param = err.source.as_ref().map_or(0, |e| e.to_errno());
+            let ee = ExtendedError::new(info.debug_id as u32, err.reply, param);
+            orig.from
+                .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee));
             err.reply = BR_TRANSACTION_COMPLETE;
             err
         });
index 1d9b66920a21633616ace842e3b99c61ece5ece8..0e5d07b7e6f0a9c9b0758445fe85bc211c2ba3b1 100644 (file)
@@ -42,6 +42,7 @@ pub(crate) struct TransactionInfo {
     pub(crate) reply: u32,
     pub(crate) oneway_spam_suspect: bool,
     pub(crate) is_reply: bool,
+    pub(crate) debug_id: usize,
 }
 
 impl TransactionInfo {
@@ -93,7 +94,6 @@ impl Transaction {
         from: &Arc<Thread>,
         info: &mut TransactionInfo,
     ) -> BinderResult<DLArc<Self>> {
-        let debug_id = super::next_debug_id();
         let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0;
         let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0;
         let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None };
@@ -101,7 +101,7 @@ impl Transaction {
         let mut alloc = match from.copy_transaction_data(
             to.clone(),
             info,
-            debug_id,
+            info.debug_id,
             allow_fds,
             txn_security_ctx_off.as_mut(),
         ) {
@@ -128,7 +128,7 @@ impl Transaction {
         let data_address = alloc.ptr;
 
         Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
-            debug_id,
+            debug_id: info.debug_id,
             target_node: Some(target_node),
             from_parent,
             sender_euid: Kuid::current_euid(),
@@ -152,9 +152,8 @@ impl Transaction {
         info: &mut TransactionInfo,
         allow_fds: bool,
     ) -> BinderResult<DLArc<Self>> {
-        let debug_id = super::next_debug_id();
         let mut alloc =
-            match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) {
+            match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) {
                 Ok(alloc) => alloc,
                 Err(err) => {
                     pr_warn!("Failure in copy_transaction_data: {:?}", err);
@@ -165,7 +164,7 @@ impl Transaction {
             alloc.set_info_clear_on_drop();
         }
         Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
-            debug_id,
+            debug_id: info.debug_id,
             target_node: None,
             from_parent: None,
             sender_euid: Kuid::current_euid(),
@@ -394,7 +393,7 @@ impl DeliverToRead for Transaction {
         let send_failed_reply = ScopeGuard::new(|| {
             if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
                 let reply = Err(BR_FAILED_REPLY);
-                self.from.deliver_reply(reply, &self);
+                self.from.deliver_reply(reply, &self, None);
             }
             self.drop_outstanding_txn();
         });
@@ -478,7 +477,7 @@ impl DeliverToRead for Transaction {
         // If this is not a reply or oneway transaction, then send a dead reply.
         if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
             let reply = Err(BR_DEAD_REPLY);
-            self.from.deliver_reply(reply, &self);
+            self.from.deliver_reply(reply, &self, None);
         }
 
         self.drop_outstanding_txn();
index bdc88cf7470927e77c280a5335ed7a5a993e3410..c43bd28b20b1a3ef3dce433d17b0b77fd128aa16 100644 (file)
@@ -1338,7 +1338,7 @@ static int ata_hpa_resize(struct ata_device *dev)
        /* do we need to do it? */
        if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) ||
            !ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) ||
-           (dev->quirks & ATA_QUIRK_BROKEN_HPA))
+           (dev->quirks & ATA_QUIRK_BROKEN_HPA) || ata_id_is_locked(dev->id))
                return 0;
 
        /* read native max address */
@@ -3992,7 +3992,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
 
        /* verify n_sectors hasn't changed */
        if (dev->class != ATA_DEV_ATA || !n_sectors ||
-           dev->n_sectors == n_sectors)
+           dev->n_sectors == n_sectors || ata_id_is_locked(dev->id))
                return 0;
 
        /* n_sectors has changed */
index 4fc22ce4bd9aa2033edbd5ee1f979f463520cbd6..8e3fc713891afc927fb08e12128c29da35e8ce89 100644 (file)
@@ -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:
index 58b95bf4bdca6af14e312f017175deb31881517f..2135c14354a8579f8fbb20d0a5555cb497d0e70d 100644 (file)
@@ -1810,6 +1810,11 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req
                data_size -= digest_size;
        }
 
+       if (data_size < 0) {
+               drbd_err(peer_device, "Invalid data reply size\n");
+               return -EIO;
+       }
+
        /* optimistically update recv_cnt.  if receiving fails below,
         * we disconnect anyways, and counters will be reset. */
        peer_device->device->recv_cnt += data_size>>9;
index 310de0463beb149039119d1b931ae424c7f7b3f2..1faecef330092b199dcc81066e9164b0f76b15de 100644 (file)
@@ -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.
index 4f6d9e6521878fe9d80422b2940a368eb69611a9..c2c11f2a01e701dc8bad4ba806cbdb646f302864 100644 (file)
@@ -3584,6 +3584,7 @@ ublk_batch_auto_buf_reg(const struct ublk_batch_io *uc,
 #define UBLK_CMD_BATCH_TMP_BUF_SZ  (48 * 10)
 struct ublk_batch_io_iter {
        void __user *uaddr;
+       const u8 *kaddr;
        unsigned done, total;
        unsigned char elem_bytes;
        /* copy to this buffer from user space */
@@ -3632,7 +3633,10 @@ static int ublk_walk_cmd_buf(struct ublk_batch_io_iter *iter,
        while (iter->done < iter->total) {
                unsigned int len = min(sizeof(iter->buf), iter->total - iter->done);
 
-               if (copy_from_user(iter->buf, iter->uaddr + iter->done, len)) {
+               if (iter->kaddr) {
+                       memcpy(iter->buf, iter->kaddr + iter->done, len);
+               } else if (copy_from_user(iter->buf, iter->uaddr + iter->done,
+                                 len)) {
                        pr_warn("ublk%d: read batch cmd buffer failed\n",
                                        data->ub->dev_info.dev_id);
                        return -EFAULT;
@@ -3723,14 +3727,21 @@ static int ublk_handle_batch_prep_cmd(const struct ublk_batch_io_data *data)
                .total = uc->nr_elem * uc->elem_bytes,
                .elem_bytes = uc->elem_bytes,
        };
+       void *cmd_buf;
        int ret;
 
+       cmd_buf = vmemdup_user(iter.uaddr, iter.total);
+       if (IS_ERR(cmd_buf))
+               return PTR_ERR(cmd_buf);
+       iter.kaddr = cmd_buf;
+
        mutex_lock(&data->ub->mutex);
        ret = ublk_walk_cmd_buf(&iter, data, ublk_batch_prep_io);
 
        if (ret && iter.done)
                ublk_batch_revert_prep_cmd(&iter, data);
        mutex_unlock(&data->ub->mutex);
+       kvfree(cmd_buf);
        return ret;
 }
 
index f765970578f98bcd76d2a5f46834c66952f6de53..8dad7bf5f6641e6f1f89a809026d9b264c58c828 100644 (file)
@@ -2079,6 +2079,15 @@ static int blkfront_resume(struct xenbus_device *dev)
                        if (!shadow[j].request)
                                continue;
 
+                       /*
+                        * For requests split across multiple slots, process the
+                        * underlying request only once: skip the linked, sg-less
+                        * secondary slot.
+                        */
+                       if (shadow[j].associated_id != NO_ASSOCIATED_ID &&
+                           shadow[j].num_sg == 0)
+                               continue;
+
                        /*
                         * Get the bios in the request so we can re-queue them.
                         */
index 2ae38a321c4b3ce7d4ba832039f0427e887c213d..e63d1af250ec91d2e2a0b3a18cb4eac778f9ee49 100644 (file)
@@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev)
        if (IS_ERR(skb))
                return PTR_ERR(skb);
 
-       bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
+       /* Bounded print: the device controls skb->len. */
+       if (skb->len > 1) {
+               int len = skb->len - 1;
 
-       hci_set_fw_info(hdev, "%s", skb->data + 1);
+               bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
+               hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
+       }
 
        kfree_skb(skb);
        return 0;
index 9e39327dc1fe2e3d9fda4e2dc398baaab1309d06..2b7231be5973d399f7f2fde344032b2f3e394365 100644 (file)
@@ -2127,6 +2127,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
        if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
                return -ENODEV;
 
+       if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
+               return -ENODEV;
+
        /* Due to the fw limitation, the type header of the packet should be
         * 4 bytes unlike 1 byte for UART. In UART, the firmware can read
         * the first byte to get the packet type and redirect the rest of data
@@ -2485,7 +2488,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
        }
 }
 
-static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
 static void btintel_pcie_reset(struct hci_dev *hdev);
 
 static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
@@ -2596,12 +2598,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data)
        }
 }
 
+/*
+ * Issue a Function Level Reset and hand teardown/re-init off to the PCI
+ * core via device_reprobe(), mirroring the PLDR path's contract.
+ *
+ * Caller must hold pci_lock_rescan_remove() and must have already
+ * disabled interrupts and drained both rx_work and coredump_work.
+ */
+static int btintel_pcie_perform_flr(struct btintel_pcie_data *data)
+{
+       struct pci_dev *pdev = data->pdev;
+       int err;
+
+       /* pci_try_reset_function() avoids the device_lock ABBA against
+        * btintel_pcie_remove(): .remove() runs with device_lock held and
+        * then waits for this work via disable_work_sync(); the blocking
+        * pci_reset_function() would deadlock by trying to re-acquire
+        * device_lock here.
+        */
+       err = pci_try_reset_function(pdev);
+       if (err) {
+               BT_ERR("Failed resetting the pcie device (%d)", err);
+               return err;
+       }
+
+       /* device_reprobe() always detaches the driver first (running
+        * .remove(), which frees 'data'); any re-probe failure leaves the
+        * device unbound but 'data' is already gone, so just log it.
+        */
+       if (device_reprobe(&pdev->dev))
+               BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev));
+
+       return 0;
+}
+
 static void btintel_pcie_reset_work(struct work_struct *wk)
 {
        struct btintel_pcie_data *data =
                container_of(wk, struct btintel_pcie_data, reset_work);
        struct pci_dev *pdev = data->pdev;
-       int err;
 
        pci_lock_rescan_remove();
 
@@ -2621,60 +2656,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
        disable_work_sync(&data->coredump_work);
 
        bt_dev_dbg(data->hdev, "Release bluetooth interface");
+
+       /* Both reset paths follow the same contract: on success they
+        * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
+        * the coredump_work with disable count 0), so enable_work() must
+        * NOT be called on the success path. Only the FLR path can fail
+        * with 'data' still alive, in which case we balance the
+        * disable_work_sync() above so a later successful reset is not
+        * permanently blocked.
+        *
+        * pci_lock_rescan_remove() (held above) serializes against PCI
+        * device addition/removal (hotplug), so no device can be added to
+        * or removed from the bus list while this code runs.
+        */
        if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
-               /* This function holds pci_lock_rescan_remove(), which acquires
-                * pci_rescan_remove_lock. This mutex serializes against PCI device
-                * addition/removal (hotplug), so no device can be added to or
-                * removed from the bus list while this code runs.
-                *
-                * device_reprobe() inside btintel_pcie_perform_pldr() destroys
-                * 'data' via .remove(); a fresh probe re-INIT_WORKs the
-                * coredump_work with disable count 0, so we must not call
-                * enable_work() on this path.
-                */
                btintel_pcie_perform_pldr(data);
                goto out;
        }
-       btintel_pcie_release_hdev(data);
-
-       /* Use pci_try_reset_function() rather than pci_reset_function() to
-        * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core
-        * calls .remove() with device_lock held, and remove() then waits for
-        * this work via cancel_work_sync(); pci_reset_function() would in
-        * turn try to acquire the same device_lock, deadlocking both paths.
-        */
-       err = pci_try_reset_function(pdev);
-       if (err) {
-               BT_ERR("Failed resetting the pcie device (%d)", err);
-               goto out_enable;
-       }
 
-       btintel_pcie_enable_interrupts(data);
-       btintel_pcie_config_msix(data);
-
-       err = btintel_pcie_enable_bt(data);
-       if (err) {
-               BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
-                      err);
-               goto out_enable;
-       }
-
-       btintel_pcie_reset_ia(data);
-       btintel_pcie_start_rx(data);
-       data->flags = 0;
+       if (btintel_pcie_perform_flr(data))
+               enable_work(&data->coredump_work);
 
-       err = btintel_pcie_setup_hdev(data);
-       if (err) {
-               BT_ERR("Failed registering hdev (%d)", err);
-               goto out_enable;
-       }
-
-out_enable:
-       /* Balance disable_work_sync() above on every exit. Leaving the
-        * counter incremented on a failed reset would permanently disable
-        * coredump_work even after a later successful reset.
-        */
-       enable_work(&data->coredump_work);
 out:
        pci_dev_put(pdev);
        pci_unlock_rescan_remove();
index e7036a48ce4861ede5e9ed609fae719460704c8b..6a1cffe08d5f7e2042bb0cf9d57412d88a198eeb 100644 (file)
@@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
+       if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size ||
+           len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) {
+               bt_dev_err(hdev, "FW download out of bounds, ignoring request");
+               len = 0;
+               goto free_skb;
+       }
        serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
                                nxpdev->fw_dnld_v3_offset, len);
 
index 04ebe290bc784ea854ec7743bc753c3e41f556d3..10c496eaea2c18dc7db576ac01d35af92c60f088 100644 (file)
@@ -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);
index 49ecb18fea45f4152e0d755bf03cf7ed11b075dc..7f54d2d2d13a045f80264a7dc945e1d76064c13a 100644 (file)
@@ -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
index 47f4902b40b47dd31eacb62a11edeceb9b47d8f8..2ad42c3bbaac78ed74cbf1c7cc4200876bb444c1 100644 (file)
@@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev)
 
        BT_DBG("hdev %p tty %p", hdev, tty);
 
+       disable_work_sync(&hu->write_work);
+
        if (hu->tx_skb) {
                kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
        }
@@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev)
 
        percpu_up_read(&hu->proto_lock);
 
+       /* Resume TX. Also reschedule in case work was queued concurrently;
+        * this may schedule write_work although there's nothing to do.
+        */
+       enable_work(&hu->write_work);
+       clear_bit(HCI_UART_SENDING, &hu->tx_state);
+       if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+               hci_uart_tx_wakeup(hu);
+
        return 0;
 }
 
@@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev)
 /* Close device */
 static int hci_uart_close(struct hci_dev *hdev)
 {
-       struct hci_uart *hu = hci_get_drvdata(hdev);
-
        BT_DBG("hdev %p", hdev);
 
-       cancel_work_sync(&hu->write_work);
-
        hci_uart_flush(hdev);
        hdev->flush = NULL;
        return 0;
index b2d1ee3a3d115e2cd51e18d7daaae96fd503aae2..1222f97800f4a1d33fc0f1c9165ea753bb3119a7 100644 (file)
@@ -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;
index 2779a8738c59edf722f9539855616831c2b948a2..74488f0a7b789b00be36978bba9884e4cfc1b846 100644 (file)
@@ -36,7 +36,7 @@ static int tpm_open(struct inode *inode, struct file *file)
 
        tpm_common_open(file, chip, priv, NULL);
 
-       return 0;
+       return nonseekable_open(inode, file);
 
  out:
        clear_bit(0, &chip->is_open);
index f48d4d9e179cdb40c03604842401935bbc38a4b8..19e8f2779265f2b3277c81ae797bd32b60008fa0 100644 (file)
@@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file)
 
        tpm_common_open(file, chip, &priv->priv, &priv->space);
 
-       return 0;
+       return nonseekable_open(inode, file);
 }
 
 static int tpmrm_release(struct inode *inode, struct file *file)
index 507224c9ecd38e92887c330ad77c502422bbfb63..b898b6544069f80e7cfb5a805521649db6740eba 100644 (file)
@@ -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;
 
        /*
index 5a0eeb84d3821db7c645015cb873421abe7d2cf5..6e984c114d96f831bbf5eaf863faa65f16c11ca2 100644 (file)
@@ -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)
index ec3b48cb0e8742ed07f190ab7a09591d358e0ab1..0f2e093111526b0f9bbb4e767e49e1202f320a54 100644 (file)
@@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok,
                read_unlock_bh(&ldev->dmb_ht_lock);
                return -EINVAL;
        }
+       if ((u64)offset + size > rmb_node->len) {
+               read_unlock_bh(&ldev->dmb_ht_lock);
+               return -EINVAL;
+       }
+
        memcpy((char *)rmb_node->cpu_addr + offset, data, size);
        sba_idx = rmb_node->sba_idx;
        read_unlock_bh(&ldev->dmb_ht_lock);
index bced421c0d658964cc5dc7ed3a46cc311144ac78..08f57bc1294df4bfbc3560ebd7977cce84495282 100644 (file)
@@ -224,21 +224,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
 {
        struct udmabuf *ubuf = buf->priv;
        struct device *dev = ubuf->device->this_device;
-       int ret = 0;
 
        if (!ubuf->sg) {
                ubuf->sg = get_sg_table(dev, buf, direction);
                if (IS_ERR(ubuf->sg)) {
+                       int ret;
+
                        ret = PTR_ERR(ubuf->sg);
                        ubuf->sg = NULL;
+                       return ret;
                } else {
                        ubuf->sg_dir = direction;
                }
-       } else {
-               dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
        }
 
-       return ret;
+       dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
+       return 0;
 }
 
 static int end_cpu_udmabuf(struct dma_buf *buf,
index bf729cde796a77a35a5da50d977174662a0046ef..5703667593a7c238d147d3c8d5fd69ffa0a8e597 100644 (file)
@@ -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,
index 0f468362c288588e588a692fd30ce0c99f79d381..8654b3365c9b6fda68d8309ec5b63804e81b5c9b 100644 (file)
@@ -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);
index e3fb36825978ed6fc9716f5c7f888b1d7547b587..783c24a20e29190fa9c71b28d8d5e0065fc68b2c 100644 (file)
@@ -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
index 42e666a628c732e531339df45f1b47b684f3efdc..0278705d809e8ff6b42efd9d966672aeb146d900 100644 (file)
@@ -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;
 }
index 40ec184eedaecc31c9f8b67c607dfbaae23fc911..0a192cf2deab62471d85b204a2d48ad12f2276b6 100644 (file)
@@ -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;
        }
 
index 7b92b233fafe6d3f37a8239fa983530f68f254a3..aa7c08e60707469a4074a652c80ab30aa90925bc 100644 (file)
@@ -117,6 +117,7 @@ struct dwapb_gpio {
        unsigned int            flags;
        struct reset_control    *rst;
        struct clk_bulk_data    clks[DWAPB_NR_CLOCKS];
+       bool                    clocks_on_for_wake;
        struct dwapb_gpio_port  ports[] __counted_by(nr_ports);
 };
 
@@ -199,6 +200,22 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
        dwapb_write(gpio, GPIO_INT_POLARITY, pol);
 }
 
+static int dwapb_irq_init_hw(struct gpio_chip *gc)
+{
+       struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
+
+       /*
+        * GPIO interrupts may retain stale state across warm reboots when
+        * peripherals stay powered. Force a known-safe state before the GPIO
+        * irqchip and irq domain are set up.
+        */
+       dwapb_write(gpio, GPIO_INTEN, 0);
+       dwapb_write(gpio, GPIO_INTMASK, 0xffffffff);
+       dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
+
+       return 0;
+}
+
 static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
 {
        struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip;
@@ -364,11 +381,24 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
        struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
        struct dwapb_context *ctx = gpio->ports[0].ctx;
        irq_hw_number_t bit = irqd_to_hwirq(d);
+       u32 wake_en = ctx->wake_en;
 
        if (enable)
-               ctx->wake_en |= BIT(bit);
+               wake_en |= BIT(bit);
        else
-               ctx->wake_en &= ~BIT(bit);
+               wake_en &= ~BIT(bit);
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+       if (d->parent_data && !!ctx->wake_en != !!wake_en) {
+               int err;
+
+               err = irq_chip_set_wake_parent(d, enable);
+               if (err)
+                       return err;
+       }
+#endif
+
+       ctx->wake_en = wake_en;
 
        return 0;
 }
@@ -457,6 +487,7 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
        girq = &gc->irq;
        girq->handler = handle_bad_irq;
        girq->default_type = IRQ_TYPE_NONE;
+       girq->init_hw = dwapb_irq_init_hw;
 
        port->pirq = pirq;
 
@@ -749,6 +780,8 @@ static int dwapb_gpio_suspend(struct device *dev)
        int i;
 
        scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
+               gpio->clocks_on_for_wake = false;
+
                for (i = 0; i < gpio->nr_ports; i++) {
                        unsigned int offset;
                        unsigned int idx = gpio->ports[i].idx;
@@ -770,11 +803,38 @@ static int dwapb_gpio_suspend(struct device *dev)
                                ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
                                ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
                                ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int dwapb_gpio_suspend_noirq(struct device *dev)
+{
+       struct dwapb_gpio *gpio = dev_get_drvdata(dev);
+       struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip;
+       bool wake_enabled = false;
+       int i;
 
-                               /* Mask out interrupts */
+       scoped_guard(gpio_generic_lock_irqsave, gen_gc) {
+               for (i = 0; i < gpio->nr_ports; i++) {
+                       unsigned int idx = gpio->ports[i].idx;
+                       struct dwapb_context *ctx = gpio->ports[i].ctx;
+
+                       if (idx == 0) {
+                               wake_enabled = ctx->wake_en;
                                dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en);
+                               break;
                        }
                }
+
+               gpio->clocks_on_for_wake = wake_enabled;
+       }
+
+       if (wake_enabled) {
+               device_set_wakeup_path(dev);
+               return 0;
        }
 
        clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
@@ -782,18 +842,27 @@ static int dwapb_gpio_suspend(struct device *dev)
        return 0;
 }
 
-static int dwapb_gpio_resume(struct device *dev)
+static int dwapb_gpio_resume_noirq(struct device *dev)
 {
        struct dwapb_gpio *gpio = dev_get_drvdata(dev);
-       struct gpio_chip *gc = &gpio->ports[0].chip.gc;
-       struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
-       int i, err;
+       int err;
+
+       if (gpio->clocks_on_for_wake)
+               return 0;
 
        err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks);
-       if (err) {
+       if (err)
                dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n");
-               return err;
-       }
+
+       return err;
+}
+
+static int dwapb_gpio_resume(struct device *dev)
+{
+       struct dwapb_gpio *gpio = dev_get_drvdata(dev);
+       struct gpio_chip *gc = &gpio->ports[0].chip.gc;
+       struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc);
+       int i;
 
        guard(gpio_generic_lock_irqsave)(gen_gc);
 
@@ -827,8 +896,11 @@ static int dwapb_gpio_resume(struct device *dev)
        return 0;
 }
 
-static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops,
-                               dwapb_gpio_suspend, dwapb_gpio_resume);
+static const struct dev_pm_ops dwapb_gpio_pm_ops = {
+       SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend, dwapb_gpio_resume)
+       NOIRQ_SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend_noirq,
+                                 dwapb_gpio_resume_noirq)
+};
 
 static struct platform_driver dwapb_gpio_driver = {
        .driver         = {
index 689dc6354c2d78f897f4f9c5c4d1d2d5de7d9f8f..a010604e5ff795a54735131dfa0f1bb68fef8e39 100644 (file)
@@ -1110,6 +1110,7 @@ static void mvebu_gpio_remove_irq_domain(void *data)
 {
        struct irq_domain *domain = data;
 
+       irq_domain_remove_generic_chips(domain);
        irq_domain_remove(domain);
 }
 
index e377f6dd4ccf131a08b1268aeb56ca9ff69daa0e..e64ee048771852fd858483cfa81712324e2df368 100644 (file)
@@ -116,6 +116,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
        return ret;
 }
 
+static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+       struct palmas_gpio *pg = gpiochip_get_data(gc);
+       struct palmas *palmas = pg->palmas;
+       unsigned int val;
+       unsigned int reg;
+       int ret;
+       int gpio16 = (offset/8);
+
+       offset %= 8;
+       reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+       ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
+       if (ret)
+               return ret;
+
+       return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
 static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
 {
        struct palmas_gpio *pg = gpiochip_get_data(gc);
@@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev)
        palmas_gpio->gpio_chip.can_sleep = true;
        palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
        palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+       palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction;
        palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
        palmas_gpio->gpio_chip.set      = palmas_gpio_set;
        palmas_gpio->gpio_chip.get      = palmas_gpio_get;
index 0f39d23ea9cba644cd105fc7e7c021fe87513dd9..bc69b8729d1967846cd2e2af6845badc7ba92e8f 100644 (file)
@@ -21,7 +21,7 @@ struct gpio_shared_proxy_data {
        struct gpio_chip gc;
        struct gpio_shared_desc *shared_desc;
        struct device *dev;
-       bool voted_high;
+       bool voted_change;
 };
 
 static int
@@ -33,52 +33,54 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value)
 
        lockdep_assert_held(&shared_desc->mutex);
 
-       if (value) {
-              /* User wants to set value to high. */
-               if (proxy->voted_high)
-                       /* Already voted for high, nothing to do. */
+       if (value != shared_desc->def_val) {
+              /* User wants to vote for a value change. */
+               if (proxy->voted_change)
+                       /* Already voted for a change, nothing to do. */
                        goto out;
 
-               /* Haven't voted for high yet. */
-               if (!shared_desc->highcnt) {
+               /* Haven't voted for a value change yet. */
+               if (!shared_desc->votecnt) {
                        /*
-                        * Current value is low, need to actually set value
-                        * to high.
+                        * Current value is default, need to actually set value
+                        * to the opposite.
                         */
-                       ret = gpiod_set_value_cansleep(desc, 1);
+                       ret = gpiod_set_value_cansleep(desc, value);
                        if (ret)
                                goto out;
                }
 
-               shared_desc->highcnt++;
-               proxy->voted_high = true;
+               shared_desc->votecnt++;
+               proxy->voted_change = true;
 
                goto out;
        }
 
-       /* Desired value is low. */
-       if (!proxy->voted_high)
-               /* We didn't vote for high, nothing to do. */
+       /* Desired value is the default. */
+       if (!proxy->voted_change)
+               /* We didn't vote for change previously, nothing to do. */
                goto out;
 
-       /* We previously voted for high. */
-       if (shared_desc->highcnt == 1) {
-               /* This is the last remaining vote for high, set value  to low. */
-               ret = gpiod_set_value_cansleep(desc, 0);
+       /* We previously voted for change. */
+       if (shared_desc->votecnt == 1) {
+               /* This is the last remaining vote for change, set value to default. */
+               ret = gpiod_set_value_cansleep(desc, shared_desc->def_val);
                if (ret)
                        goto out;
        }
 
-       shared_desc->highcnt--;
-       proxy->voted_high = false;
+       shared_desc->votecnt--;
+       proxy->voted_change = false;
 
 out:
-       if (shared_desc->highcnt)
+       if (shared_desc->votecnt)
                dev_dbg(proxy->dev,
-                       "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
-                       str_high_low(value), shared_desc->highcnt);
+                       "Voted for value '%s', effective value is '%s', number of votes: %u\n",
+                       str_high_low(value), str_high_low(!shared_desc->def_val),
+                       shared_desc->votecnt);
        else
-               dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
+               dev_dbg(proxy->dev, "Voted for value '%s', effective value is '%s'\n",
+                       str_high_low(value), str_high_low(shared_desc->def_val));
 
        return ret;
 }
@@ -106,8 +108,8 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
 
        guard(mutex)(&shared_desc->mutex);
 
-       if (proxy->voted_high) {
-               ret = gpio_shared_proxy_set_unlocked(proxy, 0);
+       if (proxy->voted_change) {
+               ret = gpio_shared_proxy_set_unlocked(proxy, shared_desc->def_val);
                if (ret)
                        dev_err(proxy->dev,
                                "Failed to unset the shared GPIO value on release: %d\n", ret);
@@ -196,13 +198,9 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
                if (ret)
                        return ret;
 
-               if (value) {
-                       proxy->voted_high = true;
-                       shared_desc->highcnt = 1;
-               } else {
-                       proxy->voted_high = false;
-                       shared_desc->highcnt = 0;
-               }
+               shared_desc->def_val = value;
+               shared_desc->votecnt = 0;
+               proxy->voted_change = false;
 
                return 0;
        }
index bbdc0ab7b647a44fca714bed0a7ff75101da7460..618756f6c6aafd087df267a4a32732ccc03af38e 100644 (file)
@@ -41,7 +41,8 @@ struct gpio_shared_desc {
        struct gpio_desc *desc;
        unsigned long cfg;
        unsigned int usecnt;
-       unsigned int highcnt;
+       unsigned int votecnt;
+       int def_val;
        struct mutex mutex; /* serializes all proxy operations on this descriptor */
 };
 
index eb1457376307d3d4f2a5458f8c2a48770f6f9b7b..b12d3a2ac630d94e8485757184c48356ed1e3cf5 100644 (file)
@@ -1084,22 +1084,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm,
                             blocks, total_allocated_on_err);
 }
 
+static int __alloc_contig_aligned_retry(struct gpu_buddy *mm,
+                                       u64 unaligned_offset,
+                                       u64 size,
+                                       u64 min_block_size,
+                                       struct list_head *blocks)
+{
+       u64 aligned_offset = round_down(unaligned_offset, min_block_size);
+
+       return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks);
+}
+
 static int __alloc_contig_try_harder(struct gpu_buddy *mm,
                                     u64 size,
                                     u64 min_block_size,
                                     struct list_head *blocks)
 {
-       u64 rhs_offset, lhs_offset, lhs_size, filled;
+       u64 rhs_offset, lhs_offset, filled;
        struct gpu_buddy_block *block;
        unsigned int tree, order;
-       LIST_HEAD(blocks_lhs);
-       unsigned long pages;
        u64 modify_size;
        int err;
 
        modify_size = rounddown_pow_of_two(size);
-       pages = modify_size >> ilog2(mm->chunk_size);
-       order = fls(pages) - 1;
+       order = ilog2(modify_size) - ilog2(mm->chunk_size);
        if (order == 0)
                return -ENOSPC;
 
@@ -1115,31 +1123,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm,
                while (iter) {
                        block = rbtree_get_free_block(iter);
 
-                       /* Allocate blocks traversing RHS */
                        rhs_offset = gpu_buddy_block_offset(block);
+
+                       /* Allocate blocks traversing RHS */
                        err =  __gpu_buddy_alloc_range(mm, rhs_offset, size,
                                                       &filled, blocks);
-                       if (!err || err != -ENOSPC)
+                       if (err && err != -ENOSPC)
                                return err;
+                       if (!err && IS_ALIGNED(rhs_offset, min_block_size))
+                               return 0;
+                       if (!err) {
+                               /* Allocate the unaligned RHS offset using round_down */
+                               gpu_buddy_free_list_internal(mm, blocks);
+                               err = __alloc_contig_aligned_retry(mm, rhs_offset,
+                                                                  size,
+                                                                  min_block_size,
+                                                                  blocks);
+                               if (!err)
+                                       return 0;
+                               if (err != -ENOSPC) {
+                                       gpu_buddy_free_list_internal(mm, blocks);
+                                       return err;
+                               }
+                               goto next;
+                       }
 
-                       lhs_size = max((size - filled), min_block_size);
-                       if (!IS_ALIGNED(lhs_size, min_block_size))
-                               lhs_size = round_up(lhs_size, min_block_size);
+                       if (size - filled > rhs_offset)
+                               goto next;
 
-                       /* Allocate blocks traversing LHS */
-                       lhs_offset = gpu_buddy_block_offset(block) - lhs_size;
-                       err =  __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size,
-                                                      NULL, &blocks_lhs);
-                       if (!err) {
-                               list_splice(&blocks_lhs, blocks);
+                       lhs_offset = rhs_offset - (size - filled);
+
+                       /* Allocate the unaligned LHS offset using round_down */
+                       gpu_buddy_free_list_internal(mm, blocks);
+                       err = __alloc_contig_aligned_retry(mm, lhs_offset, size,
+                                                          min_block_size, blocks);
+                       if (!err)
                                return 0;
-                       } else if (err != -ENOSPC) {
+                       if (err != -ENOSPC) {
                                gpu_buddy_free_list_internal(mm, blocks);
                                return err;
                        }
-                       /* Free blocks for the next iteration */
+next:
                        gpu_buddy_free_list_internal(mm, blocks);
-
                        iter = rb_prev(iter);
                }
        }
index aa039e148a5ee795fa5635b6c684691c64a5e80c..8525c45ab209b75fb33ee4edf7daf20cadfc4f90 100644 (file)
@@ -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)
index e77db76b48b8d31061e97a84c32bfcfbad42c76d..6480a344006d31299a7cc9a8aade5ef4eafe16f6 100644 (file)
@@ -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;
index 2371e20fc68bbccb91c067023e299e66437b92aa..63f27337c09ad816d0d8a1d600794d967b235872 100644 (file)
@@ -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);
 };
index 8d6502a9430671bfec6dee6dc8fce5708fd9bde5..e5f26e5892bac758e0bf870d292066fb974bdc93 100644 (file)
@@ -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;
 }
@@ -1333,7 +1342,8 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev)
         * It's unclear if this is a platform-specific or GPU-specific issue.
         * Disable ASPM on SI for the time being.
         */
-       if (adev->family == AMDGPU_FAMILY_SI)
+       if (adev->family == AMDGPU_FAMILY_SI ||
+               (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK) && adev->family == AMDGPU_FAMILY_VI))
                return true;
 
 #if IS_ENABLED(CONFIG_X86)
index 853365dee2a791a1075939a208820b078fc1298c..2860f12915c02a5bae524ab3b2700a979ebaab4b 100644 (file)
@@ -2304,6 +2304,7 @@ static int amdgpu_discovery_set_psp_ip_blocks(struct amdgpu_device *adev)
                amdgpu_device_ip_block_add(adev, &psp_v14_0_ip_block);
                break;
        case IP_VERSION(15, 0, 0):
+       case IP_VERSION(15, 0, 9):
                amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block);
                break;
        case IP_VERSION(15, 0, 8):
@@ -2375,6 +2376,7 @@ static int amdgpu_discovery_set_smu_ip_blocks(struct amdgpu_device *adev)
        case IP_VERSION(15, 0, 0):
        case IP_VERSION(15, 0, 5):
        case IP_VERSION(15, 0, 8):
+       case IP_VERSION(15, 0, 9):
                amdgpu_device_ip_block_add(adev, &smu_v15_0_ip_block);
                break;
        default:
@@ -3135,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):
index f98bfba59a2ceeffd7803d8a6648853cad6c910d..718937777e5323723a96f33492cf7e3594914269 100644 (file)
@@ -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);
 
index 96e1b72b9e1c703e287aaca7f0c7bd4c1b479d6c..e0c0d7872e452bb31c8203d6765dbae622d3fbd2 100644 (file)
@@ -275,6 +275,7 @@ static int psp_early_init(struct amdgpu_ip_block *ip_block)
                psp->boot_time_tmr = false;
                break;
        case IP_VERSION(15, 0, 0):
+       case IP_VERSION(15, 0, 9):
                psp_v15_0_0_set_psp_funcs(psp);
                psp->boot_time_tmr = false;
                break;
@@ -3475,7 +3476,9 @@ static int psp_load_non_psp_fw(struct psp_context *psp)
                     amdgpu_ip_version(adev, MP0_HWIP, 0) ==
                             IP_VERSION(15, 0, 0) ||
                     amdgpu_ip_version(adev, MP0_HWIP, 0) ==
-                            IP_VERSION(15, 0, 8)) &&
+                            IP_VERSION(15, 0, 8) ||
+                    amdgpu_ip_version(adev, MP0_HWIP, 0) ==
+                            IP_VERSION(15, 0, 9)) &&
                    (ucode->ucode_id == AMDGPU_UCODE_ID_SDMA1 ||
                     ucode->ucode_id == AMDGPU_UCODE_ID_SDMA2 ||
                     ucode->ucode_id == AMDGPU_UCODE_ID_SDMA3))
index 025625e7e800fcedef5b6c714df9ea7a97e42731..eb8bbfc7e6d9b999ea772b413bb6cd814c2b5c06 100644 (file)
@@ -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)
index ef3f0213cc465bcbdfac145ec1106a87346f118c..572f2949cb64eaad1d4cde711327dd112c36a951 100644 (file)
@@ -523,6 +523,15 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
        amdgpu_userq_cleanup(queue);
        mutex_unlock(&uq_mgr->userq_mutex);
 
+       /*
+        * A failed unmap means MES could not remove the hung queue and is now
+        * unresponsive.  Recover the GPU here so the wedged MES does not fail
+        * the next, unrelated queue submission and trigger a reset attributed
+        * to an innocent workload.
+        */
+       if (r)
+               queue_work(adev->reset_domain->wq, &uq_mgr->reset_work);
+
        cancel_delayed_work_sync(&queue->hang_detect_work);
        uq_funcs->mqd_destroy(queue);
        queue->userq_mgr = NULL;
@@ -1367,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);
        }
 }
index f317f888b59f5f78ef09996b1514b3e08859b2cd..f224d33c4bc4981bcc396733d951822b9cbaa6b7 100644 (file)
@@ -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);
@@ -2460,19 +2458,6 @@ static void amdgpu_vm_destroy_task_info(struct kref *kref)
        kfree(ti);
 }
 
-static inline struct amdgpu_vm *
-amdgpu_vm_get_vm_from_pasid(struct amdgpu_device *adev, u32 pasid)
-{
-       struct amdgpu_vm *vm;
-       unsigned long flags;
-
-       xa_lock_irqsave(&adev->vm_manager.pasids, flags);
-       vm = xa_load(&adev->vm_manager.pasids, pasid);
-       xa_unlock_irqrestore(&adev->vm_manager.pasids, flags);
-
-       return vm;
-}
-
 /**
  * amdgpu_vm_put_task_info - reference down the vm task_info ptr
  *
@@ -2519,8 +2504,16 @@ amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm)
 struct amdgpu_task_info *
 amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid)
 {
-       return amdgpu_vm_get_task_info_vm(
-                       amdgpu_vm_get_vm_from_pasid(adev, pasid));
+       struct amdgpu_task_info *ti;
+       struct amdgpu_vm *vm;
+       unsigned long flags;
+
+       xa_lock_irqsave(&adev->vm_manager.pasids, flags);
+       vm = xa_load(&adev->vm_manager.pasids, pasid);
+       ti = amdgpu_vm_get_task_info_vm(vm);
+       xa_unlock_irqrestore(&adev->vm_manager.pasids, flags);
+
+       return ti;
 }
 
 static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm)
@@ -3015,6 +3008,8 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
        is_compute_context = vm->is_compute_context;
 
        if (is_compute_context) {
+               __label__ drm_exec_retry;
+
                /* Release the root PD lock since svm_range_restore_pages
                 * might try to take it.
                 * TODO: rework svm_range_restore_pages so that this isn't
index b4b27e4c495d20be51433e10eae91e8536852e66..a9961d5048333812873eb2c171f7fc56acf3d7ee 100644 (file)
@@ -5350,6 +5350,15 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev)
        gfx_v10_0_get_tcc_info(adev);
        adev->gfx.config.pa_sc_tile_steering_override =
                gfx_v10_0_init_pa_sc_tile_steering_override(adev);
+       /* Program DB_RING_CONTROL for multiple GFX pipes
+        * Default power up value is 1.
+        * Possible values:
+        * 0 - split occlusion counters between gfx pipes
+        * 1 - all occlusion counters to pipe 0
+        * 2 - all occlusion counters to pipe 1
+        */
+       WREG32_FIELD15(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL,
+                      (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1);
 
        /* XXX SH_MEM regs */
        /* where to put LDS, scratch, GPUVM in FSA64 space */
index 1677e88a4e3639f11798129d0be57a2be2af2c5d..e0b80abcd0750f52a03e39a17923cbeea4475351 100644 (file)
@@ -406,6 +406,7 @@ soc21_asic_reset_method(struct amdgpu_device *adev)
        case IP_VERSION(14, 0, 4):
        case IP_VERSION(14, 0, 5):
        case IP_VERSION(15, 0, 0):
+       case IP_VERSION(15, 0, 9):
                return AMD_RESET_METHOD_MODE2;
        default:
                if (amdgpu_dpm_is_baco_supported(adev))
@@ -861,7 +862,6 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block)
                        AMD_CG_SUPPORT_BIF_LS;
                adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG |
                        AMD_PG_SUPPORT_VCN |
-                       AMD_PG_SUPPORT_JPEG_DPG |
                        AMD_PG_SUPPORT_JPEG |
                        AMD_PG_SUPPORT_GFX_PG;
                adev->external_rev_id = adev->rev_id + 0xF;
@@ -889,7 +889,6 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block)
                        AMD_CG_SUPPORT_BIF_LS;
                adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG |
                        AMD_PG_SUPPORT_VCN |
-                       AMD_PG_SUPPORT_JPEG_DPG |
                        AMD_PG_SUPPORT_JPEG |
                        AMD_PG_SUPPORT_GFX_PG;
                adev->external_rev_id = adev->rev_id + 0x40;
index 2e010c1f88285dfd62ffc714d2fe0b5748213610..67137679a901b72fb87756f83bea16f9f1cd2838 100644 (file)
@@ -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;
 }
@@ -3818,6 +3822,12 @@ out:
        dqm_unlock(dqm);
        return r;
 }
+
+size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, enum kfd_queue_type type)
+{
+       return dqm->mqd_mgrs[get_mqd_type_from_queue_type(type)]->mqd_size;
+}
+
 #if defined(CONFIG_DEBUG_FS)
 
 static void seq_reg_dump(struct seq_file *m,
index e0b6a47e7722b9b66ea7e25ec3c9a7e6744cb5a9..641b8ada82a037c4664bdb4afd485026dbfc38cd 100644 (file)
@@ -333,6 +333,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm);
 bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm,
                                 struct qcm_process_device *qpd,
                                 int doorbell_off, u32 *queue_format);
+size_t mqd_size_from_queue_type(struct device_queue_manager *dqm,
+                               enum kfd_queue_type type);
 
 static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
 {
index 81900b49d9d5b5a8d499d8f67afa8943bcebbe70..2e97da597b3da62bdd44e901f9a7978c2fcd956e 100644 (file)
@@ -107,6 +107,9 @@ static int allocate_event_notification_slot(struct kfd_process *p,
        }
 
        if (restore_id) {
+               if (*restore_id >= KFD_SIGNAL_EVENT_LIMIT)
+                       return -EINVAL;
+
                id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1,
                                GFP_KERNEL);
        } else {
@@ -204,7 +207,7 @@ static int create_signal_event(struct file *devkfd, struct kfd_process *p,
 
        ret = allocate_event_notification_slot(p, ev, restore_id);
        if (ret) {
-               pr_warn("Signal event wasn't created because out of kernel memory\n");
+               pr_warn("Failed to create signal event notification slot\n");
                return ret;
        }
 
index acd0e41e744c910975ab482ee5e112bccde75ab5..4d65f94da4d8181a26e24e917ba84be9bef14bb5 100644 (file)
@@ -440,7 +440,8 @@ enum kfd_queue_type  {
        KFD_QUEUE_TYPE_SDMA,
        KFD_QUEUE_TYPE_HIQ,
        KFD_QUEUE_TYPE_SDMA_XGMI,
-       KFD_QUEUE_TYPE_SDMA_BY_ENG_ID
+       KFD_QUEUE_TYPE_SDMA_BY_ENG_ID,
+       KFD_QUEUE_TYPE_MAX,
 };
 
 enum kfd_queue_format {
index ca71fa726e32b0f772f819e93141a8d9fda4da5c..5fb3679e4e85881b75ca1151e3646c12dbe39d66 100644 (file)
@@ -910,7 +910,7 @@ static void kfd_process_free_id(struct kfd_process *process)
 {
        struct kfd_process *primary_process;
 
-       if (process->context_id != KFD_CONTEXT_ID_PRIMARY)
+       if (process->context_id == KFD_CONTEXT_ID_PRIMARY)
                return;
 
        primary_process = kfd_lookup_process_by_mm(process->lead_thread->mm);
index 0ac35789b239943d1cc7136c43b813d657a92abc..9ccbc6e5b27b3b4394599affcd96e6b0275f55d7 100644 (file)
@@ -265,6 +265,11 @@ static int init_user_queue(struct process_queue_manager *pqm,
        (*q)->process = pqm->process;
 
        if (dev->kfd->shared_resources.enable_mes) {
+               if (!q_properties->wptr_bo) {
+                       pr_debug("Queue initialization with shared MES requires queue buffers to be initialized\n");
+                       return -EINVAL;
+               }
+
                retval = amdgpu_amdkfd_alloc_kernel_mem(dev->adev,
                                                AMDGPU_MES_GANG_CTX_SIZE,
                                                AMDGPU_GEM_DOMAIN_GTT,
@@ -1003,6 +1008,23 @@ int kfd_criu_restore_queue(struct kfd_process *p,
                goto exit;
        }
 
+       pdd = kfd_process_device_data_by_id(p, q_data->gpu_id);
+       if (!pdd) {
+               pr_err("Failed to get pdd\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (q_data->type >= KFD_QUEUE_TYPE_MAX) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (q_data->mqd_size != mqd_size_from_queue_type(pdd->dev->dqm, q_data->type)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
        *priv_data_offset += sizeof(*q_data);
        q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;
 
@@ -1025,13 +1047,6 @@ int kfd_criu_restore_queue(struct kfd_process *p,
 
        *priv_data_offset += q_extra_data_size;
 
-       pdd = kfd_process_device_data_by_id(p, q_data->gpu_id);
-       if (!pdd) {
-               pr_err("Failed to get pdd\n");
-               ret = -EINVAL;
-               goto exit;
-       }
-
        /*
         * data stored in this order:
         * mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]...
@@ -1042,18 +1057,10 @@ int kfd_criu_restore_queue(struct kfd_process *p,
        memset(&qp, 0, sizeof(qp));
        set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask));
 
-       ret = kfd_queue_acquire_buffers(pdd, &qp);
-       if (ret) {
-               pr_debug("failed to acquire user queue buffers for CRIU\n");
-               goto exit;
-       }
-
        print_queue_properties(&qp);
 
        ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL);
        if (ret) {
-               kfd_queue_unref_bo_vas(pdd, &qp);
-               kfd_queue_release_buffers(pdd, &qp);
                pr_err("Failed to create new queue err:%d\n", ret);
                goto exit;
        }
index 28354a4e5dd5d153d49bcb3a3c2b0125b0ddea28..98a5512b701b12f7f9706be221711206413e8eb4 100644 (file)
@@ -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);
index 18145d78334fdae198530ae0ef1f7bf913b4ee0f..9c564cd5edeea5660ebedcc2c3fe910f996afa1d 100644 (file)
@@ -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).
+        *
+        * 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).
         *
-        * 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.
+        * 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;
        }
index 3dcedaa67ed8efc49ec2ba5a9e8344ffbbdd0ce1..b43cd68cde671c4057b03bbed8866ee506b327c6 100644 (file)
@@ -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))
@@ -285,39 +292,46 @@ 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+ */
index c6f94eb71ffadcef2e8b8e5d65468b78c42b6fed..6be7f6edd0b29366b3ce906e525f058a68db8303 100644 (file)
@@ -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 */
index 175106cce5a4a18343e2e7a9c8a9d7a37fbc0794..e25b94b65daca58d274353620cbd25f7db301742 100644 (file)
@@ -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 &&
index 82d02ebbd82908ad5c214b6c7621f58e1e1cf221..1dc85f6b6689664b5e847aa2b2b62fe9935b4392 100644 (file)
@@ -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);
 
index 7c293917e6fd6f00336b8e403c3b5b10a91bde99..ecb8493ec52339b0575a7e1f374df82aba4944b3 100644 (file)
@@ -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;
index 7d8951fecd570eda92a89ca0c9b466330d0114b5..29dbb5e410d81c8ad446c71c0c131069c81593b4 100644 (file)
@@ -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;
index 47abb40667092cf1c609723e72c6f2de06402257..1cd17a0272bc8c1981f3249d66f8f0b869c07cbf 100644 (file)
@@ -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
index b92d4f378d602c86637eaa84f37cad9ed52885a0..97ea22af5d2b509148b7c122ec96512d341cee4a 100644 (file)
@@ -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;
index 7de12b16d7ade42181e8002bb0747e88fe16321f..a8241036def2b7303e27583ffcc0cd8f92cae15b 100644 (file)
@@ -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 */
index 527d17f29f3ba757604e9cf6af5c01d8c42857e9..94e166c0a9b0b81c0e2ea4f27c55d36c359c751c 100644 (file)
@@ -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"
 #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 */
index 2da3e3c8304a9076abc1422c47c82e0e805a597c..2824a0e1acc9f19c22eaa8b78404bd7d2ea1beb3 100644 (file)
  *  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)
  */
        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), \
                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;
 };
index 8079da7c53350e95cc288e61708d8ab0edc6cbca..012227c70600174612990cf03fa69598ea110e96 100644 (file)
@@ -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;
index 1d6e30269d5679bf7dd503520ac84e712463bba4..4d553be56396f399c92511a3961744fcd80e9efc 100644 (file)
@@ -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;
index 95bf187f02a5e5d63d253a56c50acf454478f704..bc82ba2e4c9bae9a0f299f4c08d36766c98657c8 100644 (file)
@@ -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 = {
index 208a2fba6d408e18760ebdb56ede6ce6f46c122a..3969a7670482005facb8a00294288b96a75db146 100644 (file)
@@ -802,6 +802,7 @@ static int smu_set_funcs(struct amdgpu_device *adev)
                break;
        case IP_VERSION(15, 0, 0):
        case IP_VERSION(15, 0, 5):
+       case IP_VERSION(15, 0, 9):
                smu_v15_0_0_set_ppt_funcs(smu);
                break;
        case IP_VERSION(15, 0, 8):
@@ -1366,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;
@@ -1427,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;
 }
 
@@ -1449,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;
 }
 
index d76e0b005308f03c1f4d71e63da8319d509d1b8f..e3a89e9a9df4168f1c6609e411ba9ecc260fd956 100644 (file)
@@ -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 {
index 75719c47a41e2096b0d3271ec4e1fd86583a6d2e..3d73f2050bbef09523984f4c23c20e5dad4bf91b 100644 (file)
@@ -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)
index fdc1456b885ce22abceca368b3798ef4bf9bde75..a6a88e7b266857e8b9d6c1f3eeac3504ec43b5ee 100644 (file)
@@ -1621,19 +1621,23 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
                table_context->power_play_table;
        PPTable_t *pptable = table_context->driver_pptable;
        CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
-       int16_t od_percent_upper = 0, od_percent_lower = 0;
+       uint32_t pp_limit = smu->adev->pm.ac_power ?
+               skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
+               skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
        uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
-       uint32_t power_limit;
+       uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit);
+       uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit);
+       int16_t od_percent_upper = 0, od_percent_lower = 0;
+       int ret;
 
-       if (smu_v14_0_get_current_power_limit(smu, &power_limit))
-               power_limit = smu->adev->pm.ac_power ?
-                             skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
-                             skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
+       if (current_power_limit) {
+               ret = smu_v14_0_get_current_power_limit(smu, current_power_limit);
+               if (ret)
+                       *current_power_limit = pp_limit;
+       }
 
-       if (current_power_limit)
-               *current_power_limit = power_limit;
        if (default_power_limit)
-               *default_power_limit = power_limit;
+               *default_power_limit = pp_limit;
 
        if (powerplay_table) {
                if (smu->od_enabled &&
@@ -1647,15 +1651,15 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
        }
 
        dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
-                                       od_percent_upper, od_percent_lower, power_limit);
+                                       od_percent_upper, od_percent_lower, pp_limit);
 
        if (max_power_limit) {
-               *max_power_limit = msg_limit * (100 + od_percent_upper);
+               *max_power_limit = max_limit * (100 + od_percent_upper);
                *max_power_limit /= 100;
        }
 
        if (min_power_limit) {
-               *min_power_limit = power_limit * (100 + od_percent_lower);
+               *min_power_limit = min_limit * (100 + od_percent_lower);
                *min_power_limit /= 100;
        }
 
index a1318409e4b5ef0d8ed807eb56e8080eae594b5a..8fc99e93ac53626feed9398dcbe3919897e4af66 100644 (file)
@@ -664,6 +664,7 @@ int smu_v15_0_gfx_off_control(struct smu_context *smu, bool enable)
 
        switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
        case IP_VERSION(15, 0, 0):
+       case IP_VERSION(15, 0, 9):
                if (!(adev->pm.pp_feature & PP_GFXOFF_MASK))
                        return 0;
                if (enable)
index a214ddbd4c86390591374e0e8026932b7c216294..bb8d09e73c7d06f8175a9db4747f33e2e7be8b6f 100644 (file)
@@ -1177,7 +1177,8 @@ static int smu_v15_0_common_get_dpm_profile_freq(struct smu_context *smu,
                        smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_SOCCLK, NULL, &clk_limit);
                break;
        case SMU_FCLK:
-               if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0))
+               if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0) ||
+                       amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 9))
                        smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_FCLK, NULL, &clk_limit);
                else
                        clk_limit = SMU_15_0_UMD_PSTATE_FCLK;
index 8cf6b73bceac075bf10a18d4f3e050ee0b755a77..5006ac181b2d7fc536dd2a189808a89314b9217e 100644 (file)
@@ -309,7 +309,9 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
        lane_count = dp->link_train.lane_count;
        for (lane = 0; lane < lane_count; lane++) {
                voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane);
+               voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT;
                pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
+               pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
                training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
                                DPCD_PRE_EMPHASIS_SET(pre_emphasis);
 
@@ -355,7 +357,9 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
        for (lane = 0; lane < lane_count; lane++) {
                training_lane = analogix_dp_get_lane_link_training(dp, lane);
                voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane);
+               voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT;
                pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
+               pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
 
                if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing &&
                    DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis)
index 7b11a582f8ec4fc00be2c3aeeaa4d9709e1df061..80ca785bdb262fb74e022957bac7b4eddfb25b65 100644 (file)
@@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
        console_unlock();
 }
 
+static int find_crtc_index_atomic(struct drm_fb_helper *helper)
+{
+       struct drm_device *dev = helper->dev;
+       int crtc_index = -EINVAL;
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_plane *plane;
+       int ret = 0;
+
+       drm_modeset_acquire_init(&ctx, 0);
+
+retry:
+       drm_for_each_plane(plane, dev) {
+               const struct drm_plane_state *plane_state;
+
+               if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               ret = drm_modeset_lock(&plane->mutex, &ctx);
+               if (ret)
+                       goto err_drm_modeset_lock;
+               plane_state = plane->state;
+
+               if (plane_state->fb == helper->fb && plane_state->crtc) {
+                       struct drm_crtc *crtc = plane_state->crtc;
+
+                       ret = drm_modeset_lock(&crtc->mutex, &ctx);
+                       if (ret)
+                               goto err_drm_modeset_lock;
+                       if (crtc->state->active)
+                               crtc_index = crtc->index;
+                       drm_modeset_unlock(&crtc->mutex);
+               }
+               drm_modeset_unlock(&plane->mutex);
+
+               if (crtc_index >= 0)
+                       break;
+       }
+
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+
+       return crtc_index;
+
+err_drm_modeset_lock:
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(&ctx);
+               goto retry;
+       }
+       return ret;
+}
+
+static int find_crtc_index_legacy(struct drm_fb_helper *helper)
+{
+       struct drm_device *dev = helper->dev;
+       struct drm_crtc *crtc;
+
+       drm_for_each_crtc(crtc, dev) {
+               struct drm_plane *plane = crtc->primary;
+
+               if (!crtc->enabled)
+                       continue;
+               if (!plane || plane->fb != helper->fb)
+                       continue; /* CRTC doesn't display fbdev emulation */
+
+               return crtc->index;
+       }
+
+       return -EINVAL;
+}
+
+static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper)
+{
+       struct drm_device *dev = helper->dev;
+       int crtc_index;
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       if (drm_drv_uses_atomic_modeset(dev))
+               crtc_index = find_crtc_index_atomic(helper);
+       else
+               crtc_index = find_crtc_index_legacy(helper);
+
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return crtc_index;
+}
+
 static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
 {
        struct drm_device *dev = helper->dev;
        struct drm_clip_rect *clip = &helper->damage_clip;
        struct drm_clip_rect clip_copy;
+       int crtc_index;
        unsigned long flags;
        int ret;
 
        mutex_lock(&helper->lock);
-       drm_client_modeset_wait_for_vblank(&helper->client, 0);
+       crtc_index = drm_fb_helper_find_crtc_index(helper);
+       if (crtc_index >= 0)
+               drm_client_modeset_wait_for_vblank(&helper->client, crtc_index);
        mutex_unlock(&helper->lock);
 
        if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
index 958cb605aedd530733d426551351f0737c54f43c..9a06ff7d2608e6f40def924ce8255def498a3162 100644 (file)
@@ -1146,20 +1146,35 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm,
                };
                bool use_iova = dma_use_iova(&svm_pages->state);
 
-               if (use_iova)
-                       dma_iova_destroy(dev, &svm_pages->state,
-                                        svm_pages->state_offset,
-                                        svm_pages->dma_addr[0].dir, 0);
+               /*
+                * IOVA is reserved for the whole range but only the linked
+                * system pages (state_offset bytes) need unlinking; free the
+                * entire reservation to avoid leaking the device-page part.
+                * On the error path state_offset is 0, so just free it.
+                */
+               if (use_iova) {
+                       if (svm_pages->state_offset)
+                               dma_iova_unlink(dev, &svm_pages->state, 0,
+                                               svm_pages->state_offset,
+                                               svm_pages->dma_addr[0].dir, 0);
+                       dma_iova_free(dev, &svm_pages->state);
+               }
 
                for (i = 0, j = 0; i < npages; j++) {
                        struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j];
 
-                       if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM)
-                               dma_unmap_page(dev,
-                                              addr->addr,
-                                              PAGE_SIZE << addr->order,
-                                              addr->dir);
-                       else if (dpagemap && dpagemap->ops->device_unmap)
+                       if (addr->proto == DRM_INTERCONNECT_SYSTEM) {
+                               /*
+                                * Linked IOVA pages were already torn down by
+                                * the dma_iova_unlink()/dma_iova_free() above;
+                                * only the non-IOVA mappings need unmap here.
+                                */
+                               if (!use_iova)
+                                       dma_unmap_page(dev,
+                                                      addr->addr,
+                                                      PAGE_SIZE << addr->order,
+                                                      addr->dir);
+                       } else if (dpagemap && dpagemap->ops->device_unmap)
                                dpagemap->ops->device_unmap(dpagemap,
                                                            dev, addr);
                        i += 1 << addr->order;
@@ -1486,7 +1501,7 @@ map_pages:
                /* Unlock and restart mapping to allocate memory. */
                drm_gpusvm_notifier_unlock(gpusvm);
                svm_pages->dma_addr =
-                       kvmalloc_objs(*svm_pages->dma_addr, npages);
+                       kvzalloc_objs(*svm_pages->dma_addr, npages);
                if (!svm_pages->dma_addr) {
                        err = -ENOMEM;
                        goto err_free;
@@ -1529,6 +1544,16 @@ map_pages:
                                        err = -EAGAIN;
                                        goto err_unmap;
                                }
+
+                               /*
+                                * Set the dpagemap as soon as the first
+                                * device page is mapped so the err_unmap path
+                                * can device_unmap() the device mappings that
+                                * have already been created.
+                                */
+                               drm_pagemap_get(dpagemap);
+                               drm_pagemap_put(svm_pages->dpagemap);
+                               svm_pages->dpagemap = dpagemap;
                        }
                        svm_pages->dma_addr[j] =
                                dpagemap->ops->device_map(dpagemap,
@@ -1596,12 +1621,8 @@ map_pages:
                        goto err_unmap;
        }
 
-       if (pagemap) {
+       if (pagemap)
                flags.has_devmem_pages = true;
-               drm_pagemap_get(dpagemap);
-               drm_pagemap_put(svm_pages->dpagemap);
-               svm_pages->dpagemap = dpagemap;
-       }
 
        /* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */
        WRITE_ONCE(svm_pages->flags.__flags, flags.__flags);
index e2df4becce629a0d7766bed94da0a5183f728e74..9039a39c43243ab59c9abcc43aeafd3a60b93178 100644 (file)
@@ -373,13 +373,25 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
                        return -EINVAL;
                file_priv->supports_virtualized_cursor_plane = req->value;
                break;
-       case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE:
+       case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: {
+               struct drm_plane *plane;
+               bool has_plane_with_color_pipeline = false;
+
                if (!file_priv->atomic)
                        return -EINVAL;
                if (req->value > 1)
                        return -EINVAL;
+               drm_for_each_plane(plane, dev) {
+                       if (plane->color_pipeline_property) {
+                               has_plane_with_color_pipeline = true;
+                               break;
+                       }
+               }
+               if (!has_plane_with_color_pipeline)
+                       return -EOPNOTSUPP;
                file_priv->plane_color_pipeline = req->value;
                break;
+       }
        default:
                return -EINVAL;
        }
index f85cb293a3dbc877ac08e7f80e3bfd841c8dfa7e..e417c7533053d2fedc48a1a7857697171ed5ec29 100644 (file)
@@ -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,
index 85d3aa3b9894c096d2a8aa6c1e8ccd8ec52e521b..7ff5712f8b1990bc7ba3b5490abf4d58ff0eea3a 100644 (file)
@@ -5737,8 +5737,9 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
        struct intel_display *display = to_intel_display(intel_dp);
        bool force_retrain = intel_dp->link.force_retrain;
        bool reprobe_needed = false;
+       int tries = 33;
 
-       for (;;) {
+       while (--tries) {
                u8 esi[4] = {};
                u8 ack[4] = {};
                bool new_irqs;
@@ -5781,6 +5782,11 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
                        break;
        }
 
+       if (!tries) {
+               drm_dbg_kms(display->drm, "DPRX ESI not clearing, device may be stuck\n");
+               reprobe_needed = true;
+       }
+
        return !reprobe_needed;
 }
 
index 615ee980470ee217a423d8d4fd391cf43d9da3eb..34dbe450cc5b50cebb9324237b46d78e7cef51c3 100644 (file)
@@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder,
        else
                val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
 
-        /* DP2.0 10G and 20G rates enable MPLLA*/
-       if (port_clock == 1000000 || port_clock == 2000000)
-               val |= XELPDP_SSC_ENABLE_PLLA;
-       else
-               val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0;
+       val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0;
 
        intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
                     XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE |
index e138982dc91f6dd99f0865a24c1719b45e684e33..beaa1d62613db55ac232f475bd5eb49456df8b84 100644 (file)
@@ -1522,9 +1522,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state
            needs_panel_replay)
                return 0;
 
-       if (intel_vrr_always_use_vrr_tg(display))
-               return 0;
-
        return 1;
 }
 
index ad4bfff6903dabe726da105f04956495a7ab3f0e..164b7d61c9a31306b28e29979e3e4f30d451ac1b 100644 (file)
@@ -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 */
index 5a3677ea25b071f16f32479043f3b9e857577eb3..a4ce21d4c0245aeed558d0e4990e6f6ab1c92c6d 100644 (file)
@@ -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;
index aeafe1742d3080a2a3c1655da5b8bd2045436ca4..c58ffa5a8fa6f51178add1a668578b2a46f42eed 100644 (file)
@@ -769,8 +769,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
                struct intel_engine_cs *engine;
 
                if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) {
-                       kfree(set.engines);
-                       return -EFAULT;
+                       err = -EFAULT;
+                       goto err;
                }
 
                memset(&set.engines[n], 0, sizeof(set.engines[n]));
@@ -786,8 +786,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
                        drm_dbg(&i915->drm,
                                "Invalid engine[%d]: { class:%d, instance:%d }\n",
                                n, ci.engine_class, ci.engine_instance);
-                       kfree(set.engines);
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto err;
                }
 
                set.engines[n].type = I915_GEM_ENGINE_TYPE_PHYSICAL;
@@ -800,15 +800,21 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
                                           set_proto_ctx_engines_extensions,
                                           ARRAY_SIZE(set_proto_ctx_engines_extensions),
                                           &set);
-       if (err) {
-               kfree(set.engines);
-               return err;
-       }
+       if (err)
+               goto err_extensions;
 
        pc->num_user_engines = set.num_engines;
        pc->user_engines = set.engines;
 
        return 0;
+
+err_extensions:
+       for (n = 0; n < set.num_engines; n++)
+               kfree(set.engines[n].siblings);
+err:
+       kfree(set.engines);
+
+       return err;
 }
 
 static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv,
@@ -850,7 +856,7 @@ static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv,
                pe = &pc->user_engines[idx];
 
                /* Only render engine supports RPCS configuration. */
-               if (pe->engine->class != RENDER_CLASS)
+               if (!pe->engine || pe->engine->class != RENDER_CLASS)
                        return -EINVAL;
 
                sseu = &pe->sseu;
index be4bbff1a57c9cc3f847958c6990e34fa1857a29..d5190e11b270627978dce3ea8fddae2fa7f6e110 100644 (file)
@@ -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;
index 1359fc9cb88ef2e4cb9c698952dfdebc76f67c9d..e693b0c9d2a3e2ee0530b0cafbe39621038c0c20 100644 (file)
@@ -3932,11 +3932,11 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
        struct drm_i915_private *i915 = siblings[0]->i915;
        struct virtual_engine *ve;
        unsigned int n;
-       int err;
+       int err = -ENOMEM;
 
        ve = kzalloc_flex(*ve, siblings, count);
        if (!ve)
-               return ERR_PTR(-ENOMEM);
+               goto err;
 
        ve->base.i915 = i915;
        ve->base.gt = siblings[0]->gt;
@@ -3968,10 +3968,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
        intel_engine_init_execlists(&ve->base);
 
        ve->base.sched_engine = i915_sched_engine_create(ENGINE_VIRTUAL);
-       if (!ve->base.sched_engine) {
-               err = -ENOMEM;
-               goto err_put;
-       }
+       if (!ve->base.sched_engine)
+               goto err_noput;
        ve->base.sched_engine->private_data = &ve->base;
 
        ve->base.cops = &virtual_context_ops;
@@ -3987,10 +3985,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
        intel_context_init(&ve->context, &ve->base);
 
        ve->base.breadcrumbs = intel_breadcrumbs_create(NULL);
-       if (!ve->base.breadcrumbs) {
-               err = -ENOMEM;
+       if (!ve->base.breadcrumbs)
                goto err_put;
-       }
 
        for (n = 0; n < count; n++) {
                struct intel_engine_cs *sibling = siblings[n];
@@ -4065,8 +4061,13 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
        virtual_engine_initial_hint(ve);
        return &ve->context;
 
+err_noput:
+       kfree(ve);
+       goto err;
+
 err_put:
        intel_context_put(&ve->context);
+err:
        return ERR_PTR(err);
 }
 
index 33351deeea4f0b7797b554eb3144319001063840..07eaf71955c4478f83fc635cec21ad4ccc55a4ad 100644 (file)
@@ -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;
index 52e16c1e7af0ddf717bd70e0be2d33aa1afa9f87..406e0758e860f23e29d5be24905bc8f385985f68 100644 (file)
@@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
                goto err_free_ctx;
 
        ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
-       if (IS_ERR(ctx->vm_ctx)) {
-               err = PTR_ERR(ctx->vm_ctx);
+       if (!ctx->vm_ctx) {
+               err = -EINVAL;
                goto err_free_ctx;
        }
 
index 6bb5baa6c41b73c211839e72e5553d26c7525733..805d9f9bc1dd9e7d710558c146edfd169e767604 100644 (file)
@@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp)
        return 0;
 }
 
-const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
+static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
        .set = pvr_fw_trace_init_mask_set,
        .get = param_get_hexint,
 };
index 107bdb642f22924e19b7ddfe348ef1fcedc6cd38..190c082b12c8cfae89c2d3136816accc0c92f298 100644 (file)
@@ -231,29 +231,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt,
                 * covered by a number of LPTEs, the LPTEs once again take
                 * control over their address range.
                 *
-                * Determine how many LPTEs need to transition state.
+                * Transition each LPTE individually as each may have a
+                * different target state (sparse, invalid, or valid).
                 */
-               pgt->pte[ptei].s.spte_valid = false;
-               for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) {
+               for (ptei++; ptei < lpti; ptei++) {
                        if (pgt->pte[ptei].s.sptes)
                                break;
-                       pgt->pte[ptei].s.spte_valid = false;
                }
 
-               if (pgt->pte[pteb].s.sparse) {
-                       TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes);
-                       pair->func->sparse(vmm, pgt->pt[0], pteb, ptes);
-               } else if (!pgt->pte[pteb].s.lpte_valid) {
-                       if (pair->func->invalid) {
-                               /* If the MMU supports it, restore the LPTE to the
-                                * INVALID state to tell the MMU there is no point
-                                * trying to fetch the corresponding SPTEs.
-                                */
-                               TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes);
-                               pair->func->invalid(vmm, pgt->pt[0], pteb, ptes);
+               while (pteb < ptei) {
+                       pgt->pte[pteb].s.spte_valid = false;
+                       if (pgt->pte[pteb].s.sparse) {
+                               TRA(it, "LPTE %05x: U -> S", pteb);
+                               pair->func->sparse(vmm, pgt->pt[0], pteb, 1);
+                       } else if (!pgt->pte[pteb].s.lpte_valid) {
+                               if (pair->func->invalid) {
+                                       TRA(it, "LPTE %05x: U -> I", pteb);
+                                       pair->func->invalid(vmm, pgt->pt[0], pteb, 1);
+                               }
                        }
-               } else {
-                       TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes);
+                       pteb++;
                }
        }
 }
index 0b25abebb803c6a016ce85a80469f9e8124a324d..9687c59de35055eb93ed1014a8061526a94aa63a 100644 (file)
@@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev)
                return ret;
 
 #ifdef CONFIG_DEBUG_FS
-       drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+       ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+       if (ret)
+               return ret;
+
        INIT_LIST_HEAD(&ptdev->gems.node);
 #endif
 
index de8e6689a869a066423c47296596746fd94567b4..90f59d782a809ff09e83b6f17be412cc66116862 100644 (file)
@@ -829,6 +829,7 @@ static int panthor_fw_load(struct panthor_device *ptdev)
        }
 
        if (hdr.size > iter.size) {
+               ret = -EINVAL;
                drm_err(&ptdev->base, "Firmware image is truncated\n");
                goto out;
        }
index 4032c6ad45bc190b2bfd550aa1fd3b2da83f8af4..e384cbd0cbf72fb92dfb73a5230321752a7bd83a 100644 (file)
@@ -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) {
index 2fc47f3b463b7e99b2ac5773683d5ffc333f03b6..7a374e462348bc032c5e75a7a1dabb4dbc1a6a89 100644 (file)
@@ -180,19 +180,27 @@ static void test_multiple_loops(struct kunit *test)
 {
        struct drm_exec exec;
 
-       drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
-       drm_exec_until_all_locked(&exec)
        {
-               break;
+               __label__ drm_exec_retry;
+
+               drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
+               drm_exec_until_all_locked(&exec)
+               {
+                       break;
+               }
+               drm_exec_fini(&exec);
        }
-       drm_exec_fini(&exec);
 
-       drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
-       drm_exec_until_all_locked(&exec)
        {
-               break;
+               __label__ drm_exec_retry;
+
+               drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
+               drm_exec_until_all_locked(&exec)
+               {
+                       break;
+               }
+               drm_exec_fini(&exec);
        }
-       drm_exec_fini(&exec);
        KUNIT_SUCCEED(test);
 }
 
index 278bbe7a11add370dcc328d7bf1622df712ba7df..46983e7de7a33604474766dd7721eea0c8cad54b 100644 (file)
@@ -1051,9 +1051,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
                return -EBUSY;
 
 #ifdef CONFIG_X86
-       /* Anything returned to the system needs to be cached. */
-       if (tt->caching != ttm_cached)
-               set_pages_array_wb(tt->pages, tt->num_pages);
+       /* Anything returned to the system needs to be cached. Walk allocations
+        * skipping NULL pages and issue set_pages_array_wb() per contiguous run.
+        */
+       if (tt->caching != ttm_cached) {
+               pgoff_t run_start = 0, run_count = 0;
+
+               for (i = 0; i < tt->num_pages; i += num_pages) {
+                       page = tt->pages[i];
+                       if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
+                               if (run_count) {
+                                       set_pages_array_wb(&tt->pages[run_start],
+                                                          run_count);
+                                       run_count = 0;
+                               }
+                               num_pages = 1;
+                               continue;
+                       }
+                       num_pages = 1UL << ttm_pool_page_order(pool, page);
+                       if (!run_count)
+                               run_start = i;
+                       run_count += num_pages;
+               }
+               if (run_count)
+                       set_pages_array_wb(&tt->pages[run_start], run_count);
+       }
 #endif
 
        if (tt->dma_address || flags->purge) {
@@ -1061,7 +1083,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
                        unsigned int order;
 
                        page = tt->pages[i];
-                       if (unlikely(!page)) {
+                       if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
                                num_pages = 1;
                                continue;
                        }
@@ -1104,6 +1126,10 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
                if (unlikely(!page))
                        continue;
 
+               /* Already-handled entry from a previous attempt. */
+               if (unlikely(ttm_backup_page_ptr_is_handle(page)))
+                       continue;
+
                ttm_pool_split_for_swap(pool, page);
 
                shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
index 1db43c6a078d54a9306b456f24ffa2c9cbf4e8f1..7682b24f13ec51db7364242ebef22c6d88125f78 100644 (file)
@@ -495,6 +495,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
               sizeof(indirect_csd.wg_uniform_offsets));
 
        info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect);
+       if (!info->indirect)
+               return -ENOENT;
 
        return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit,
                                          &info->job, &info->clean_job,
index 435d37d36034023a2087b0a67c1708afc0431eef..66c3f6f74e9c6972c38f67fca078a4c80e47b33d 100644 (file)
@@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
        if (!vgdev->has_virgl_3d)
                return;
 
-       objs = virtio_gpu_array_alloc(1);
-       if (!objs)
-               return;
-       virtio_gpu_array_add_obj(objs, obj);
+       if (vfpriv->context_created) {
+               objs = virtio_gpu_array_alloc(1);
+               if (!objs)
+                       return;
+               virtio_gpu_array_add_obj(objs, obj);
 
-       virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
-                                              objs);
+               virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
+                                                      objs);
+       }
        virtio_gpu_notify(vgdev);
 }
 
index cfde9f573df6ecbcbb9d0e8cad777a987ebf477e..b4329f28e97665470ce95e3622afbb9a926e58d9 100644 (file)
@@ -49,7 +49,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
                                virtio_gpu_cmd_get_edids(vgdev);
                        virtio_gpu_cmd_get_display_info(vgdev);
                        virtio_gpu_notify(vgdev);
-                       drm_helper_hpd_irq_event(vgdev->ddev);
+                       wait_event_timeout(vgdev->resp_wq,
+                                          !vgdev->display_info_pending,
+                                          5 * HZ);
+                       drm_kms_helper_hotplug_event(vgdev->ddev);
                }
                events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
        }
index c8b9475a7472e09abc61085231a2629f2fad593d..e5e1af8b8e8a0a327a253b462fcf942f87c0c0c4 100644 (file)
@@ -840,9 +840,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
        vgdev->display_info_pending = false;
        spin_unlock(&vgdev->display_info_lock);
        wake_up(&vgdev->resp_wq);
-
-       if (!drm_helper_hpd_irq_event(vgdev->ddev))
-               drm_kms_helper_hotplug_event(vgdev->ddev);
 }
 
 static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
index 9240aff779da3d16313cdd9640e1c3e7b272c74c..c2c686aed1cb8a6e857f94792073dc2226566f5f 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <kunit/test-bug.h>
 #include <kunit/test.h>
-#include <kunit/test-bug.h>
 #include <kunit/visibility.h>
 
 #define PLATFORM_CASE(platform__, graphics_step__)                                     \
index 4c80bac676229e1f8386ecf1c3cced5b8d9e2cf1..7ed76349075fd15bb51e1d487cc33448b86386fe 100644 (file)
@@ -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;
 
index 6340317f7d2e6a73ce7f480b444129f13ecb7093..7ae1d9ac05743edad30940a032256894d153f550 100644 (file)
@@ -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,
index fcc63ae3f455161aee04480f67c9c2cf3409a5f4..e45f24301050ca2e4907aa401c2cb4e627eff41b 100644 (file)
@@ -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 */
index abe25aedeeadef60820247ec5e14678c321f0852..dcb48caa485dc981840baf59c313f5d9c98e3b29 100644 (file)
@@ -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) {
                /*
index 32dd2ffbc796397028a1fb0025f0cc0db2d399d3..aad10899d9ace1be81b84be4c4545945bb6b0916 100644 (file)
@@ -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 */
index 8a920e58245cd7e091b15f2743033ac98d017410..bf0728838ead51e8b63cb1821b7ef6fdc9c6a377 100644 (file)
@@ -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);
index e05dabfcd43ca8ac9cc633db4933f095470f5989..d5293bc33a670d06f6fb230014744ee27aab8f42 100644 (file)
@@ -292,13 +292,23 @@ retry:
                goto err_exec;
        }
 
-       /* Wait behind rebinds */
+       /*
+        * Wait behind rebinds and any kernel operations (evictions, defrag
+        * moves, ...) on the VM and all external BOs. The VM's private BOs
+        * carry their kernel ops in the VM dma-resv KERNEL slot, while each
+        * external BO carries them in its own dma-resv KERNEL slot; both are
+        * covered by iterating every object locked by the exec, mirroring the
+        * drm_gpuvm_resv_add_fence() below.
+        */
        if (!xe_vm_in_lr_mode(vm)) {
-               err = xe_sched_job_add_deps(job,
-                                           xe_vm_resv(vm),
-                                           DMA_RESV_USAGE_KERNEL);
-               if (err)
-                       goto err_put_job;
+               struct drm_gem_object *obj;
+
+               drm_exec_for_each_locked_object(exec, obj) {
+                       err = xe_sched_job_add_deps(job, obj->resv,
+                                                   DMA_RESV_USAGE_KERNEL);
+                       if (err)
+                               goto err_put_job;
+               }
        }
 
        for (i = 0; i < num_syncs && !err; i++)
index e5e53b421f29fb32ef7f8ef6fd8b04ed63dce34f..cda14d954e572fde3b644a6a4d2d333a6b2884b5 100644 (file)
@@ -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;
        /**
index f5c3d8a97ec64002dab3a9fec80d034e6d0f3e2b..1c92b96e4c3246d86dea83b408c7e6fe24adf3e0 100644 (file)
@@ -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;
 }
index c7b9642b41ba74ebb67965b0baefdc55bd571b2b..31a2acb63ac34d93b38e8cd9e7d5c6e5346a64da 100644 (file)
@@ -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 */
index 9428dd5e7760c50bc35d22d3b36f07abf0358a54..7d28290e7d1ca38f092b8a2a29ab4852b0fe0e76 100644 (file)
@@ -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;
index 965c45889c726f19604f0d7c4b21d6986f96b05a..78e5b63f3ebeafc95d59862972e6b5a566f0d34a 100644 (file)
@@ -138,6 +138,7 @@ 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,
index 4cb5781829122950af0e2bb69a1a70c02277bd74..99347f216ec8932216695324f808d0f2ac41d9b9 100644 (file)
@@ -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);
@@ -112,6 +157,10 @@ static const struct init_funcs init_funcs[] = {
                .init = xe_sched_job_module_init,
                .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,
index 79cb9639c0f3df6b6ebab70eb2e11d7d23694028..e8e54f701cf3ee8bbd5df9e71b3e9b18cf21c012 100644 (file)
@@ -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
 
index 33487e91f366cafc0c7e5bafa14aa10318f0905c..1ea67eaeae2437e13b67f47ca1b897cfbfd3b44d 100644 (file)
@@ -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;
index 3165686e3e04bc455c0a98f3c95c265de83d8c69..9cd873708136c5ac6cedc1360e89af67c446a1b2 100644 (file)
@@ -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);
 }
 
index 670bc2206feab3840b9585575ec1084b3bcd564a..39c9c8f0ea2d20b19d2cb5925d1b167dc0411434 100644 (file)
@@ -1026,12 +1026,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t
        u64 *ptr = data;
        u32 i;
 
+       /*
+        * @qword_ofs is the absolute entry offset within the page table, while
+        * @ptes is indexed relative to @update->ofs (its first entry). The GPU
+        * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized
+        * chunks, calling this with an advancing @qword_ofs but a fresh @data
+        * pointer per chunk, so translate back into a @ptes index rather than
+        * assuming the chunk starts at ptes[0].
+        */
        for (i = 0; i < num_qwords; i++) {
+               u32 idx = qword_ofs - update->ofs + i;
+
                if (map)
                        xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
-                                 sizeof(u64), u64, ptes[i].pte);
+                                 sizeof(u64), u64, ptes[idx].pte);
                else
-                       ptr[i] = ptes[i].pte;
+                       ptr[i] = ptes[idx].pte;
        }
 }
 
@@ -1408,6 +1418,7 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update)
                                     pt_update_ops, rftree);
 }
 
+#if IS_ENABLED(CONFIG_DRM_GPUSVM)
 /*
  * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit()
  * and the matching late release in xe_pt_update_ops_run(). Read mode by
@@ -1434,6 +1445,10 @@ static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm)
        xe_svm_notifier_unlock(vm);
 #endif
 }
+#else
+static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { }
+static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { }
+#endif
 
 #if IS_ENABLED(CONFIG_DRM_GPUSVM)
 #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT
@@ -2350,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);
 }
 
index 09b99fb2608bce103f8c97bde4a250ec482035b9..6787564629c65f8315e62d28eb153617f787b3a6 100644 (file)
@@ -404,6 +404,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 +413,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 +432,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);
        }
        return err;
 }
index 00e58b36c510aca68dd330bdf71868afc76d891a..e1034d852104069edde8a216da6be335aac3e5d0 100644 (file)
 #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);
index 080c2fff0e95c3c3f125a91f1677257216159725..32ded13491ca26577a13272323bf45b10315abce 100644 (file)
@@ -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:
index c4fb290041956de2976a2ff52e0320cd7872d141..246fe18431428bfa3307bd7be914aa5dc441695a 100644 (file)
@@ -643,7 +643,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
                                 xe_device_is_l2_flush_optimized(xe) &&
                                 (pat_index != 19 && coh_mode != XE_COH_2WAY))) {
                        err = -EINVAL;
-                       goto madv_fini;
+                       goto free_vmas;
                }
        }
 
index 900daf1d1b1bb5ebcdb68ba395ffa0f72280e879..fe65ed246775d99973db5ac3552c40003cea9a55 100644 (file)
@@ -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)
index e3884096c2fe11157d8664eadfc8bff1a1e57fa9..ea3b584990c9a6fad9eed025fae8dc486d802e76 100644 (file)
@@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
                if (WARN_ON(!cache))
                        continue;
 
-               mutex_lock(&mapping->cache->lock);
+               mutex_lock(&cache->lock);
                WARN_ON(kref_read(&mapping->ref) != 1);
                __host1x_bo_unpin(&mapping->ref);
-               mutex_unlock(&mapping->cache->lock);
+               mutex_unlock(&cache->lock);
        }
 }
 EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
index d0130658091b02afe02d540b9e420d8eb8b0d104..536f6d01fd14c651c7858342aaafab7f5f83a7b2 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kfifo.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
+#include <linux/overflow.h>
 #include "hid_bpf_dispatch.h"
 
 const struct hid_ops *hid_ops;
@@ -296,10 +297,12 @@ __bpf_kfunc __u8 *
 hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size)
 {
        struct hid_bpf_ctx_kern *ctx_kern;
+       size_t end;
 
        ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
 
-       if (rdwr_buf_size + offset > ctx->allocated_size)
+       if (check_add_overflow(rdwr_buf_size, offset, &end) ||
+           end > ctx->allocated_size)
                return NULL;
 
        return ctx_kern->data + offset;
index 5e8ced7bc05a3acca29c6102105e7fbf78d56975..adaa44a858edd9e53ab1dd9b1ed56cdb9b144868 100644 (file)
@@ -109,9 +109,10 @@ struct appleir {
        struct hid_device *hid;
        unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
        struct timer_list key_up_timer; /* timer for key up */
-       spinlock_t lock;                /* protects .current_key */
+       spinlock_t lock;                /* protects .current_key, .removing */
        int current_key;                /* the currently pressed key */
        int prev_key_idx;               /* key index in a 2 packets message */
+       bool removing;                  /* set during teardown; gates input_dev access */
 };
 
 static int get_key(int data)
@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t)
        unsigned long flags;
 
        spin_lock_irqsave(&appleir->lock, flags);
-       if (appleir->current_key) {
+       if (!appleir->removing && appleir->current_key) {
                key_up(hid, appleir, appleir->current_key);
                appleir->current_key = 0;
        }
@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report,
                int index;
 
                spin_lock_irqsave(&appleir->lock, flags);
+               if (appleir->removing) {
+                       spin_unlock_irqrestore(&appleir->lock, flags);
+                       goto out;
+               }
                /*
                 * If we already have a key down, take it up before marking
                 * this one down
@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report,
        appleir->prev_key_idx = 0;
 
        if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
-               key_down(hid, appleir, appleir->current_key);
-               /*
-                * Remote doesn't do key up, either pull them up, in the test
-                * above, or here set a timer which pulls them up after 1/8 s
-                */
-               mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+               spin_lock_irqsave(&appleir->lock, flags);
+               if (!appleir->removing) {
+                       key_down(hid, appleir, appleir->current_key);
+                       /*
+                        * Remote doesn't do key up, either pull them up, in
+                        * the test above, or here set a timer which pulls them
+                        * up after 1/8 s
+                        */
+                       mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+               }
+               spin_unlock_irqrestore(&appleir->lock, flags);
                goto out;
        }
 
        if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
-               battery_flat(appleir);
+               spin_lock_irqsave(&appleir->lock, flags);
+               if (!appleir->removing)
+                       battery_flat(appleir);
+               spin_unlock_irqrestore(&appleir->lock, flags);
                /* Fall through */
        }
 
@@ -318,8 +331,20 @@ fail:
 static void appleir_remove(struct hid_device *hid)
 {
        struct appleir *appleir = hid_get_drvdata(hid);
+       unsigned long flags;
+
+       /*
+        * Mark the driver as tearing down so that any concurrent raw_event
+        * (e.g. from a USB URB completion that hid_hw_stop() has not yet
+        * killed) and the key_up_timer softirq stop touching input_dev
+        * before hid_hw_stop() frees it via hidinput_disconnect().
+        */
+       spin_lock_irqsave(&appleir->lock, flags);
+       appleir->removing = true;
+       spin_unlock_irqrestore(&appleir->lock, flags);
+
+       timer_shutdown_sync(&appleir->key_up_timer);
        hid_hw_stop(hid);
-       timer_delete_sync(&appleir->key_up_timer);
 }
 
 static const struct hid_device_id appleir_devices[] = {
index 41a79e43c82b32c8a04187f08d76497a71d2da50..cf123347a2af7bfe466bd0b502bf3c753d237bb3 100644 (file)
@@ -2045,6 +2045,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
        u8 *cdata = data;
        int ret = 0;
 
+       if (report_enum->numbered && (size < 1 || bufsize < 1)) {
+               hid_warn_ratelimited(hid,
+                                    "Event data for numbered report is too short (%d vs %zu)\n",
+                                    size, bufsize);
+               return -EINVAL;
+       }
+
        report = hid_get_report(report_enum, data);
        if (!report)
                return 0;
index 11e21f98872327ecb1d4870c5a60c521d95d92e8..b52e93a91ae54225dbcf52e5f39007feea4f8955 100644 (file)
@@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id *
 
        ret = letsketch_setup_input_tablet(data);
        if (ret)
-               return ret;
+               goto err_shutdown_timer;
 
        ret = letsketch_setup_input_tablet_pad(data);
        if (ret)
-               return ret;
+               goto err_shutdown_timer;
+
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (ret)
+               goto err_shutdown_timer;
 
-       return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       return 0;
+
+err_shutdown_timer:
+       /*
+        * Drain any pending callback and permanently disable the timer
+        * before devm releases data: if hid_hw_start() enabled I/O on an
+        * always-poll-quirk device and then failed, raw_event may have
+        * armed the timer already.
+        */
+       timer_shutdown_sync(&data->inrange_timer);
+       return ret;
+}
+
+static void letsketch_remove(struct hid_device *hdev)
+{
+       struct letsketch_data *data = hid_get_drvdata(hdev);
+
+       /*
+        * hid_hw_stop() synchronously kills the URBs that deliver
+        * raw_event(), so once it returns no path can re-arm
+        * inrange_timer.  timer_shutdown_sync() then drains any
+        * in-flight callback and permanently disables further
+        * mod_timer() calls before devm releases data.
+        */
+       hid_hw_stop(hdev);
+       timer_shutdown_sync(&data->inrange_timer);
 }
 
 static const struct hid_device_id letsketch_devices[] = {
@@ -315,6 +344,7 @@ static struct hid_driver letsketch_driver = {
        .name = "letsketch",
        .id_table = letsketch_devices,
        .probe = letsketch_probe,
+       .remove = letsketch_remove,
        .raw_event = letsketch_raw_event,
 };
 module_hid_driver(letsketch_driver);
index 1a88bc44ada4442495d82a333d16418435b69a57..02ef3e2094b4c50176dee45fe1475b240c715ea4 100644 (file)
@@ -1374,11 +1374,27 @@ static const struct hid_device_id lg_g15_devices[] = {
 };
 MODULE_DEVICE_TABLE(hid, lg_g15_devices);
 
+static void lg_g15_remove(struct hid_device *hdev)
+{
+       struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+
+       /*
+        * g15->work is only initialized for the models that schedule it
+        * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only
+        * cancel it when it was set up.
+        */
+       if (g15 && g15->work.func)
+               cancel_work_sync(&g15->work);
+
+       hid_hw_stop(hdev);
+}
+
 static struct hid_driver lg_g15_driver = {
        .name                   = "lg-g15",
        .id_table               = lg_g15_devices,
        .raw_event              = lg_g15_raw_event,
        .probe                  = lg_g15_probe,
+       .remove                 = lg_g15_remove,
 };
 module_hid_driver(lg_g15_driver);
 
index 381e4dc5aba7aa6bc5182efe9c67fe69688b8421..9c574ab8b60b3789b17cbe7e4f377f49f9f93181 100644 (file)
@@ -1907,8 +1907,13 @@ static int logi_dj_probe(struct hid_device *hdev,
        output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
        rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
 
-       if (rep && (rep->maxfield < 1 ||
-                   rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) {
+       if (rep && rep->maxfield < 1) {
+               hid_err(hdev, "Expected size of DJ short report is %d, but got 0",
+                       DJREPORT_SHORT_LENGTH - 1);
+               return -EINVAL;
+       }
+
+       if (rep && rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) {
                hid_err(hdev, "Expected size of DJ short report is %d, but got %d",
                        DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count);
                return -EINVAL;
index 0495152091e3b537e2bf3ffa15573fc72f67a8d9..edb37b4c867e4bd5f04a92e49241faa82e6d7413 100644 (file)
@@ -31,6 +31,7 @@
  * [1] https://gitlab.freedesktop.org/libevdev/hid-tools
  */
 
+#include <linux/bitmap.h>
 #include <linux/bits.h>
 #include <linux/device.h>
 #include <linux/hid.h>
@@ -97,8 +98,7 @@ enum report_mode {
        TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS,
 };
 
-#define MT_IO_SLOTS_MASK               GENMASK(7, 0) /* reserve first 8 bits for slot tracking */
-#define MT_IO_FLAGS_RUNNING            32
+#define MT_IO_FLAGS_RUNNING            0
 
 static const bool mtrue = true;                /* default for true */
 static const bool mfalse;              /* default for false */
@@ -174,10 +174,9 @@ struct mt_device {
        struct timer_list release_timer;        /* to release sticky fingers */
        struct hid_haptic_device *haptic;       /* haptic related configuration */
        struct hid_device *hdev;        /* hid_device we're attached to */
-       unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_RUNNING)
-                                        * first 8 bits are reserved for keeping the slot
-                                        * states, this is fine because we only support up
-                                        * to 250 slots (MT_MAX_MAXCONTACT)
+       unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_RUNNING) */
+       unsigned long *active_slots;    /* bitmap of slots with an active
+                                        * contact, sized for maxcontacts
                                         */
        __u8 inputmode_value;   /* InputMode HID feature value */
        __u8 maxcontacts;
@@ -1036,7 +1035,7 @@ static void mt_release_pending_palms(struct mt_device *td,
 
        for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) {
                clear_bit(slotnum, app->pending_palm_slots);
-               clear_bit(slotnum, &td->mt_io_flags);
+               clear_bit(slotnum, td->active_slots);
 
                input_mt_slot(input, slotnum);
                input_mt_report_slot_inactive(input);
@@ -1247,9 +1246,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
                input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
                input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
 
-               set_bit(slotnum, &td->mt_io_flags);
+               set_bit(slotnum, td->active_slots);
        } else {
-               clear_bit(slotnum, &td->mt_io_flags);
+               clear_bit(slotnum, td->active_slots);
        }
 
        return 0;
@@ -1384,7 +1383,7 @@ static void mt_touch_report(struct hid_device *hid,
         * defect.
         */
        if (app->quirks & MT_QUIRK_STICKY_FINGERS) {
-               if (td->mt_io_flags & MT_IO_SLOTS_MASK)
+               if (!bitmap_empty(td->active_slots, td->maxcontacts))
                        mod_timer(&td->release_timer,
                                  jiffies + msecs_to_jiffies(100));
                else
@@ -1443,6 +1442,15 @@ static int mt_touch_input_configured(struct hid_device *hdev,
        if (td->is_pressurepad)
                __set_bit(INPUT_PROP_PRESSUREPAD, input->propbit);
 
+       if (!td->active_slots) {
+               td->active_slots = devm_kcalloc(&td->hdev->dev,
+                                               BITS_TO_LONGS(td->maxcontacts),
+                                               sizeof(long),
+                                               GFP_KERNEL);
+               if (!td->active_slots)
+                       return -ENOMEM;
+       }
+
        app->pending_palm_slots = devm_kcalloc(&hi->input->dev,
                                               BITS_TO_LONGS(td->maxcontacts),
                                               sizeof(long),
@@ -2062,7 +2070,7 @@ static void mt_release_contacts(struct hid_device *hid)
                        for (i = 0; i < mt->num_slots; i++) {
                                input_mt_slot(input_dev, i);
                                input_mt_report_slot_inactive(input_dev);
-                               clear_bit(i, &td->mt_io_flags);
+                               clear_bit(i, td->active_slots);
                        }
                        input_mt_sync_frame(input_dev);
                        input_sync(input_dev);
@@ -2085,7 +2093,7 @@ static void mt_expired_timeout(struct timer_list *t)
         */
        if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
                return;
-       if (td->mt_io_flags & MT_IO_SLOTS_MASK)
+       if (!bitmap_empty(td->active_slots, td->maxcontacts))
                mt_release_contacts(hdev);
        clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
 }
index 2cc01e1bc1a844b7458622adaf8ec7159c98183c..d73e97c8b853e97acd7293eac8d830fdd7c43f6b 100644 (file)
@@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
        struct picolcd_pending *work;
        struct hid_report *report = picolcd_out_report(report_id, hdev);
        unsigned long flags;
-       int i, j, k;
+       int i, j;
+       unsigned int k;
 
        if (!report || !data)
                return NULL;
index 90666ff629defa97590ca411090bf996861304fa..34f710c465b80a0c46cc207e3d99a07c5767f291 100644 (file)
@@ -286,6 +286,54 @@ done_proc:
 }
 EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
 
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+                                     u32 usage_id, u32 attr_usage_id,
+                                     u32 report_id,
+                                     enum sensor_hub_read_flags flag,
+                                     u32 buffer_size, u8 *buffer)
+{
+       struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+       struct hid_report *report;
+       unsigned long flags;
+       long cycles;
+       int ret;
+
+       report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
+       if (!report)
+               return -EINVAL;
+
+       mutex_lock(hsdev->mutex_ptr);
+       if (flag == SENSOR_HUB_SYNC) {
+               memset(&hsdev->pending, 0, sizeof(hsdev->pending));
+               init_completion(&hsdev->pending.ready);
+               hsdev->pending.usage_id = usage_id;
+               hsdev->pending.attr_usage_id = attr_usage_id;
+               hsdev->pending.max_raw_size = buffer_size;
+               hsdev->pending.raw_data = buffer;
+
+               spin_lock_irqsave(&data->lock, flags);
+               hsdev->pending.status = true;
+               spin_unlock_irqrestore(&data->lock, flags);
+       }
+       mutex_lock(&data->mutex);
+       hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
+       mutex_unlock(&data->mutex);
+       ret = 0;
+       if (flag == SENSOR_HUB_SYNC) {
+               cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready,
+                                                                  HZ * 5);
+               if (cycles == 0)
+                       ret = -ETIMEDOUT;
+               else if (cycles < 0)
+                       ret = cycles;
+
+               hsdev->pending.status = false;
+       }
+       mutex_unlock(hsdev->mutex_ptr);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values);
 
 int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        u32 usage_id,
@@ -478,6 +526,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
        struct hid_collection *collection = NULL;
        void *priv = NULL;
        struct hid_sensor_hub_device *hsdev = NULL;
+       u32 copy_size;
+       u32 avail;
 
        hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
                         report->id, size, report->type);
@@ -518,12 +568,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
                                              hsdev->pending.attr_usage_id ==
                                              report->field[i]->logical)) {
                        hid_dbg(hdev, "data was pending ...\n");
-                       hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
-                       if (hsdev->pending.raw_data)
-                               hsdev->pending.raw_size = sz;
-                       else
-                               hsdev->pending.raw_size = 0;
-                       complete(&hsdev->pending.ready);
+                       if (hsdev->pending.max_raw_size) {
+                               if (hsdev->pending.index < hsdev->pending.max_raw_size) {
+                                       avail = hsdev->pending.max_raw_size - hsdev->pending.index;
+                                       copy_size = clamp(sz, 0U, avail);
+
+                                       memcpy(hsdev->pending.raw_data + hsdev->pending.index,
+                                              ptr, copy_size);
+                                       hsdev->pending.index += copy_size;
+                                       if (hsdev->pending.index >= hsdev->pending.max_raw_size) {
+                                               hsdev->pending.raw_size = hsdev->pending.index;
+                                               complete(&hsdev->pending.ready);
+                                       }
+                               }
+                       } else {
+                               hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
+                               if (hsdev->pending.raw_data)
+                                       hsdev->pending.raw_size = sz;
+                               else
+                                       hsdev->pending.raw_size = 0;
+                               complete(&hsdev->pending.ready);
+                       }
                }
                if (callback->capture_sample) {
                        if (report->field[i]->logical)
index 28313d0fad37e161f27991d7449bf3f5a43ffb05..d5e6e2eca3b34da30dc7a54d8dcd09436ae754de 100644 (file)
@@ -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,
index 9152cf436bea0867bf830b4e49cad4b1fc66e09f..51a0c3d80fc92fa673af4820c9149a566770dbf2 100644 (file)
@@ -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;
 
index 6c1cfe9ec8accefaa3f95424393953ff3b869ff6..e33512b25353189e1e54ee0c8e5c1d7745d0dc9d 100644 (file)
@@ -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;
index 126040ca05f1470f0e003d25f4c6d65a3a52790f..307925fb78e361d469357ceb21e6fce449f4f88b 100644 (file)
@@ -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;
index 2398eb7e12cdd25ce0ac988a9535c7a833eb664a..dc8a6285cf3df462de43fbcab11c19942dc2f7f3 100644 (file)
@@ -991,6 +991,8 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
        if (samples && count > samples)
                count = samples;
 
+       count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH);
+
        ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count);
        if (ret)
                return ret;
index 4717d80fc24af69f611c270d0587b5a7d8fea426..7ac885d94d7f45edc7f7823165a1ad7fff39f8a7 100644 (file)
@@ -147,8 +147,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
        if (mask == IIO_CHAN_INFO_SCALE) {
                /* Check no integer component */
                if (val)
-                       return -EINVAL;
-               ret = kxsd9_write_scale(indio_dev, val2);
+                       ret = -EINVAL;
+               else
+                       ret = kxsd9_write_scale(indio_dev, val2);
        }
 
        pm_runtime_put_autosuspend(st->dev);
index 1c663c98c6c96a098dc90539ff7790c3cdf84af2..3755a81c1efdb7b5adb4951d07e4ef1c09cc10c7 100644 (file)
@@ -108,6 +108,7 @@ config AD4130
        depends on SPI
        depends on GPIOLIB
        select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        select IIO_KFIFO_BUF
        select REGMAP_SPI
        depends on COMMON_CLK
@@ -328,6 +329,7 @@ config AD7298
 config AD7380
        tristate "Analog Devices AD7380 ADC driver"
        depends on SPI_MASTER
+       select REGMAP
        select SPI_OFFLOAD
        select IIO_BUFFER
        select IIO_BUFFER_DMAENGINE
@@ -452,6 +454,7 @@ config AD7779
        depends on SPI
        select CRC8
        select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        select IIO_BACKEND
        help
          Say yes here to build support for Analog Devices AD777X family
index 0128d003f960d57e31adc8d73fe5e16c1d45216f..32a15d193d97ddbc99c7d31457f5e672644e022c 100644 (file)
@@ -178,6 +178,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
+       init_completion(&st->completion);
+
        retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
                                  LPC32XXAD_NAME, st);
        if (retval < 0) {
@@ -196,8 +198,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, iodev);
 
-       init_completion(&st->completion);
-
        iodev->name = LPC32XXAD_NAME;
        iodev->info = &lpc32xx_adc_iio_info;
        iodev->modes = INDIO_DIRECT_MODE;
index 35b81331fdeeeb67d84e707d5cf9ca589fe8bf71..894d3211b28382e7c5faa83eed0731bf51c22610 100644 (file)
@@ -197,13 +197,13 @@ static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable)
                writel(0, NXP_SAR_ADC_IMR(info->regs));
 }
 
-static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, unsigned int cycles)
+static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, u64 cycles)
 {
        u64 rate;
 
        rate = clk_get_rate(info->clk);
        if (rate)
-               ndelay(div64_u64(NSEC_PER_SEC, rate * cycles));
+               ndelay(div64_u64(NSEC_PER_SEC * cycles, rate));
 }
 
 static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable)
index bdb3ca8f229a028324af57c5a05194e557ad1dac..91c0fb1f4da78482229be149494ba7af118fd5a7 100644 (file)
@@ -282,6 +282,7 @@ static int spear_adc_probe(struct platform_device *pdev)
        st = iio_priv(indio_dev);
        st->dev = dev;
 
+       init_completion(&st->completion);
        mutex_init(&st->lock);
 
        /*
@@ -328,8 +329,6 @@ static int spear_adc_probe(struct platform_device *pdev)
 
        spear_adc_configure(st);
 
-       init_completion(&st->completion);
-
        indio_dev->name = SPEAR_ADC_MOD_NAME;
        indio_dev->info = &spear_adc_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
index d31f3d6eb7811daca2f0f9cf460884164cfea8a7..b0f04741ddc6e7fa4bdfd02d3f8e833b2a3d73e9 100644 (file)
@@ -459,7 +459,11 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       return i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC);
+       ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC);
+       if (ret)
+               pm_runtime_put_autosuspend(dev);
+
+       return ret;
 }
 
 static int ads1119_triggered_buffer_postdisable(struct iio_dev *indio_dev)
index 522b43118af607bae5d7f720e525ca59f0c5c979..45de477788090d392225da71c15bfcff1752ba88 100644 (file)
@@ -320,7 +320,8 @@ static int ads124s_probe(struct spi_device *spi)
        ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
                                                   "reset", GPIOD_OUT_LOW);
        if (IS_ERR(ads124s_priv->reset_gpio))
-               dev_info(&spi->dev, "Reset GPIO not defined\n");
+               return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio),
+                                    "Failed to get reset GPIO\n");
 
        ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
 
index dbc5e16fbde43ee61bdffeb94d7119a226f3949f..76f91696f66a36b81744a0f2f98f106527667728 100644 (file)
@@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
        u8 *outdata;
        struct st_sensor_data *sdata = iio_priv(indio_dev);
        unsigned int byte_for_channel;
+       u32 tmp;
 
        byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
                                        ch->scan_type.shift, 8);
@@ -508,12 +509,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
        if (err < 0)
                return err;
 
-       if (byte_for_channel == 1)
-               *data = (s8)*outdata;
-       else if (byte_for_channel == 2)
-               *data = (s16)get_unaligned_le16(outdata);
-       else if (byte_for_channel == 3)
-               *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23);
+       if (byte_for_channel == 1) {
+               tmp = *outdata;
+       } else if (byte_for_channel == 2) {
+               if (ch->scan_type.endianness == IIO_BE)
+                       tmp = get_unaligned_be16(outdata);
+               else
+                       tmp = get_unaligned_le16(outdata);
+       } else if (byte_for_channel == 3) {
+               if (ch->scan_type.endianness == IIO_BE)
+                       tmp = get_unaligned_be24(outdata);
+               else
+                       tmp = get_unaligned_le24(outdata);
+       } else {
+               return -EINVAL;
+       }
+       *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1);
 
        return 0;
 }
index 5c0f3064df7ae0d4e5dbded87993e7f390431407..8640b0ef4433da2b40d4b7e53fe6109737314951 100644 (file)
@@ -1135,26 +1135,33 @@ static int mcp47feb02_probe(struct i2c_client *client)
 
        vdd_uV = ret;
 
-       ret = devm_regulator_get_enable_read_voltage(dev, "vref");
-       if (ret > 0) {
-               vref_uV = ret;
+       if (device_property_present(dev, "vref-supply")) {
+               vref_uV = devm_regulator_get_enable_read_voltage(dev, "vref");
+               if (vref_uV < 0)
+                       return vref_uV;
+
+               if (vref_uV == 0)
+                       return dev_err_probe(dev, -EINVAL, "Vref is 0 uV.\n");
+
                data->use_vref = true;
        } else {
                vref_uV = 0;
-               dev_dbg(dev, "using internal band gap as voltage reference.\n");
-               dev_dbg(dev, "Vref is unavailable.\n");
+               dev_dbg(dev, "Using internal band gap as voltage reference.\n");
        }
 
-       if (chip_features->have_ext_vref1) {
-               ret = devm_regulator_get_enable_read_voltage(dev, "vref1");
-               if (ret > 0) {
-                       vref1_uV = ret;
-                       data->use_vref1 = true;
-               } else {
-                       vref1_uV = 0;
-                       dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
-                       dev_dbg(dev, "Vref1 is unavailable.\n");
-               }
+       if (chip_features->have_ext_vref1 &&
+           device_property_present(dev, "vref1-supply")) {
+               vref1_uV = devm_regulator_get_enable_read_voltage(dev, "vref1");
+               if (vref1_uV < 0)
+                       return vref1_uV;
+
+               if (vref1_uV == 0)
+                       return dev_err_probe(dev, -EINVAL, "Vref1 is 0 uV.\n");
+
+               data->use_vref1 = true;
+       } else {
+               vref1_uV = 0;
+               dev_dbg(dev, "Using internal band gap as voltage reference 1.\n");
        }
 
        ret = mcp47feb02_init_ctrl_regs(data);
index d76e13cbac680331e3712a4e02ea96d2efe638f0..3e6a7af6ab017ed87a3ddc7829beeb9cf30f48fc 100644 (file)
@@ -94,7 +94,7 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
        else
                ret = devm_request_irq(&adis->spi->dev, adis->spi->irq,
                                       &iio_trigger_generic_data_rdy_poll,
-                                      adis->irq_flag,
+                                      adis->irq_flag | IRQF_NO_THREAD,
                                       indio_dev->name,
                                       adis->trig);
        if (ret)
index 4abb83b75e2e87a7d83b2505d164cbd96ddad30b..86f6ecfd64aa2fbd6403705249a5111c82d7531a 100644 (file)
@@ -788,7 +788,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
 
        ret = devm_request_irq(&indio_dev->dev, irq,
                               &iio_trigger_generic_data_rdy_poll,
-                              irq_type, "bmi160", data->trig);
+                              irq_type | IRQF_NO_THREAD,
+                              "bmi160", data->trig);
        if (ret)
                return ret;
 
index 532d5fdffaf8d89dd296da2b5d6dafb3a76ef551..7df920ef3cf08b9478e21dd4b4c0a4c1e1e23d76 100644 (file)
@@ -1170,10 +1170,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
        accel_st->filter = INV_ICM42600_FILTER_AVG_16X;
 
        /*
-        * clock period is 32kHz (31250ns)
+        * clock period is 8kHz (125000ns)
         * jitter is +/- 2% (20 per mille)
         */
-       ts_chip.clock_period = 31250;
+       ts_chip.clock_period = 125000;
        ts_chip.jitter = 20;
        ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
        inv_sensors_timestamp_init(&accel_st->ts, &ts_chip);
index 68a395758031884792781d6eee748d273a86a22e..5c3840acf085799ce9f723ac26c22468584fec08 100644 (file)
@@ -248,6 +248,7 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
 
        /* compute watermark value in bytes */
        wm_size = watermark * packet_size;
+       st->fifo.watermark.value = watermark;
 
        /* changing FIFO watermark requires to turn off watermark interrupt */
        ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,
@@ -454,11 +455,10 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
        st->fifo.nb.accel = 0;
        st->fifo.nb.total = 0;
 
-       /* compute maximum FIFO read size */
+       /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */
        if (max == 0)
-               max_count = sizeof(st->fifo.data);
-       else
-               max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
+               max = st->fifo.watermark.value;
+       max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
 
        /* read FIFO count value */
        raw_fifo_count = (__be16 *)st->buffer;
@@ -574,6 +574,7 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st)
 
        st->fifo.watermark.eff_gyro = 1;
        st->fifo.watermark.eff_accel = 1;
+       st->fifo.watermark.value = 1;
 
        /*
         * Default FIFO configuration (bits 7 to 5)
index ffca4da1e249364c75fff12bdba68c8429420129..88b8b9f780af3bd16c2833973225f83a7bb16b44 100644 (file)
@@ -34,6 +34,7 @@ struct inv_icm42600_fifo {
                unsigned int accel;
                unsigned int eff_gyro;
                unsigned int eff_accel;
+               unsigned int value;
        } watermark;
        size_t count;
        struct {
index 11339ddf1da36c85e56de6c4a95486713cbd182a..a18dcac939298a5f93aeb30518fca85ef0366463 100644 (file)
@@ -755,10 +755,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
        }
 
        /*
-        * clock period is 32kHz (31250ns)
+        * clock period is 8kHz (125000ns)
         * jitter is +/- 2% (20 per mille)
         */
-       ts_chip.clock_period = 31250;
+       ts_chip.clock_period = 125000;
        ts_chip.jitter = 20;
        ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
        inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
index 630e2cae6f19e5857f6ae1845307c2f69637ea1e..f4edcb73ec8c2d0041bfe2d0435b7d9c56820a1c 100644 (file)
@@ -1712,6 +1712,26 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
                return -ENODEV;
        }
 
+       hw->settings = &st_lsm6dsx_sensor_settings[i];
+
+       if (hw->settings->shub_settings.page_mux.addr) {
+               /*
+                * If the IMU has the shub page selected on init, for example
+                * after a CPU watchdog reset while the page is selected, the
+                * regular register space is shadowed. While the regular
+                * register space is shadowed, the registers needed for
+                * initializing the IMU are not available.
+                *
+                * Unconditionally clear the shub page selection to ensure
+                * normal register access.
+                */
+               err = st_lsm6dsx_set_page(hw, false);
+               if (err < 0) {
+                       dev_err(hw->dev, "failed to clear shub page\n");
+                       return err;
+               }
+       }
+
        err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data);
        if (err < 0) {
                dev_err(hw->dev, "failed to read whoami register\n");
@@ -1724,7 +1744,6 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
        }
 
        *name = st_lsm6dsx_sensor_settings[i].id[j].name;
-       hw->settings = &st_lsm6dsx_sensor_settings[i];
 
        return 0;
 }
index a0d6fcf2a9c9b61bd9c0493525ec8b4f2920bbfa..e6730f52262a47fd2b627bb37aa81a89b7868295 100644 (file)
@@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev)
                goto unlock;
        }
 
+       kfifo_reset_out(&ev_int->det_events);
+
        iio_device_get(indio_dev);
 
        fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
@@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev)
        if (fd < 0) {
                clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
                iio_device_put(indio_dev);
-       } else {
-               kfifo_reset_out(&ev_int->det_events);
        }
-
 unlock:
        mutex_unlock(&iio_dev_opaque->mlock);
        return fd;
index ef36824f312f0f64d088d21e7ab079c71a11c0dc..f23bbce12c72c7333897b5e640cd9e29c3f16b57 100644 (file)
@@ -45,6 +45,7 @@ config ADUX1020
 
 config AL3000A
        tristate "AL3000a ambient light sensor"
+       select REGMAP_I2C
        depends on I2C
        help
          Say Y here if you want to build a driver for the Dyna Image AL3000a
@@ -55,6 +56,7 @@ config AL3000A
 
 config AL3010
        tristate "AL3010 ambient light sensor"
+       select REGMAP_I2C
        depends on I2C
        help
          Say Y here if you want to build a driver for the Dyna Image AL3010
@@ -65,6 +67,7 @@ config AL3010
 
 config AL3320A
        tristate "AL3320A ambient light sensor"
+       select REGMAP_I2C
        depends on I2C
        help
          Say Y here if you want to build a driver for the Dyna Image AL3320A
index 62a77acfd0755e7aacc2f8967f73b39dd8bfa548..49f6d8336dbedd230adb3ded27749783e0f8cf47 100644 (file)
@@ -41,7 +41,7 @@ enum al3xxxx_range {
 };
 
 static const int al3010_scales[][2] = {
-       {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
+       { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 },
 };
 
 static const struct regmap_config al3010_regmap_config = {
index c83f67ff2464a3ce2313c9a9017faf39f4e74be7..a8db514cca5ef500d30c30f82a2a6d342c1112f0 100644 (file)
@@ -258,7 +258,7 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev,
                case IIO_LIGHT:
                        ret = gp2ap002_get_lux(gp2ap002);
                        if (ret < 0)
-                               return ret;
+                               goto out;
                        *val = ret;
                        ret = IIO_VAL_INT;
                        goto out;
index f3ffa9721ad57b073036e8d2e4e51d28a43a1ba7..ef3ed9635a1ef8ee716208b4481339fb5353a4a4 100644 (file)
@@ -1070,10 +1070,8 @@ static int tsl2591_probe(struct i2c_client *client)
                                                NULL, tsl2591_event_handler,
                                                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                                "tsl2591_irq", indio_dev);
-               if (ret) {
-                       dev_err_probe(&client->dev, ret, "IRQ request error\n");
-                       return -EINVAL;
-               }
+               if (ret)
+                       return ret;
                indio_dev->info = &tsl2591_info;
        } else {
                indio_dev->info = &tsl2591_info_no_irq;
index 52d39c104560e63d5c7203de61f2c796a54053d3..cc3e66dbb90fcbce087d79612fa0644c142e8d37 100644 (file)
@@ -85,6 +85,13 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
                                long mask)
 {
        struct dev_rot_state *rot_state = iio_priv(indio_dev);
+       struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev;
+       struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion;
+       u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION;
+       union {
+               s16 val16[4];
+               s32 val32[4];
+       } raw_buf;
        int ret_type;
        int i;
 
@@ -94,8 +101,37 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                if (size >= 4) {
-                       for (i = 0; i < 4; ++i)
-                               vals[i] = rot_state->scan.sampled_vals[i];
+                       if (info->size <= 0 || info->size > sizeof(raw_buf))
+                               return -EINVAL;
+
+                       hid_sensor_power_state(&rot_state->common_attributes, true);
+
+                       ret_type = sensor_hub_input_attr_read_values(hsdev,
+                                                                    hsdev->usage,
+                                                                    usage_id,
+                                                                    info->report_id,
+                                                                    SENSOR_HUB_SYNC,
+                                                                    info->size,
+                                                                    (u8 *)&raw_buf);
+
+                       hid_sensor_power_state(&rot_state->common_attributes, false);
+
+                       if (ret_type < 0)
+                               return ret_type;
+
+                       switch (info->size) {
+                       case sizeof(raw_buf.val16):
+                               for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++)
+                                       vals[i] = raw_buf.val16[i];
+                               break;
+                       case sizeof(raw_buf.val32):
+                               for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++)
+                                       vals[i] = raw_buf.val32[i];
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+
                        ret_type = IIO_VAL_INT_MULTIPLE;
                        *val_len =  4;
                } else
index 830a5065c008f88493faac8b03cc382ceeb39420..16e112b796ba9e7aec3a998b5ffc6aff3ac2f5e3 100644 (file)
@@ -106,18 +106,18 @@ static int mpl115_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_PROCESSED:
                pm_runtime_get_sync(data->dev);
                ret = mpl115_comp_pressure(data, val, val2);
+               pm_runtime_put_autosuspend(data->dev);
                if (ret < 0)
                        return ret;
-               pm_runtime_put_autosuspend(data->dev);
 
                return IIO_VAL_INT_PLUS_MICRO;
        case IIO_CHAN_INFO_RAW:
                pm_runtime_get_sync(data->dev);
                /* temperature -5.35 C / LSB, 472 LSB is 25 C */
                ret = mpl115_read_temp(data);
+               pm_runtime_put_autosuspend(data->dev);
                if (ret < 0)
                        return ret;
-               pm_runtime_put_autosuspend(data->dev);
                *val = ret >> 6;
 
                return IIO_VAL_INT;
index 07d6e65709f7fe6e1ed51867e08112c8a68ee98a..0850bf691820454633e75a68c9ddb23e93f2e483 100644 (file)
@@ -13,7 +13,7 @@ obj-$(CONFIG_MAX31865) += max31865.o
 obj-$(CONFIG_MCP9600) += mcp9600.o
 obj-$(CONFIG_MLX90614) += mlx90614.o
 obj-$(CONFIG_MLX90632) += mlx90632.o
-obj-$(CONFIG_MLX90632) += mlx90635.o
+obj-$(CONFIG_MLX90635) += mlx90635.o
 obj-$(CONFIG_TMP006) += tmp006.o
 obj-$(CONFIG_TMP007) += tmp007.o
 obj-$(CONFIG_TMP117) += tmp117.o
index 9480d1a51c116ef088277983175a11ca5c898d58..e88d3efb967b3bb59ea91f8a37445b769cd91e7c 100644 (file)
@@ -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))
index 8d19613179e3eebda79cc15a31e13301274d90ee..e0b3b36b8b1496550e3ef22a31e9f04173778cff 100644 (file)
@@ -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);
index 3b613b57e269781e94e9d63ea75c7dcc46b1dacb..86811d31092ce03c8156b444fece47b824231219 100644 (file)
@@ -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;
index 25f6c49aec77989c3eddbc82e291796a9425393c..e002343832f74d0834c270427035f49c71f44431 100644 (file)
@@ -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);
 
index 7041a8e9134b220b428d37a51bd444a87d25610e..92edec4fa61b6c144b78af5b1dccd8a8e6d9e8fc 100644 (file)
@@ -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);
        }
index cb54c7c8fcd8fc6b5e7d92f02d0a19bde94d482d..b7388b41ed958929460c32cb7d702d446e35f3c0 100644 (file)
@@ -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);
index 1813567d3b16c9b16264d6df60c84ca502265c1b..36a1d506f08f65a6337217f4eed95a48754ab7ea 100644 (file)
@@ -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) {
index 9947feb7fb8a0bcd1ecf9e5d136e9ea7e326e8e7..fca9e1d9d5e9bda8d2463630c8f1d6f2112e0c9c 100644 (file)
@@ -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;
index b74ac85c1b8b8bcc8b83133f467f3e253f08ec7f..2349c08c32ede920f412791dd3aab1aec45344e3 100644 (file)
@@ -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);
index 7f36f73844a910691df9e1ea57bc79e42c80524e..6293b6e8148b34e19e4542a74b25f80a2fdd29aa 100644 (file)
@@ -112,6 +112,8 @@ static int probe_maple_controller(struct device *dev)
        pad->dev = idev;
        pad->mdev = mdev;
 
+       maple_set_drvdata(mdev, pad);
+
        idev->open = dc_pad_open;
        idev->close = dc_pad_close;
 
@@ -146,7 +148,6 @@ static int probe_maple_controller(struct device *dev)
                goto fail;
 
        mdev->driver = mdrv;
-       maple_set_drvdata(mdev, pad);
 
        return 0;
 
index 80a5181313e15301a5b72fd530414c2e6b2bf3ee..3d5538dd4f2374a1a5e4ef934b64e3483842ed7e 100644 (file)
@@ -166,6 +166,8 @@ static int probe_maple_kbd(struct device *dev)
        kbd->dev = idev;
        memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
 
+       maple_set_drvdata(mdev, kbd);
+
        idev->name = mdev->product_name;
        idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
        idev->keycode = kbd->keycode;
@@ -190,8 +192,6 @@ static int probe_maple_kbd(struct device *dev)
 
        mdev->driver = mdrv;
 
-       maple_set_drvdata(mdev, kbd);
-
        return error;
 
 fail_register:
index c99f7e23421911f1cd0a91245636ab0592c70057..0c8f7d1b02aa3c99ab64ee0ed2c5e1bd94536549 100644 (file)
@@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapleq *mq)
 
 static int dc_mouse_open(struct input_dev *dev)
 {
-       struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+       struct dc_mouse *mse = input_get_drvdata(dev);
 
        maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
                MAPLE_FUNC_MOUSE);
@@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_dev *dev)
 
 static void dc_mouse_close(struct input_dev *dev)
 {
-       struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+       struct dc_mouse *mse = input_get_drvdata(dev);
 
        maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
                MAPLE_FUNC_MOUSE);
@@ -88,6 +88,9 @@ static int probe_maple_mouse(struct device *dev)
        mse->dev = input_dev;
        mse->mdev = mdev;
 
+       maple_set_drvdata(mdev, mse);
+
+       input_set_drvdata(input_dev, mse);
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
        input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
                BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
@@ -102,12 +105,12 @@ static int probe_maple_mouse(struct device *dev)
                goto fail_register;
 
        mdev->driver = mdrv;
-       maple_set_drvdata(mdev, mse);
 
        return error;
 
 fail_register:
        input_free_device(input_dev);
+       maple_set_drvdata(mdev, NULL);
 fail_nomem:
        kfree(mse);
 fail:
index 53ad35d61d476c12955edff69b52b12dc44bd898..5cef97246a1584e2b2444b6dafce262bc91adf17 100644 (file)
@@ -165,7 +165,7 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou
        unsigned int x;
        unsigned int y;
 
-       if (touch->id > MMS114_MAX_TOUCH) {
+       if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) {
                dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
                return;
        }
index 26fedf5883eff6d2fa1703364713d616e5df07c6..a458b9fd2fcd627ef391561e1a2a8e8181489050 100644 (file)
@@ -2238,7 +2238,9 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c
        struct dm_io_region io_reg = {
                .bdev = c->bdev,
                .sector = block_to_sector(c, block),
-               .count = block_to_sector(c, count),
+               .count = likely(c->sectors_per_block_bits >= 0) ?
+                       count << c->sectors_per_block_bits :
+                       count * (c->block_size >> SECTOR_SHIFT),
        };
 
        if (WARN_ON_ONCE(dm_bufio_in_request()))
index 05285c04ff2cdae285e18c80ecf005f55dfa7d77..7fe4d19ade4f03ed753c0530f5b5b26e1c18a521 100644 (file)
@@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev,
        int r;
        struct era_metadata *md = kzalloc_obj(*md);
 
-       if (!md)
-               return NULL;
+       if (!md) {
+               DMERR("could not allocate metadata struct");
+               return ERR_PTR(-ENOMEM);
+       }
 
        md->bdev = bdev;
        md->block_size = block_size;
@@ -1229,6 +1231,7 @@ static dm_block_t get_block(struct era *era, struct bio *bio)
 static void remap_to_origin(struct era *era, struct bio *bio)
 {
        bio_set_dev(bio, era->origin_dev->bdev);
+       bio->bi_iter.bi_sector = dm_target_offset(era->ti, bio->bi_iter.bi_sector);
 }
 
 /*
@@ -1486,7 +1489,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        if (r) {
                ti->error = "Error opening metadata device";
                era_destroy(era);
-               return -EINVAL;
+               return r;
        }
 
        r = dm_get_device(ti, argv[1], BLK_OPEN_READ | BLK_OPEN_WRITE,
@@ -1494,7 +1497,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        if (r) {
                ti->error = "Error opening data device";
                era_destroy(era);
-               return -EINVAL;
+               return r;
        }
 
        r = sscanf(argv[2], "%u%c", &era->sectors_per_block, &dummy);
@@ -1508,7 +1511,7 @@ static int era_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        if (r) {
                ti->error = "could not set max io len";
                era_destroy(era);
-               return -EINVAL;
+               return r;
        }
 
        if (!valid_block_size(era->sectors_per_block)) {
@@ -1560,7 +1563,7 @@ static void era_dtr(struct dm_target *ti)
 static int era_map(struct dm_target *ti, struct bio *bio)
 {
        struct era *era = ti->private;
-       dm_block_t block = get_block(era, bio);
+       dm_block_t block;
 
        /*
         * All bios get remapped to the origin device.  We do this now, but
@@ -1568,6 +1571,7 @@ static int era_map(struct dm_target *ti, struct bio *bio)
         * block is marked in this era.
         */
        remap_to_origin(era, bio);
+       block = get_block(era, bio);
 
        /*
         * REQ_PREFLUSH bios carry no data, so we're not interested in them.
index be1b4aa8f28bcf1682b20bcd3a8c5cb9e0815bc9..41293c18d10f2b96fa3219b10800366dd44edb2f 100644 (file)
@@ -347,7 +347,8 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        err = get_key_size(&argv[1]);
        if (err < 0) {
                ti->error = "Cannot parse key size";
-               return -EINVAL;
+               err = -EINVAL;
+               goto bad;
        }
        ctx->key_size = err;
 
@@ -398,6 +399,7 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        if (ctx->iv_offset & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
                ti->error = "Wrong alignment of iv_offset sector";
                err = -EINVAL;
+               goto bad;
        }
 
        ctx->max_dun = (ctx->iv_offset + ti->len - 1) >>
index 65c30dec82220375446a770376f0e8a571ac138b..1f2593f113f642bb08b164db06a3a6c96191754e 100644 (file)
@@ -1480,9 +1480,6 @@ thorough_test:
                        *metadata_offset = 0;
                }
 
-               if (unlikely(!is_power_of_2(ic->tag_size)))
-                       hash_offset = (hash_offset + to_copy) % ic->tag_size;
-
                total_size -= to_copy;
        } while (unlikely(total_size));
 
@@ -2523,6 +2520,9 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)
        if (unlikely((bio->bi_opf & REQ_PREFLUSH) != 0))
                return DM_MAPIO_REMAPPED;
 
+       if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio)))
+               return DM_MAPIO_KILL;
+
 retry:
        if (!dio->integrity_payload) {
                unsigned digest_size, extra_size;
@@ -2587,10 +2587,6 @@ skip_spinlock:
 
        dio->bio_details.bi_iter = bio->bi_iter;
 
-       if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) {
-               return DM_MAPIO_KILL;
-       }
-
        bio->bi_iter.bi_sector += ic->start + SB_SECTORS;
 
        bip = bio_integrity_alloc(bio, GFP_NOIO, 1);
@@ -2606,7 +2602,7 @@ skip_spinlock:
                        struct bio_vec bv = bio_iter_iovec(bio, dio->bio_details.bi_iter);
                        const char *mem = integrity_kmap(ic, bv.bv_page);
                        if (ic->tag_size < ic->tuple_size)
-                               memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tuple_size);
+                               memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tag_size);
                        integrity_sector_checksum(ic, &dio->ahash_req, dio->bio_details.bi_iter.bi_sector, mem, bv.bv_offset, dio->integrity_payload + pos);
                        integrity_kunmap(ic, mem);
                        pos += ic->tuple_size;
@@ -5130,6 +5126,20 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv
                ti->error = "Journal mac mismatch";
                goto bad;
        }
+       if (ic->fix_hmac && !(ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) && ic->journal_mac_alg.key_string) {
+               /*
+                * If this happens, it may be either because someone tampered
+                * with the device or it may be due to a bug in the
+                * integritysetup tool.
+                *
+                * In the latter case, upgrade to integritysetup 2.8.7 and use
+                * the argument --integrity-legacy-hmac when using the open
+                * command.
+                */
+               r = -EINVAL;
+               ti->error = "fix_hmac is on the command line but not in the superblock";
+               goto bad;
+       }
 
        get_provided_data_sectors(ic);
        if (!ic->provided_data_sectors) {
index ac77dc0ca22553e272a12ea8af3033cf3e969ce7..61af2a437a050856565f4da5d7a131010cbaf409 100644 (file)
@@ -785,7 +785,7 @@ static void list_version_get_info(struct target_type *tt, void *param)
        struct vers_iter *info = param;
 
        /* Check space - it might have changed since the first iteration */
-       if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) {
+       if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) {
                info->flags = DM_BUFFER_FULL_FLAG;
                return;
        }
@@ -2473,7 +2473,7 @@ int __init dm_early_create(struct dm_ioctl *dmi,
        /* resume device */
        r = dm_resume(md);
        if (r)
-               goto err_destroy_table;
+               goto err_hash_remove;
 
        DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
        dm_put(md);
index d316757a328beccfefa33373e79a077675fbba84..2ddeb4250c5929d9636cae00dcefe2d0f441c966 100644 (file)
@@ -425,6 +425,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
         */
        bitset_size = dm_round_up(region_count, BITS_PER_LONG);
        bitset_size >>= BYTE_SHIFT;
+       /* Handle dm_round_up rollover on 32-bit systems */
+       if (!bitset_size)
+               bitset_size = 1UL << (BITS_PER_LONG - BYTE_SHIFT);
 
        lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits);
 
index 81c795c0400e930f19651ef0902547892ea342a4..d5cfd162c06335d7459f71fb02df220ce0355545 100644 (file)
@@ -168,6 +168,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as,
                argc--;
 
                if (!strcmp(arg, "cache_mode")) {
+                       if (!argc) {
+                               *error = "Missing value for cache_mode";
+                               return -EINVAL;
+                       }
                        arg = dm_shift_arg(as);
                        if (!strcmp(arg, "writeback")) {
                                opts->cache_mode = PCACHE_CACHE_MODE_WRITEBACK;
@@ -177,6 +181,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as,
                        }
                        argc--;
                } else if (!strcmp(arg, "data_crc")) {
+                       if (!argc) {
+                               *error = "Missing value for data_crc";
+                               return -EINVAL;
+                       }
                        arg = dm_shift_arg(as);
                        if (!strcmp(arg, "true")) {
                                opts->data_crc = true;
index c53cf07ab7b0b6885143ad972c56a394fb51c76b..5df710061a11653f901c8c6bbf5fe6192928b6f9 100644 (file)
@@ -692,10 +692,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
                 */
                last = raw_cpu_ptr(stats->last);
                stats_aux->merged =
-                       (bi_sector == (READ_ONCE(last->last_sector) &&
-                                      ((bi_rw == WRITE) ==
-                                       (READ_ONCE(last->last_rw) == WRITE))
-                                      ));
+                       bi_sector == READ_ONCE(last->last_sector) &&
+                               (bi_rw == WRITE) == (READ_ONCE(last->last_rw) == WRITE);
                WRITE_ONCE(last->last_sector, end_sector);
                WRITE_ONCE(last->last_rw, bi_rw);
        } else
@@ -842,10 +840,10 @@ static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long
                result = jiffies_to_msecs(j & 0x3fffff);
        if (j >= 1 << 22) {
                mult = jiffies_to_msecs(1 << 22);
-               result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff);
+               result += (unsigned long long)mult * ((j >> 22) & 0x3fffff);
        }
        if (j >= 1ULL << 44)
-               result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44);
+               result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44);
 
        return result;
 }
index b6a2d2081a24b6d2e9cd10239a79fdb643e16c13..e60e1326376a0bdc63d47ad5be5dd76bd729d89d 100644 (file)
@@ -186,6 +186,7 @@ struct dm_pool_metadata {
        uint32_t time;
        dm_block_t root;
        dm_block_t details_root;
+       dm_block_t held_root;
        struct list_head thin_devices;
        uint64_t trans_id;
        unsigned long flags;
@@ -748,6 +749,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd)
         */
        pmd->root = le64_to_cpu(disk_super->data_mapping_root);
        pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+       pmd->held_root = le64_to_cpu(disk_super->held_root);
 
        __setup_btree_details(pmd);
        dm_bm_unlock(sblock);
@@ -838,6 +840,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
        pmd->time = le32_to_cpu(disk_super->time);
        pmd->root = le64_to_cpu(disk_super->data_mapping_root);
        pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+       pmd->held_root = le64_to_cpu(disk_super->held_root);
        pmd->trans_id = le64_to_cpu(disk_super->trans_id);
        pmd->flags = le32_to_cpu(disk_super->flags);
        pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
@@ -928,6 +931,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
        disk_super->time = cpu_to_le32(pmd->time);
        disk_super->data_mapping_root = cpu_to_le64(pmd->root);
        disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+       disk_super->held_root = cpu_to_le64(pmd->held_root);
        disk_super->trans_id = cpu_to_le64(pmd->trans_id);
        disk_super->flags = cpu_to_le32(pmd->flags);
 
@@ -1333,9 +1337,14 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
 {
        int r, inc;
        struct thin_disk_superblock *disk_super;
-       struct dm_block *copy, *sblock;
+       struct dm_block *copy;
        dm_block_t held_root;
 
+       if (pmd->held_root) {
+               DMWARN("Pool metadata snapshot already exists: release this before taking another.");
+               return -EBUSY;
+       }
+
        /*
         * We commit to ensure the btree roots which we increment in a
         * moment are up to date.
@@ -1353,22 +1362,16 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
        dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
        r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION,
                               &sb_validator, &copy, &inc);
-       if (r)
+       if (r) {
+               dm_sm_dec_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
                return r;
+       }
 
        BUG_ON(!inc);
 
        held_root = dm_block_location(copy);
        disk_super = dm_block_data(copy);
 
-       if (le64_to_cpu(disk_super->held_root)) {
-               DMWARN("Pool metadata snapshot already exists: release this before taking another.");
-
-               dm_tm_dec(pmd->tm, held_root);
-               dm_tm_unlock(pmd->tm, copy);
-               return -EBUSY;
-       }
-
        /*
         * Wipe the spacemap since we're not publishing this.
         */
@@ -1384,18 +1387,8 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
        dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root));
        dm_tm_unlock(pmd->tm, copy);
 
-       /*
-        * Write the held root into the superblock.
-        */
-       r = superblock_lock(pmd, &sblock);
-       if (r) {
-               dm_tm_dec(pmd->tm, held_root);
-               return r;
-       }
+       pmd->held_root = held_root;
 
-       disk_super = dm_block_data(sblock);
-       disk_super->held_root = cpu_to_le64(held_root);
-       dm_bm_unlock(sblock);
        return 0;
 }
 
@@ -1415,18 +1408,10 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
 {
        int r;
        struct thin_disk_superblock *disk_super;
-       struct dm_block *sblock, *copy;
+       struct dm_block *copy;
        dm_block_t held_root;
 
-       r = superblock_lock(pmd, &sblock);
-       if (r)
-               return r;
-
-       disk_super = dm_block_data(sblock);
-       held_root = le64_to_cpu(disk_super->held_root);
-       disk_super->held_root = cpu_to_le64(0);
-
-       dm_bm_unlock(sblock);
+       held_root = pmd->held_root;
 
        if (!held_root) {
                DMWARN("No pool metadata snapshot found: nothing to release.");
@@ -1437,13 +1422,15 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
        if (r)
                return r;
 
+       pmd->held_root = 0;
+
        disk_super = dm_block_data(copy);
        dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root));
        dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root));
-       dm_sm_dec_block(pmd->metadata_sm, held_root);
-
        dm_tm_unlock(pmd->tm, copy);
 
+       dm_sm_dec_block(pmd->metadata_sm, held_root);
+
        return 0;
 }
 
@@ -1462,19 +1449,7 @@ int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
 static int __get_metadata_snap(struct dm_pool_metadata *pmd,
                               dm_block_t *result)
 {
-       int r;
-       struct thin_disk_superblock *disk_super;
-       struct dm_block *sblock;
-
-       r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
-                           &sb_validator, &sblock);
-       if (r)
-               return r;
-
-       disk_super = dm_block_data(sblock);
-       *result = le64_to_cpu(disk_super->held_root);
-
-       dm_bm_unlock(sblock);
+       *result = pmd->held_root;
 
        return 0;
 }
index 85ad9dc210ff0f56cc23c229987fcf9b4cef085f..c79f60df3a905cdd97bb3df740df20da69008464 100644 (file)
@@ -220,7 +220,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
                                     PTR_ERR(bbuf));
 
                        /* assume the block is corrupted */
-                       if (neras && *neras <= v->fec->roots)
+                       if (neras && *neras < v->fec->roots)
                                fio->erasures[(*neras)++] = i;
 
                        continue;
@@ -238,7 +238,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
                         * skip if we have already found the theoretical
                         * maximum number (i.e. fec->roots) of erasures
                         */
-                       if (neras && *neras <= v->fec->roots &&
+                       if (neras && *neras < v->fec->roots &&
                            fec_is_erasure(v, io, want_digest, bbuf))
                                fio->erasures[(*neras)++] = i;
                }
index 50b5e187d5cc1b0091f018e0749e96b540b9e1e9..3885b514fc23e72eccf786c23fe17c2383f18384 100644 (file)
@@ -47,7 +47,7 @@ struct dm_verity_fec {
 /* per-bio data */
 struct dm_verity_fec_io {
        struct rs_control *rs;  /* Reed-Solomon state */
-       int erasures[DM_VERITY_FEC_MAX_ROOTS + 1]; /* erasures for decode_rs8 */
+       int erasures[DM_VERITY_FEC_MAX_ROOTS]; /* erasures for decode_rs8 */
        u8 *output;             /* buffer for corrected output */
        unsigned int level;             /* recursion level */
        unsigned int nbufs;             /* number of buffers allocated */
index 0666699b6858166227131e17a0da99e5ae22a4c2..9a64f575ae5f61f2589e41bf46e1d1deedfa7333 100644 (file)
@@ -70,7 +70,7 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
 
        table = dm_get_live_table(md, &srcu_idx);
 
-       if (table->num_targets != 1)
+       if (!table || table->num_targets != 1)
                goto out;
 
        ti = dm_table_get_target(table, 0);
index 9a9847f94c460c3ff810bc9a390b04b381a02740..1b076309125403946f5e1e9bda985e807a06401b 100644 (file)
@@ -26,7 +26,7 @@
 
 #define DM_MSG_PREFIX                  "verity"
 
-#define DM_VERITY_ENV_LENGTH           42
+#define DM_VERITY_ENV_LENGTH           46
 #define DM_VERITY_ENV_VAR_NAME         "DM_VERITY_ERR_BLOCK_NR"
 
 #define DM_VERITY_DEFAULT_PREFETCH_SIZE        262144
@@ -180,14 +180,16 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
        char *envp[] = { verity_env, NULL };
        const char *type_str = "";
        struct mapped_device *md = dm_table_get_md(v->ti->table);
+       int ce;
 
        /* Corruption should be visible in device status in all modes */
        v->hash_failed = true;
 
-       if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
-               goto out;
-
-       v->corrupted_errs++;
+       ce = atomic_read(&v->corrupted_errs);
+       do {
+               if (ce >= DM_VERITY_MAX_CORRUPTED_ERRS)
+                       goto out;
+       } while (!atomic_try_cmpxchg(&v->corrupted_errs, &ce, ce + 1));
 
        switch (type) {
        case DM_VERITY_BLOCK_TYPE_DATA:
@@ -203,7 +205,7 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
        DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
                    type_str, block);
 
-       if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) {
+       if (ce + 1 == DM_VERITY_MAX_CORRUPTED_ERRS) {
                DMERR("%s: reached maximum errors", v->data_dev->name);
                dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0);
        }
@@ -1262,6 +1264,8 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
                        continue;
 
                } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) {
+                       if (v->use_bh_wq)
+                               continue;
                        v->use_bh_wq = true;
                        static_branch_inc(&use_bh_wq_enabled);
                        continue;
index 2922263501f68d6ef02f509ccd4a0bcf4e13663e..e104a651c6574cf6e478491c3e515e228172a7ab 100644 (file)
@@ -68,7 +68,7 @@ struct dm_verity {
        unsigned int digest_size;       /* digest size for the current hash algorithm */
        enum verity_mode mode;  /* mode for handling verification errors */
        enum verity_mode error_mode;/* mode for handling I/O errors */
-       unsigned int corrupted_errs;/* Number of errors for corrupted blocks */
+       atomic_t corrupted_errs;/* Number of errors for corrupted blocks */
 
        struct workqueue_struct *verify_wq;
 
index 7287bed6eb642171f0262d4406728382909bfbd9..d413bfaf352782ae631e127e1538bdd51aec976c 100644 (file)
@@ -735,7 +735,16 @@ static struct table_device *open_table_device(struct mapped_device *md,
                return ERR_PTR(-ENOMEM);
        refcount_set(&td->count, 1);
 
-       bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL);
+       /*
+        * Open the backing device with kernel rather than caller
+        * credentials. Otherwise the caller's credentials would be
+        * pinned in bdev_file->f_cred until the table device is closed.
+        * That would keep the caller's thread keyring alive long beyond the
+        * lifetime of the caller, breaking userspace expectation (e.g.
+        * cryptsetup(8) leaking the LUKS volume key).
+        */
+       scoped_with_kernel_creds()
+               bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL);
        if (IS_ERR(bdev_file)) {
                r = PTR_ERR(bdev_file);
                goto out_free_td;
index a01fe313558e732aeeb11b35dca592ad84b02bb5..ce33907bfc2429b09ccd3fbe5a69507560c5b2dd 100644 (file)
@@ -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);
index 0274e8d07660dd0e3af237da6ba91480d2c0a9da..54a923ba4f1e7d3bf0f3e303801c1f5639350b8d 100644 (file)
@@ -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);
 }
index ab38e4c45a8da7831faf6884740d942fe1a8a1cb..4dc16649e61d425d66fde6093296c00cf0ee6b7d 100644 (file)
@@ -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;
 }
index 18ecddd6df6f22f5d8e5c645ca2d192c4586b0a3..18f4905c15b9b4ec9260a88f026552daf38ebff1 100644 (file)
@@ -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)
index eef53455b8ee49310887274f4dc35c790ce896a8..c688f3eaf4686f5facd4170816f7a270fdddefb9 100644 (file)
@@ -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;
 }
index 6c3cb2f1c9d32bbb3daf16bb55767aaef28b9160..2dae474dcd067993aef16669fe9138821a551492 100644 (file)
@@ -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);
        /*
index cef5f9677d39eba98a37c0ab1fb13ce62eef367e..66e058df4c328b5f1c51bb5472e32876cdd4fd1c 100644 (file)
@@ -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;
 
index dce5e67ce3c2b68ef5a3f035b21243c9402791c0..f447902d707e147dce81ee69e690c22fbba2f4a9 100644 (file)
@@ -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
index fbed278157f68d9c3864556120fe5a6fcddc564a..01745eca1f7303eede03d4859767aeb2a2918927 100644 (file)
@@ -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 (file)
index 0000000..de4c463
--- /dev/null
@@ -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);
+
+/****************************************************************************/
index 37075ead0f3377b601c616400496f26f0521917d..da4277ced4d63b0a2e387dfa5e826eb946c18da3 100644 (file)
@@ -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) {
index 5765377746288782d6ea5e19b06bdabfad91f2c0..16629382a787bd5882102ce4373fa93259c73690 100644 (file)
@@ -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);
index 795a94e6b482fd076ed71f9fabfe8c73be84f556..4b41550fd374e4ef0524b5ffd2c78308638794a1 100644 (file)
@@ -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;
index 866933fc84265dafb1a0031e81ea060a5767eb8e..f33f753f0a9fda38cf52668052cb1c3060fcea8c 100644 (file)
@@ -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);
index c75bb8b80cc1e14b6de7ed7c6935490518930f63..66f0985ef7cdb6c82ccc2de138f12b26ab474f26 100644 (file)
@@ -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;
index 6d6aa709a21f8e5b73d1d2cde07ae08135d267dc..b7b7758ce4d860cd72af3f3e4aab445d1fd023ce 100644 (file)
@@ -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;
 }
index 64b8b99a3a682655fb24c6bd8480944ee42c6122..1f4053e531fd88e2c25df663e82b122e0dc09bab 100644 (file)
@@ -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
index fad0334f759dd5f81113e10ec04e8bbaf490bc83..a88ac2cfaccdcc4b36f5950e2c466637fd2692c7 100644 (file)
@@ -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;
 }
index 525c34c281b65df0aba78168b3eb13260b14b4fb..beb033705cf3e7ad0faefa1f79d4fd19ed843fd4 100644 (file)
@@ -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;
 }
index 19b13ae536d48ae0f133941084a01449eea80558..8f6a89d9ba83fecf37ac07d32bcc66c7f04ff17c 100644 (file)
@@ -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,
index 3ca30e7dce33694e7d9acd4a1b1134172d21886b..10c8080207f4959a7e40fb52696d77e8e324f3be 100644 (file)
@@ -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);
index a937ca3eeff5657656dfd4b4e55d2606bc3f0ae2..a48274297d3b12c64e318a7db4cc38eaeff15ac9 100644 (file)
@@ -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);
index f86786344d52b0fa8914d348c4a23a7e8dc38e29..74bb5ee83b31442deafc0ea58522ca79f141810c 100644 (file)
@@ -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;
 
index 724a8163a5142a6835950abb63d80f29417b2654..951dd10e192b7924f9d3f05065a298ddcf8f4b25 100644 (file)
@@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev)
               nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */
               nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */
               nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */
-              nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */
-              nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */
-              nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */
+              nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */
+              nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */
+              nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */
 }
 
 static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev)
index e4058708ae6833d48ea0626b0f9b00372f91c1ff..a8fad6fe530207ca7e97124c75c1c24c5169ed80 100644 (file)
@@ -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.
index 06cb2629f66abcf744fc014f5fbe5d55ea963898..4fd1aefb780fdbc881514adc03324e6b061fbd83 100644 (file)
@@ -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>
index 60c6542028cf44e88f1ec2d037615a67a2b0b2cc..dc0ecb566a85a3ba6b5bb02da51c1a6fed3aa937 100644 (file)
@@ -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
index 93558e33bc02bb38597b0791d5a5a316462ef578..7c749301ea84ccfd08675161e1bd09e7a7fff6ba 100644 (file)
@@ -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");
 
index 4cc4a1581dd1157d1998dc1b557a7e9e9f6345ce..69c61ccf621df849929647f2c4c451334c2ba17e 100644 (file)
@@ -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");
 
index 42a77d435b3920f76a27915ed5b28e914717e1ee..c3c2aa21da47b20548ad964c5a5d86c23f037d90 100644 (file)
@@ -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");
 
index d257440fa01ff1886776481fdca05cfc8476e5f9..f41d4a0d140f7e410ff6c7772fd8eebb5ee1d756 100644 (file)
@@ -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);
        }
 }
index 9278a1522aae5e458eb0987c7d94d81d2bf5158a..8fd058c328564eb9b263eb85b29f314277078fb0 100644 (file)
@@ -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>
  */
index cf48bb26d46d233a6479df7cd619ea4d59102923..c7933d1acc99a9949e43e8c70d02457df4179d6d 100644 (file)
@@ -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");
 
index d1c1897d47b9c6bb394646798f9d6fc8ce1204c4..65999f04f4b76e39d51cb04fb24f511dc4bbe6ed 100644 (file)
@@ -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>
  */
index eb4f5884ad73585fbeea6642dd05d14e9a7cdcc1..ef9fd693e9bd38a444bc05da3f49dbf2443e50e7 100644 (file)
@@ -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>
index 4bfa8d0fbb32fd365dc038835a1783e908ac4ea2..aefcded8e12a8fb3cce0c6547c7116ed3fdc68e8 100644 (file)
@@ -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>
index 162c7546d3a8332707c94d7637093624372e12f9..d669c9e610c7e84c18f249d99623368bacfa21e8 100644 (file)
@@ -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
index 5c751933da6a9d9852f08cd54a50f1bf98e5d56f..a515c368bac0153e1c33298e5a0f025a11dd70e1 100644 (file)
@@ -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;
index fd282a1700fb98413a0d315a86a8e9d7caed76f4..d394f1f43b685eee64f99eae2f2ac5b2949c1274 100644 (file)
@@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp)
        dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma);
 
        for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
-               kfree(queue->tx_skb);
-               queue->tx_skb = NULL;
+               if (queue->tx_skb) {
+                       unsigned int dropped = 0, tail;
+
+                       for (tail = queue->tx_tail; tail != queue->tx_head;
+                            tail++) {
+                               if (macb_tx_skb(queue, tail)->skb)
+                                       dropped++;
+                               macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
+                       }
+
+                       queue->stats.tx_dropped += dropped;
+                       bp->dev->stats.tx_dropped += dropped;
+
+                       kfree(queue->tx_skb);
+                       queue->tx_skb = NULL;
+               }
+
+               queue->tx_head = 0;
+               queue->tx_tail = 0;
                queue->tx_ring = NULL;
                queue->rx_ring = NULL;
        }
index 0db08ac3d098fbe093f4fabbf69df71a8c16c850..e303956b4bf12a948412a4b1e6c7f37383ae9ab2 100644 (file)
@@ -3779,9 +3779,7 @@ setup_nic_dev_done:
 static int octeon_enable_sriov(struct octeon_device *oct)
 {
        unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced;
-       struct pci_dev *vfdev;
        int err;
-       u32 u;
 
        if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) {
                err = pci_enable_sriov(oct->pci_dev,
@@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
                        return err;
                }
                oct->sriov_info.sriov_enabled = 1;
-
-               /* init lookup table that maps DPI ring number to VF pci_dev
-                * struct pointer
-                */
-               u = 0;
-               vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
-                                      OCTEON_CN23XX_VF_VID, NULL);
-               while (vfdev) {
-                       if (vfdev->is_virtfn &&
-                           (vfdev->physfn == oct->pci_dev)) {
-                               oct->sriov_info.dpiring_to_vfpcidev_lut[u] =
-                                       vfdev;
-                               u += oct->sriov_info.rings_per_vf;
-                       }
-                       vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
-                                              OCTEON_CN23XX_VF_VID, vfdev);
-               }
        }
 
        return num_vfs_alloced;
@@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
 
 static int lio_pci_sriov_disable(struct octeon_device *oct)
 {
-       int u;
-
        if (pci_vfs_assigned(oct->pci_dev)) {
                dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n");
                return -EPERM;
@@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct)
 
        pci_disable_sriov(oct->pci_dev);
 
-       u = 0;
-       while (u < MAX_POSSIBLE_VFS) {
-               oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL;
-               u += oct->sriov_info.rings_per_vf;
-       }
-
        oct->sriov_info.num_vfs_alloced = 0;
        dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n",
                 oct->pf_num);
index 19344b21f8fb941f04c71f79151e4c9f8a428298..858a0fff2cc0b6a84cee35437171d43af4f4d0cb 100644 (file)
@@ -390,9 +390,6 @@ struct octeon_sriov_info {
 
        struct lio_trusted_vf   trusted_vf;
 
-       /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/
-       struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS];
-
        u64     vf_macaddr[MAX_POSSIBLE_VFS];
 
        u16     vf_vlantci[MAX_POSSIBLE_VFS];
index ad685f5d0a1363741e1cf6ff02c9012d62685715..697fcdc41e3cd7f035953cc2fde009561194afa9 100644 (file)
 #include "octeon_mailbox.h"
 #include "cn23xx_pf_device.h"
 
+static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no)
+{
+       struct pci_dev *vfdev = NULL;
+       int vfidx;
+
+       if (!oct->sriov_info.rings_per_vf)
+               return NULL;
+
+       if (q_no % oct->sriov_info.rings_per_vf)
+               return NULL;
+
+       vfidx = q_no / oct->sriov_info.rings_per_vf;
+       if (vfidx >= oct->sriov_info.num_vfs_alloced)
+               return NULL;
+
+       while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+                                      OCTEON_CN23XX_VF_VID, vfdev))) {
+               if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev &&
+                   pci_iov_vf_id(vfdev) == vfidx)
+                       return vfdev;
+       }
+
+       return NULL;
+}
+
 /**
  * octeon_mbox_read:
  * @mbox: Pointer mailbox
@@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
                                   struct octeon_mbox_cmd *mbox_cmd)
 {
        struct octeon_device *oct = mbox->oct_dev;
+       struct pci_dev *vfdev;
 
        switch (mbox_cmd->msg.s.cmd) {
        case OCTEON_VF_ACTIVE:
@@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
                dev_info(&oct->pci_dev->dev,
                         "got a request for FLR from VF that owns DPI ring %u\n",
                         mbox->q_no);
-               pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]);
+               vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no);
+               if (!vfdev)
+                       break;
+
+               pcie_flr(vfdev);
+               pci_dev_put(vfdev);
                break;
 
        case OCTEON_PF_CHANGED_VF_MACADDR:
index 0297c7ab0614eacb17aabaedf1946ef1e3ff4f50..6a0ce2665031d6a630551665b502b17217e88d55 100644 (file)
@@ -4580,7 +4580,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf,
                                               pfvf->rx_chan_base);
        } else {
-               if (!nix_rx_multicast)
+               if (!nix_rx_multicast && !is_vf(pcifunc))
                        rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false);
        }
 
@@ -4590,7 +4590,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                                              pfvf->rx_chan_base,
                                              pfvf->rx_chan_cnt);
        else
-               if (!nix_rx_multicast)
+               if (!nix_rx_multicast && !is_vf(pcifunc))
                        rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false);
 
        return 0;
index b63df5737ff2316abd289f6fc1d4471144857550..2e33b33ec9934196d9a9d8663155401359771d77 100644 (file)
@@ -1568,15 +1568,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf)
        otx2_sq_free_sqbs(pf);
        for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) {
                sq = &qset->sq[qidx];
-               /* Skip freeing Qos queues if they are not initialized */
-               if (!sq->sqe)
-                       continue;
-               qmem_free(pf->dev, sq->sqe);
-               qmem_free(pf->dev, sq->sqe_ring);
-               qmem_free(pf->dev, sq->cpt_resp);
-               qmem_free(pf->dev, sq->tso_hdrs);
-               qmem_free(pf->dev, sq->timestamps);
-               kfree(sq->sg);
+               /* sq->sqe is not initialized for unused QoS queues */
+               if (sq->sqe) {
+                       qmem_free(pf->dev, sq->sqe);
+                       qmem_free(pf->dev, sq->sqe_ring);
+                       qmem_free(pf->dev, sq->cpt_resp);
+                       qmem_free(pf->dev, sq->tso_hdrs);
+                       qmem_free(pf->dev, sq->timestamps);
+                       kfree(sq->sg);
+               }
                kfree(sq->sqb_ptrs);
        }
 }
@@ -1711,13 +1711,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf)
        return err;
 
 err_free_nix_queues:
-       otx2_free_sq_res(pf);
        otx2_free_cq_res(pf);
        otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false);
 err_free_txsch:
        otx2_txschq_stop(pf);
 err_free_sq_ptrs:
-       otx2_sq_free_sqbs(pf);
+       otx2_free_sq_res(pf);
 err_free_rq_ptrs:
        otx2_free_aura_ptr(pf, AURA_NIX_RQ);
        otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true);
@@ -2517,10 +2516,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set);
 
 static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac)
 {
+       struct npc_get_field_status_req *freq;
+       struct npc_get_field_status_rsp *frsp;
        struct npc_install_flow_req *req;
        int err;
 
        mutex_lock(&pf->mbox.lock);
+
+       /* Skip installing the DMAC filter if the hardware parser profile
+        * does not support DMAC extraction.
+        */
+       freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox);
+       if (!freq) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       freq->field = NPC_DMAC;
+       err = otx2_sync_mbox_msg(&pf->mbox);
+       if (err)
+               goto out;
+
+       frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp
+              (&pf->mbox.mbox, 0, &freq->hdr);
+       if (IS_ERR(frsp)) {
+               err = PTR_ERR(frsp);
+               goto out;
+       }
+
+       if (!frsp->enable) {
+               netdev_warn(pf->netdev,
+                           "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n",
+                           vf);
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
        req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox);
        if (!req) {
                err = -ENOMEM;
@@ -2559,13 +2590,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
        if (!is_valid_ether_addr(mac))
                return -EINVAL;
 
-       config = &pf->vf_configs[vf];
-       ether_addr_copy(config->mac, mac);
-
        ret = otx2_do_set_vf_mac(pf, vf, mac);
-       if (ret == 0)
-               dev_info(&pdev->dev,
-                        "Load/Reload VF driver\n");
+       if (ret == 0) {
+               config = &pf->vf_configs[vf];
+               ether_addr_copy(config->mac, mac);
+               dev_info(&pdev->dev, "Load/Reload VF driver\n");
+       }
 
        return ret;
 }
index 2270e2e550dd9ee46ae4d49a10d62b3e7e0e1b83..d507289096c2067a65119b5f818c91eee874e86e 100644 (file)
@@ -987,6 +987,18 @@ struct mlx5e_priv {
        struct ethtool_fec_hist_range *fec_ranges;
 };
 
+static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
+{
+       /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */
+       return smp_load_acquire(&priv->stats_nch);
+}
+
+static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n)
+{
+       /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */
+       smp_store_release(&priv->stats_nch, n);
+}
+
 struct mlx5e_dev {
        struct net_device *netdev;
        struct devlink_port dl_port;
index 195863b2c013e2b7e0093dceb9a0e4ea1943b1a3..631f802105d5bf42031c4802ca0dc43fbf269fa5 100644 (file)
@@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
 static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
                                     int buf_len)
 {
+       u16 nch = mlx5e_stats_nch_read(priv);
        int ch, i = 0;
 
-       for (ch = 0; ch < priv->stats_nch; ch++) {
+       for (ch = 0; ch < nch; ch++) {
                void *buf = data + i;
 
                if (WARN_ON_ONCE(buf +
@@ -49,9 +50,16 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
 }
 
 static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
+{
+       u16 nch = mlx5e_stats_nch_read(priv);
+
+       return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch;
+}
+
+static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
 {
        return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
-               priv->stats_nch);
+               max(priv->max_nch, priv->stats_nch));
 }
 
 static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
@@ -67,7 +75,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
        sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
        priv = container_of(sagent, struct mlx5e_priv, stats_agent);
        buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
-       agent = sagent->agent;
+       agent = READ_ONCE(sagent->agent);
        buf = sagent->buf;
 
        memset(buf, 0, buf_len);
@@ -100,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
        sagent = &priv->stats_agent;
 
        block->version = MLX5_HV_VHCA_STATS_VERSION;
-       block->rings   = priv->stats_nch;
+       block->rings   = mlx5e_stats_nch_read(priv);
 
        if (!block->command) {
                cancel_delayed_work_sync(&priv->stats_agent.work);
@@ -122,18 +130,21 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
 
 void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
 {
-       int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+       int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv);
        struct mlx5_hv_vhca_agent *agent;
 
        priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
        if (!priv->stats_agent.buf)
                return;
 
+       INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
+
        agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
                                          MLX5_HV_VHCA_AGENT_STATS,
                                          mlx5e_hv_vhca_stats_control, NULL,
                                          mlx5e_hv_vhca_stats_cleanup,
-                                         priv);
+                                         priv,
+                                         &priv->stats_agent.agent);
 
        if (IS_ERR_OR_NULL(agent)) {
                if (IS_ERR(agent))
@@ -142,18 +153,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
                                    agent);
 
                kvfree(priv->stats_agent.buf);
-               return;
+               priv->stats_agent.buf = NULL;
        }
-
-       priv->stats_agent.agent = agent;
-       INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
 }
 
 void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
 {
-       if (IS_ERR_OR_NULL(priv->stats_agent.agent))
+       struct mlx5_hv_vhca_agent *agent;
+
+       agent = READ_ONCE(priv->stats_agent.agent);
+       if (IS_ERR_OR_NULL(agent))
                return;
 
-       mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
+       mlx5_hv_vhca_agent_destroy(agent);
+       WRITE_ONCE(priv->stats_agent.agent, NULL);
        kvfree(priv->stats_agent.buf);
+       priv->stats_agent.buf = NULL;
 }
index 775f0c6e55c955ebfa6adccee20d821f57c18abd..aa8610cedaa85b9cef8e12b252904c22b4501465 100644 (file)
@@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu)
                                                GFP_KERNEL, cpu_to_node(cpu));
        if (!priv->channel_stats[ix])
                return -ENOMEM;
-       priv->stats_nch++;
+       mlx5e_stats_nch_write(priv, priv->stats_nch + 1);
 
        return 0;
 }
@@ -4040,9 +4040,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
 
 void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
 {
+       u16 nch = mlx5e_stats_nch_read(priv);
        int i;
 
-       for (i = 0; i < priv->stats_nch; i++) {
+       for (i = 0; i < nch; i++) {
                struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
                struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq;
                struct mlx5e_rq_stats *rq_stats = &channel_stats->rq;
@@ -5488,7 +5489,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i,
        struct mlx5e_rq_stats *xskrq_stats;
        struct mlx5e_rq_stats *rq_stats;
 
-       if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch)
+       if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv))
                return;
 
        channel_stats = priv->channel_stats[i];
@@ -5512,7 +5513,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i,
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5e_sq_stats *sq_stats;
 
-       if (!priv->stats_nch)
+       if (!mlx5e_stats_nch_read(priv))
                return;
 
        /* no special case needed for ptp htb etc since txq2sq_stats is kept up
@@ -5538,6 +5539,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
                                 struct netdev_queue_stats_tx *tx)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       u16 nch = mlx5e_stats_nch_read(priv);
        struct mlx5e_ptp *ptp_channel;
        int i, tc;
 
@@ -5549,7 +5551,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
                rx->hw_gro_wire_packets = 0;
                rx->hw_gro_wire_bytes = 0;
 
-               for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) {
+               for (i = priv->channels.params.num_channels; i < nch; i++) {
                        struct netdev_queue_stats_rx rx_i = {0};
 
                        mlx5e_get_queue_stats_rx(dev, i, &rx_i);
@@ -5585,7 +5587,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
        tx->stop = 0;
        tx->wake = 0;
 
-       for (i = 0; i < priv->stats_nch; i++) {
+       for (i = 0; i < nch; i++) {
                struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
 
                /* handle two cases:
index 7f33261ba6553bb70d908c1b96b1eef4055d6a26..de38b60806c264fa35040c3d7141f60212fd2100 100644 (file)
@@ -515,6 +515,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c)
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
 {
        struct mlx5e_sw_stats *s = &priv->stats.sw;
+       u16 nch = mlx5e_stats_nch_read(priv);
        int i;
 
        memset(s, 0, sizeof(*s));
@@ -522,7 +523,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
        for (i = 0; i < priv->channels.num; i++) /* for active channels only */
                mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]);
 
-       for (i = 0; i < priv->stats_nch; i++) {
+       for (i = 0; i < nch; i++) {
                struct mlx5e_channel_stats *channel_stats =
                        priv->channel_stats[i];
 
@@ -2614,7 +2615,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; }
 
 static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
 {
-       int max_nch = priv->stats_nch;
+       int max_nch = mlx5e_stats_nch_read(priv);
 
        return (NUM_RQ_STATS * max_nch) +
               (NUM_CH_STATS * max_nch) +
@@ -2627,8 +2628,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
 
 static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
 {
+       int max_nch = mlx5e_stats_nch_read(priv);
        bool is_xsk = priv->xsk.ever_used;
-       int max_nch = priv->stats_nch;
        int i, j, tc;
 
        for (i = 0; i < max_nch; i++)
@@ -2660,8 +2661,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
 
 static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels)
 {
+       int max_nch = mlx5e_stats_nch_read(priv);
        bool is_xsk = priv->xsk.ever_used;
-       int max_nch = priv->stats_nch;
        int i, j, tc;
 
        for (i = 0; i < max_nch; i++)
index 910492eb51f276644ba6ce153f308f1e691f90d6..1bc7b9019124a449ee68041fa29733287de94569 100644 (file)
@@ -5547,6 +5547,9 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
 
        mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
                i = mlx5_lag_get_dev_seq(peer_esw->dev);
+               if (i < 0)
+                       continue;
+
                list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
                        mlx5e_tc_del_fdb_peers_flow(flow);
        }
index 0a6003fe60e9f8443b08cf758dde9374e14d9bcc..674bed721e63755f404cfd59ff64195301dae82c 100644 (file)
@@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv)
 
 static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
 {
+       u16 nch = mlx5e_stats_nch_read(priv);
        struct rtnl_link_stats64 s = {};
        int i, j;
 
-       for (i = 0; i < priv->stats_nch; i++) {
+       for (i = 0; i < nch; i++) {
                struct mlx5e_channel_stats *channel_stats;
                struct mlx5e_rq_stats *rq_stats;
 
index 50bfb450c71e2f80fe5af39c1246aca44d580354..abf72026c7513305b696e5725208821ed6460e7e 100644 (file)
@@ -194,8 +194,10 @@ static void mlx5_mpesw_work(struct work_struct *work)
        struct mlx5_lag *ldev = mpesww->lag;
 
        devcom = mlx5_lag_get_devcom_comp(ldev);
-       if (!devcom)
-               return;
+       if (!devcom) {
+               mpesww->result = -ENODEV;
+               goto complete;
+       }
 
        mlx5_devcom_comp_lock(devcom);
        mlx5_mpesw_sd_devcoms_lock(ldev);
@@ -213,6 +215,7 @@ unlock:
        mutex_unlock(&ldev->lock);
        mlx5_mpesw_sd_devcoms_unlock(ldev);
        mlx5_devcom_comp_unlock(devcom);
+complete:
        complete(&mpesww->comp);
 }
 
index 113866494d164bee26b87668700ceac1cfef59e7..6b4ad3c53f2f33bd8c60dbe0344f08fc86e4b737 100644 (file)
@@ -78,7 +78,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
        }
        return 0;
 err:
-       mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) {
+       mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) {
                struct mlx5_eswitch *slave_esw;
 
                if (j == master_idx)
index d6dc7bce855e0bebbb8a4cf195230ae097fe7808..305752dab7bdf4fc027ff784065a309092611314 100644 (file)
@@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
        return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
                                         NULL,
                                         mlx5_hv_vhca_control_agent_invalidate,
-                                        NULL, NULL);
+                                        NULL, NULL, NULL);
 }
 
 static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
@@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
                          void (*invalidate)(struct mlx5_hv_vhca_agent*,
                                             u64 block_mask),
                          void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
-                         void *priv)
+                         void *priv,
+                         struct mlx5_hv_vhca_agent **ctx_update)
 {
        struct mlx5_hv_vhca_agent *agent;
 
@@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
        agent->invalidate = invalidate;
        agent->cleanup   = cleaup;
 
+       if (ctx_update)
+               WRITE_ONCE(*ctx_update, agent);
+
        mutex_lock(&hv_vhca->agents_lock);
        hv_vhca->agents[type] = agent;
        mutex_unlock(&hv_vhca->agents_lock);
index f240ffe5116c5b86dd83c5013e01d3cd52ba4962..8b3974cf0ee49afbebba989aba6276b029930279 100644 (file)
@@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
                          void (*invalidate)(struct mlx5_hv_vhca_agent*,
                                             u64 block_mask),
                          void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
-                         void *context);
+                         void *context,
+                         struct mlx5_hv_vhca_agent **ctx_update);
 
 void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent);
 int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
@@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
                          void (*invalidate)(struct mlx5_hv_vhca_agent*,
                                             u64 block_mask),
                          void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
-                         void *context)
+                         void *context,
+                         struct mlx5_hv_vhca_agent **ctx_update)
 {
        return NULL;
 }
index 4571c56ec3c9b29683f16cc336d1872fa2cfc108..97f6097d4c705fc5c2c3c38c38acb5c070e7b0d0 100644 (file)
@@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy,
                                   int reformat_type)
 {
        mutex_lock(&tun_entropy->lock);
-       if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN)
+       if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN ||
+           reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL)
                tun_entropy->num_enabling_entries--;
        else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE &&
                 --tun_entropy->num_disabling_entries == 0)
index 997be91f0a135a146f221a096773041aa301c7c8..7cedc348790dba072f0a0394eb184203fe0792ff 100644 (file)
@@ -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 */
        }
 
index 72e3b189bac5ac737fcf2f86346edc221de40dac..eb28df80b2818ef22d4fe136848fb9e981397741 100644 (file)
@@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin)
        kfree(admin->cache.keystream);
        kfree(admin->cache.maskstream);
        kfree(admin->cache.actionstream);
-       mutex_destroy(&admin->lock);
        kfree(admin);
 }
 
@@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,
        if (!admin)
                return ERR_PTR(-ENOMEM);
 
-       mutex_init(&admin->lock);
+       admin->vctrl = ctrl;
        INIT_LIST_HEAD(&admin->list);
        INIT_LIST_HEAD(&admin->rules);
        INIT_LIST_HEAD(&admin->enabled);
@@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x)
        ctrl->ops = &lan966x_vcap_ops;
 
        INIT_LIST_HEAD(&ctrl->list);
+       mutex_init(&ctrl->lock);
        for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) {
                cfg = &lan966x_vcap_inst_cfg[i];
 
@@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x)
                lan966x_vcap_admin_free(admin);
        }
 
+       mutex_destroy(&ctrl->lock);
        kfree(ctrl);
 }
index 95b93e46a41dce3f3b1e49535dfab0c346b62d1c..cf332de6bf736cb93bdc1742017ead41fd816898 100644 (file)
@@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin)
 {
        if (!admin)
                return;
-       mutex_destroy(&admin->lock);
        kfree(admin->cache.keystream);
        kfree(admin->cache.maskstream);
        kfree(admin->cache.actionstream);
@@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
        INIT_LIST_HEAD(&admin->list);
        INIT_LIST_HEAD(&admin->rules);
        INIT_LIST_HEAD(&admin->enabled);
-       mutex_init(&admin->lock);
+       admin->vctrl = ctrl;
        admin->vtype = cfg->vtype;
        admin->vinst = cfg->vinst;
        admin->ingress = cfg->ingress;
@@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5)
        ctrl->ops = &sparx5_vcap_ops;
 
        INIT_LIST_HEAD(&ctrl->list);
+       mutex_init(&ctrl->lock);
        for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) {
                cfg = &consts->vcaps_cfg[idx];
                admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg);
@@ -2097,5 +2097,6 @@ void sparx5_vcap_deinit(struct sparx5 *sparx5)
                list_del(&admin->list);
                sparx5_vcap_admin_free(admin);
        }
+       mutex_destroy(&ctrl->lock);
        kfree(ctrl);
 }
index 0fdb5e363badc0e134e404bcabc1bd7847ac651f..ff86cde11a32c7513613d8579a9816f430274292 100644 (file)
@@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id)
        return false;
 }
 
+void vcap_lock(struct vcap_admin *admin)
+{
+       mutex_lock(&admin->vctrl->lock);
+}
+
+void vcap_unlock(struct vcap_admin *admin)
+{
+       mutex_unlock(&admin->vctrl->lock);
+}
+
 /* Find a rule with a provided rule id return a locked vcap */
 static struct vcap_rule_internal *
 vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
@@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
 
        /* Look for the rule id in all vcaps */
        list_for_each_entry(admin, &vctrl->list, list) {
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                list_for_each_entry(ri, &admin->rules, list)
                        if (ri->data.id == id)
                                return ri;
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
        }
        return NULL;
 }
@@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie)
 
        /* Look for the rule id in all vcaps */
        list_for_each_entry(admin, &vctrl->list, list) {
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                list_for_each_entry(ri, &admin->rules, list) {
                        if (ri->data.cookie == cookie) {
                                id = ri->data.id;
                                break;
                        }
                }
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
                if (id)
                        return id;
        }
@@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid)
        int count = 0;
 
        list_for_each_entry(elem, &admin->rules, list) {
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                if (elem->data.vcap_chain_id >= min_cid &&
                    elem->data.vcap_chain_id < max_cid)
                        ++count;
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
        }
        return count;
 }
@@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule)
        if (ret)
                return ret;
        /* Insert the new rule in the list of vcap rules */
-       mutex_lock(&ri->admin->lock);
+       vcap_lock(ri->admin);
 
        vcap_rule_set_state(ri);
        ret = vcap_insert_rule(ri, &move);
@@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule)
                goto out;
        }
 out:
-       mutex_unlock(&ri->admin->lock);
+       vcap_unlock(ri->admin);
        return ret;
 }
 EXPORT_SYMBOL_GPL(vcap_add_rule);
@@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
        if (vctrl->vcaps[admin->vtype].rows == 0)
                return ERR_PTR(-EINVAL);
 
-       mutex_lock(&admin->lock);
+       vcap_lock(admin);
        /* Check if a rule with this id already exists */
        if (vcap_rule_exists(vctrl, id)) {
                err = -EINVAL;
@@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
                goto out_free;
        }
 
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
        return (struct vcap_rule *)ri;
 
 out_free:
        kfree(ri);
 out_unlock:
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
        return ERR_PTR(err);
 
 }
@@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id)
                return ERR_PTR(-ENOENT);
 
        rule = vcap_decode_rule(elem);
-       mutex_unlock(&elem->admin->lock);
+       vcap_unlock(elem->admin);
        return rule;
 }
 EXPORT_SYMBOL_GPL(vcap_get_rule);
@@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule)
        err =  vcap_write_counter(ri, &ctr);
 
 out:
-       mutex_unlock(&ri->admin->lock);
+       vcap_unlock(ri->admin);
        return err;
 }
 EXPORT_SYMBOL_GPL(vcap_mod_rule);
@@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
                admin->last_used_addr = elem->addr;
        }
 
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
        return err;
 }
 EXPORT_SYMBOL_GPL(vcap_del_rule);
@@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
        if (ret)
                return ret;
 
-       mutex_lock(&admin->lock);
+       vcap_lock(admin);
        list_for_each_entry_safe(ri, next_ri, &admin->rules, list) {
                vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size);
                list_del(&ri->list);
@@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
                list_del(&eport->list);
                kfree(eport);
        }
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
 
        return 0;
 }
@@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
                        continue;
 
                /* Found the admin, now find the offloadable rules */
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                list_for_each_entry(ri, &admin->rules, list) {
                        /* Is the rule in the lookup defined by the chain */
                        if (!(ri->data.vcap_chain_id >= chain &&
@@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
                        if (err)
                                break;
                }
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
                if (err)
                        break;
        }
@@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
                        continue;
 
                /* Found the admin, now find the rules on the chain */
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                list_for_each_entry(ri, &admin->rules, list) {
                        if (ri->data.vcap_chain_id != chain)
                                continue;
@@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
                        if (err)
                                break;
                }
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
                if (err)
                        break;
        }
@@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev,
        eport->cookie = cookie;
        eport->src_cid = src_cid;
        eport->dst_cid = dst_cid;
-       mutex_lock(&admin->lock);
+       vcap_lock(admin);
        list_add_tail(&eport->list, &admin->enabled);
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
 
        if (vcap_path_exist(vctrl, ndev, src_cid)) {
                /* Enable chained lookups */
@@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev,
                dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid);
        }
 
-       mutex_lock(&found->lock);
+       vcap_lock(found);
        list_del(&eport->list);
-       mutex_unlock(&found->lock);
+       vcap_unlock(found);
        kfree(eport);
        return 0;
 }
@@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
                return -EINVAL;
        }
 
-       mutex_lock(&ri->admin->lock);
+       vcap_lock(ri->admin);
        err = vcap_write_counter(ri, ctr);
-       mutex_unlock(&ri->admin->lock);
+       vcap_unlock(ri->admin);
 
        return err;
 }
@@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
                return -EINVAL;
        }
 
-       mutex_lock(&ri->admin->lock);
+       vcap_lock(ri->admin);
        err = vcap_read_counter(ri, ctr);
-       mutex_unlock(&ri->admin->lock);
+       vcap_unlock(ri->admin);
 
        return err;
 }
@@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
 
        /* Iterate all rules in each VCAP instance */
        list_for_each_entry(admin, &vctrl->list, list) {
-               mutex_lock(&admin->lock);
+               vcap_lock(admin);
                list_for_each_entry(ri, &admin->rules, list) {
                        if (ri->data.cookie != cookie)
                                continue;
@@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
                        if (err)
                                goto unlock;
                }
-               mutex_unlock(&admin->lock);
+               vcap_unlock(admin);
        }
        return err;
 
 unlock:
-       mutex_unlock(&admin->lock);
+       vcap_unlock(admin);
        return err;
 }
 EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie);
index 6069ad95c27e93b60ae406da9834dba4767ac419..05b4b02e59ef5fd616c05ddfbd7d6e808ea1560c 100644 (file)
@@ -164,7 +164,7 @@ struct vcap_admin {
        struct list_head list; /* for insertion in vcap_control */
        struct list_head rules; /* list of rules */
        struct list_head enabled; /* list of enabled ports */
-       struct mutex lock; /* control access to rules */
+       struct vcap_control *vctrl; /* the control instance owning this vcap */
        enum vcap_type vtype;  /* type of vcap */
        int vinst; /* instance number within the same type */
        int first_cid; /* first chain id in this vcap */
@@ -275,6 +275,7 @@ struct vcap_control {
        const struct vcap_info *vcaps; /* client supplied vcap models */
        const struct vcap_statistics *stats; /* client supplied vcap stats */
        struct list_head list; /* list of vcap instances */
+       struct mutex lock; /* serialize access to all vcap instances */
 };
 
 #endif /* __VCAP_API__ */
index 59bfbda29bb3dab1f45098134ffc5ee662c01ce0..e0c65c7ab23e1e18089006d2c6093320006570de 100644 (file)
@@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused)
        };
        int ret;
 
-       mutex_lock(&info->admin->lock);
+       vcap_lock(info->admin);
        ret = vcap_show_admin(info->vctrl, info->admin, &out);
-       mutex_unlock(&info->admin->lock);
+       vcap_unlock(info->admin);
        return ret;
 }
 DEFINE_SHOW_ATTRIBUTE(vcap_debugfs);
@@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused)
        };
        int ret;
 
-       mutex_lock(&info->admin->lock);
+       vcap_lock(info->admin);
        ret = vcap_show_admin_raw(info->vctrl, info->admin, &out);
-       mutex_unlock(&info->admin->lock);
+       vcap_unlock(info->admin);
        return ret;
 }
 DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs);
index 9c9d380421255d62dfb2d7a272c5cff6fb2f92ac..ac2a3b8c4f3217efeef3caba5140c200cf7df4bc 100644 (file)
@@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
 {
        /* Initialize the shared objects */
        INIT_LIST_HEAD(&test_vctrl.list);
+       mutex_init(&test_vctrl.lock);
        INIT_LIST_HEAD(&admin->list);
        INIT_LIST_HEAD(&admin->rules);
        INIT_LIST_HEAD(&admin->enabled);
-       mutex_init(&admin->lock);
+       admin->vctrl = &test_vctrl;
        list_add_tail(&admin->list, &test_vctrl.list);
        memset(test_updateaddr, 0, sizeof(test_updateaddr));
        test_updateaddridx = 0;
index ce26ccbdccdf93e079c7e857227c61454e7efcdf..83de384d3e3bd48b84ed59f2508db323ca12aa58 100644 (file)
@@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
 {
        /* Initialize the shared objects */
        INIT_LIST_HEAD(&test_vctrl.list);
+       mutex_init(&test_vctrl.lock);
        INIT_LIST_HEAD(&admin->list);
        INIT_LIST_HEAD(&admin->rules);
        INIT_LIST_HEAD(&admin->enabled);
-       mutex_init(&admin->lock);
+       admin->vctrl = &test_vctrl;
        list_add_tail(&admin->list, &test_vctrl.list);
        memset(test_updateaddr, 0, sizeof(test_updateaddr));
        test_updateaddridx = 0;
index 844bdf6b5f4502edee09890d21db9d82dc8caeb0..b4057fbe3d188c66e4179ddb6a9929d6ad9af4e9 100644 (file)
@@ -50,6 +50,9 @@ struct vcap_stream_iter {
 
 /* Check that the control has a valid set of callbacks */
 int vcap_api_check(struct vcap_control *ctrl);
+/* Serialize access to the vcap instances of a control */
+void vcap_lock(struct vcap_admin *admin);
+void vcap_unlock(struct vcap_admin *admin);
 /* Erase the VCAP cache area used or encoding and decoding */
 void vcap_erase_cache(struct vcap_rule_internal *ri);
 
index 7438ea6b3f266e31aaadadbe351785dc8357336b..9d9bfd116dabc2f2b637145af6ff8171e7e142b2 100644 (file)
@@ -2120,12 +2120,16 @@ drop:
 }
 
 static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev,
-                            dma_addr_t *da, bool *from_pool)
+                            dma_addr_t *da, bool *from_pool,
+                            struct page **pp_page, u32 *dma_sync_offset)
 {
        struct page *page;
        u32 offset;
        void *va;
+
        *from_pool = false;
+       *pp_page = NULL;
+       *dma_sync_offset = 0;
 
        /* Don't use fragments for jumbo frames or XDP where it's 1 fragment
         * per page.
@@ -2163,31 +2167,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev,
        va  = page_to_virt(page) + offset;
        *da = page_pool_get_dma_addr(page) + offset + rxq->headroom;
        *from_pool = true;
+       *pp_page = page;
+       *dma_sync_offset = offset + rxq->headroom;
 
        return va;
 }
 
 /* Allocate frag for rx buffer, and save the old buf */
 static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq,
-                              struct mana_recv_buf_oob *rxoob, void **old_buf,
-                              bool *old_fp)
+                              struct mana_recv_buf_oob *rxoob, u32 pktlen,
+                              void **old_buf, bool *old_fp)
 {
+       struct page *pp_page;
+       u32 dma_sync_offset;
        bool from_pool;
        dma_addr_t da;
        void *va;
 
-       va = mana_get_rxfrag(rxq, dev, &da, &from_pool);
+       va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page,
+                            &dma_sync_offset);
        if (!va)
                return;
-       if (!rxoob->from_pool || rxq->frag_count == 1)
+       if (!rxoob->from_pool || rxq->frag_count == 1) {
                dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize,
                                 DMA_FROM_DEVICE);
+       } else {
+               /* The page pool maps the whole page and only syncs for device
+                * automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes
+                * for the CPU before they are read: this is required if DMA
+                * is incoherent or bounce buffers are used.
+                */
+               page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page,
+                                          rxoob->dma_sync_offset, pktlen);
+       }
        *old_buf = rxoob->buf_va;
        *old_fp = rxoob->from_pool;
 
        rxoob->buf_va = va;
        rxoob->sgl[0].address = da;
        rxoob->from_pool = from_pool;
+       rxoob->pp_page = pp_page;
+       rxoob->dma_sync_offset = dma_sync_offset;
 }
 
 static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
@@ -2246,12 +2266,26 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
                rxbuf_oob = &rxq->rx_oobs[curr];
                WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1);
 
-               mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp);
+               if (unlikely(pktlen > rxq->datasize)) {
+                       /* Increase it even if mana_rx_skb() isn't called. */
+                       rxq->rx_cq.work_done++;
 
-               /* Unsuccessful refill will have old_buf == NULL.
-                * In this case, mana_rx_skb() will drop the packet.
-                */
-               mana_rx_skb(old_buf, old_fp, oob, rxq, i);
+                       ++ndev->stats.rx_dropped;
+                       netdev_warn_once(ndev,
+                               "Dropped oversized RX packet: len=%u, datasize=%u\n",
+                               pktlen, rxq->datasize);
+
+                       /* Reuse the RX buffer since rxbuf_oob is unchanged. */
+               } else {
+
+                       mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen,
+                                          &old_buf, &old_fp);
+
+                       /* Unsuccessful refill will have old_buf == NULL.
+                        * In this case, mana_rx_skb() will drop the packet.
+                        */
+                       mana_rx_skb(old_buf, old_fp, oob, rxq, i);
+               }
 
                mana_move_wq_tail(rxq->gdma_rq,
                                  rxbuf_oob->wqe_inf.wqe_size_in_bu);
@@ -2655,6 +2689,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key,
                            struct mana_rxq *rxq, struct device *dev)
 {
        struct mana_port_context *mpc = netdev_priv(rxq->ndev);
+       struct page *pp_page = NULL;
+       u32 dma_sync_offset = 0;
        bool from_pool = false;
        dma_addr_t da;
        void *va;
@@ -2662,13 +2698,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key,
        if (mpc->rxbufs_pre)
                va = mana_get_rxbuf_pre(rxq, &da);
        else
-               va = mana_get_rxfrag(rxq, dev, &da, &from_pool);
+               va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page,
+                                    &dma_sync_offset);
 
        if (!va)
                return -ENOMEM;
 
        rx_oob->buf_va = va;
        rx_oob->from_pool = from_pool;
+       rx_oob->pp_page = pp_page;
+       rx_oob->dma_sync_offset = dma_sync_offset;
 
        rx_oob->sgl[0].address = da;
        rx_oob->sgl[0].size = rxq->datasize;
index de5e29230b3c8deb12f1da98ae9bc1d2bc92f8e0..c46408698263251b8f148c19ff4b1cef93255be8 100644 (file)
@@ -166,18 +166,23 @@ static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw)
  *
  * Return: 0 on success, negative errno on failure
  **/
-static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
+static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size)
 {
-       const int size_in_words = size / sizeof(u32);
+       const int size_in_words = size / sizeof(__le32);
        struct mucse_mbx_info *mbx = &hw->mbx;
+       int off = MUCSE_MBX_FWPF_SHM;
        int err;
 
        err = mucse_obtain_mbx_lock_pf(hw);
        if (err)
                return err;
 
+       /* memcpy_fromio() is unsuitable: the mailbox uses 32-bit MMIO
+        * registers, not byte-addressable RAM. readl() guarantees
+        * the required 32-bit access width.
+        */
        for (int i = 0; i < size_in_words; i++)
-               msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i);
+               msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i));
        /* Hw needs write data_reg at last */
        mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
        /* flush reqs as we have read this request data */
@@ -236,7 +241,7 @@ static int mucse_poll_for_msg(struct mucse_hw *hw)
  * Return: 0 if it successfully received a message notification and
  * copied it into the receive buffer, negative errno on failure
  **/
-int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
+int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size)
 {
        int err;
 
@@ -290,9 +295,9 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw)
  * Return: 0 if it successfully copied message into the buffer,
  * negative errno on failure
  **/
-static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
+static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size)
 {
-       const int size_in_words = size / sizeof(u32);
+       const int size_in_words = size / sizeof(__le32);
        struct mucse_mbx_info *mbx = &hw->mbx;
        int err;
 
@@ -300,8 +305,12 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
        if (err)
                return err;
 
+       /* memcpy_toio() would decompose into arbitrary-width accesses;
+        * the mailbox requires 32-bit MMIO writes via writel().
+        */
        for (int i = 0; i < size_in_words; i++)
-               mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]);
+               mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4,
+                             le32_to_cpu(msg[i]));
 
        /* flush acks as we are overwriting the message buffer */
        hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx);
@@ -360,7 +369,8 @@ static int mucse_poll_for_ack(struct mucse_hw *hw)
  * Return: 0 if it successfully copied message into the buffer and
  * received an ack to that message within delay * timeout_cnt period
  **/
-int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
+int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg,
+                                u16 size)
 {
        int err;
 
index e6fcc8d1d3ca72c548c4ac1286327126c183cada..75b88b18b04d34b67a35a6150bc82f4499c2c947 100644 (file)
@@ -14,7 +14,8 @@
 #define MUCSE_MBX_REQ             BIT(0) /* Request a req to mailbox */
 #define MUCSE_MBX_PFU             BIT(3) /* PF owns the mailbox buffer */
 
-int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
+int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw,
+                                const __le32 *msg, u16 size);
 void mucse_init_mbx_params_pf(struct mucse_hw *hw);
-int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
+int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);
 #endif /* _RNPGBE_MBX_H */
index 8c8bd5e8e1db10300106c9cfb0faa1a94dee4061..5ba74997beacf25ca58be367f300b7cd7a90c0bf 100644 (file)
  * Return: 0 on success, negative errno on failure
  **/
 static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
-                                      struct mbx_fw_cmd_req *req,
-                                      struct mbx_fw_cmd_reply *reply)
+                                      union mbx_fw_cmd_req_u *req,
+                                      union mbx_fw_cmd_reply_u *reply)
 {
-       int len = le16_to_cpu(req->datalen);
+       int len = le16_to_cpu(req->r.datalen);
        int retry_cnt = 3;
        int err;
 
        mutex_lock(&hw->mbx.lock);
-       err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len);
+       err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len);
        if (err)
                goto out;
        do {
-               err = mucse_poll_and_read_mbx(hw, (u32 *)reply,
-                                             sizeof(*reply));
+               err = mucse_poll_and_read_mbx(hw, reply->dwords,
+                                             sizeof(reply->r));
                if (err)
                        goto out;
                /* mucse_write_and_wait_ack_mbx return 0 means fw has
                 * received request, wait for the expect opcode
                 * reply with 'retry_cnt' times.
                 */
-       } while (--retry_cnt >= 0 && reply->opcode != req->opcode);
+       } while (--retry_cnt >= 0 && reply->r.opcode != req->r.opcode);
 out:
        mutex_unlock(&hw->mbx.lock);
        if (!err && retry_cnt < 0)
                return -ETIMEDOUT;
-       if (!err && reply->error_code)
+       if (!err && reply->r.error_code)
                return -EIO;
 
        return err;
@@ -61,17 +61,19 @@ out:
  **/
 static int mucse_mbx_get_info(struct mucse_hw *hw)
 {
-       struct mbx_fw_cmd_req req = {
-               .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
-               .opcode  = cpu_to_le16(GET_HW_INFO),
+       union mbx_fw_cmd_req_u req = {
+               .r = {
+                       .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
+                       .opcode  = cpu_to_le16(GET_HW_INFO),
+               },
        };
-       struct mbx_fw_cmd_reply reply = {};
+       union mbx_fw_cmd_reply_u reply = {};
        int err;
 
        err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
        if (!err)
                hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0),
-                                       le16_to_cpu(reply.hw_info.pfnum));
+                                       le16_to_cpu(reply.r.hw_info.pfnum));
 
        return err;
 }
@@ -111,21 +113,23 @@ int mucse_mbx_sync_fw(struct mucse_hw *hw)
  **/
 int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
 {
-       struct mbx_fw_cmd_req req = {
-               .datalen = cpu_to_le16(sizeof(req.powerup) +
-                                      MUCSE_MBX_REQ_HDR_LEN),
-               .opcode  = cpu_to_le16(POWER_UP),
-               .powerup = {
-                       /* fw needs this to reply correct cmd */
-                       .version = cpu_to_le32(GENMASK_U32(31, 0)),
-                       .status  = cpu_to_le32(is_powerup ? 1 : 0),
+       union mbx_fw_cmd_req_u req = {
+               .r = {
+                       .datalen = cpu_to_le16(sizeof(req.r.powerup) +
+                                              MUCSE_MBX_REQ_HDR_LEN),
+                       .opcode  = cpu_to_le16(POWER_UP),
+                       .powerup = {
+                               /* fw needs this to reply correct cmd */
+                               .version = cpu_to_le32(GENMASK_U32(31, 0)),
+                               .status  = cpu_to_le32(is_powerup ? 1 : 0),
+                       },
                },
        };
        int len, err;
 
-       len = le16_to_cpu(req.datalen);
+       len = le16_to_cpu(req.r.datalen);
        mutex_lock(&hw->mbx.lock);
-       err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
+       err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
        mutex_unlock(&hw->mbx.lock);
 
        return err;
@@ -142,11 +146,13 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
  **/
 int mucse_mbx_reset_hw(struct mucse_hw *hw)
 {
-       struct mbx_fw_cmd_req req = {
-               .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
-               .opcode  = cpu_to_le16(RESET_HW),
+       union mbx_fw_cmd_req_u req = {
+               .r = {
+                       .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
+                       .opcode  = cpu_to_le16(RESET_HW),
+               },
        };
-       struct mbx_fw_cmd_reply reply = {};
+       union mbx_fw_cmd_reply_u reply = {};
 
        return mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
 }
@@ -166,24 +172,26 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
                          u8 *mac_addr,
                          int port)
 {
-       struct mbx_fw_cmd_req req = {
-               .datalen      = cpu_to_le16(sizeof(req.get_mac_addr) +
-                                           MUCSE_MBX_REQ_HDR_LEN),
-               .opcode       = cpu_to_le16(GET_MAC_ADDRESS),
-               .get_mac_addr = {
-                       .port_mask = cpu_to_le32(BIT(port)),
-                       .pfvf_num  = cpu_to_le32(pfvfnum),
+       union mbx_fw_cmd_req_u req = {
+               .r = {
+                       .datalen      = cpu_to_le16(sizeof(req.r.get_mac_addr) +
+                                                   MUCSE_MBX_REQ_HDR_LEN),
+                       .opcode       = cpu_to_le16(GET_MAC_ADDRESS),
+                       .get_mac_addr = {
+                               .port_mask = cpu_to_le32(BIT(port)),
+                               .pfvf_num  = cpu_to_le32(pfvfnum),
+                       },
                },
        };
-       struct mbx_fw_cmd_reply reply = {};
+       union mbx_fw_cmd_reply_u reply = {};
        int err;
 
        err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
        if (err)
                return err;
 
-       if (le32_to_cpu(reply.mac_addr.ports) & BIT(port))
-               memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN);
+       if (le32_to_cpu(reply.r.mac_addr.ports) & BIT(port))
+               memcpy(mac_addr, reply.r.mac_addr.addrs[port].mac, ETH_ALEN);
        else
                return -ENODATA;
 
index fb24fc12b613933d237deb12be9925fb31848ca3..fe996aeffc4de73cc0c611ac3e3763f6b50573bf 100644 (file)
@@ -80,6 +80,20 @@ struct mbx_fw_cmd_reply {
        };
 } __packed;
 
+/* Union wrappers to expose struct as __le32 dword array for mailbox
+ * transport, eliminating the need for pointer casts.  The __packed
+ * structs have no padding, so dwords[] overlays the fields exactly.
+ */
+union mbx_fw_cmd_req_u {
+       struct mbx_fw_cmd_req r;
+       __le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)];
+};
+
+union mbx_fw_cmd_reply_u {
+       struct mbx_fw_cmd_reply r;
+       __le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
+};
+
 int mucse_mbx_sync_fw(struct mucse_hw *hw);
 int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);
 int mucse_mbx_reset_hw(struct mucse_hw *hw);
index 33e18bb6977401a4a4c0edda77dced947cb60237..c11e0d8f98aa5c64763b17a8bbb29a1cd2bb7147 100644 (file)
@@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev,
        struct sk_buff *skb;
 
        skb = qede_build_skb(rxq, bd, len, pad);
+       if (unlikely(!skb))
+               return NULL;
+
        bd->page_offset += rxq->rx_buf_seg_size;
 
        if (bd->page_offset == PAGE_SIZE) {
@@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev,
        }
 
        skb = qede_build_skb(rxq, bd, len, pad);
+       if (unlikely(!skb))
+               return NULL;
 
        if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
                /* Incr page ref count to reuse on allocation failure so
index 9f3479500f85a1d52d56331ef5d1966ccb734ad2..d055a2628d8c9d0b6e7e85eb98f6fa9b5a1bd531 100644 (file)
@@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
 
                consume_skb(skb);
        } else {
-               __rmnet_map_ingress_handler(skb, port);
+               if (rmnet_map_validate_packet_len(skb, port))
+                       __rmnet_map_ingress_handler(skb, port);
+               else
+                       kfree_skb(skb);
        }
 }
 
index b70284095568ce32b0cd1b8f91e6fa4334b32817..60ca8b780c88ad7d9a6334370c0ff73a83e02d2d 100644 (file)
@@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
 void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
 void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
                                    u32 count, u32 time);
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port);
 
 #endif /* _RMNET_MAP_H_ */
index 8b4640c5d61ebe854ae54ec7decf649d50097f1a..305ae15ae8f393ecbe0749cdca3f55f2a5159edf 100644 (file)
@@ -333,54 +333,62 @@ done:
        return map_header;
 }
 
-/* Deaggregates a single packet
- * A whole new buffer is allocated for each portion of an aggregated frame.
- * Caller should keep calling deaggregate() on the source skb until 0 is
- * returned, indicating that there are no more packets to deaggregate. Caller
- * is responsible for freeing the original skb.
- */
-struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
-                                     struct rmnet_port *port)
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port)
 {
        struct rmnet_map_v5_csum_header *next_hdr = NULL;
        struct rmnet_map_header *maph;
        void *data = skb->data;
-       struct sk_buff *skbn;
-       u8 nexthdr_type;
        u32 packet_len;
 
-       if (skb->len == 0)
-               return NULL;
+       if (skb->len < sizeof(*maph))
+               return 0;
 
        maph = (struct rmnet_map_header *)skb->data;
+
+       /* Some hardware can send us empty frames. Catch them */
+       if (!maph->pkt_len)
+               return 0;
+
        packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
 
        if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
                packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-       } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
-               if (!(maph->flags & MAP_CMD_FLAG)) {
-                       packet_len += sizeof(*next_hdr);
-                       if (maph->flags & MAP_NEXT_HEADER_FLAG)
-                               next_hdr = data + sizeof(*maph);
-                       else
-                               /* Mapv5 data pkt without csum hdr is invalid */
-                               return NULL;
-               }
+       } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
+                  !(maph->flags & MAP_CMD_FLAG)) {
+               /* Mapv5 data pkt without csum hdr is invalid */
+               if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
+                       return 0;
+
+               packet_len += sizeof(*next_hdr);
+               next_hdr = data + sizeof(*maph);
        }
 
-       if (((int)skb->len - (int)packet_len) < 0)
-               return NULL;
+       if (skb->len < packet_len)
+               return 0;
 
-       /* Some hardware can send us empty frames. Catch them */
-       if (!maph->pkt_len)
-               return NULL;
+       if (next_hdr &&
+           u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
+           RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
+               return 0;
 
-       if (next_hdr) {
-               nexthdr_type = u8_get_bits(next_hdr->header_info,
-                                          MAPV5_HDRINFO_HDR_TYPE_FMASK);
-               if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
-                       return NULL;
-       }
+       return packet_len;
+}
+
+/* Deaggregates a single packet
+ * A whole new buffer is allocated for each portion of an aggregated frame.
+ * Caller should keep calling deaggregate() on the source skb until 0 is
+ * returned, indicating that there are no more packets to deaggregate. Caller
+ * is responsible for freeing the original skb.
+ */
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+                                     struct rmnet_port *port)
+{
+       struct sk_buff *skbn;
+       u32 packet_len;
+
+       packet_len = rmnet_map_validate_packet_len(skb, port);
+       if (!packet_len)
+               return NULL;
 
        skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
        if (!skbn)
index fb009120a92415cf51f12eab15b4c7925a25704d..ee0e2eb7dbc61c1affe4b0203337f2fdaeaa031c 100644 (file)
@@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
        }
 
        unprotected_len = skb->len;
-       eth = eth_hdr(skb);
+       eth = skb_eth_hdr(skb);
        sci_present = macsec_send_sci(secy);
        hh = skb_push(skb, macsec_extra_len(sci_present));
        memmove(hh, eth, 2 * ETH_ALEN);
@@ -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);
index c591eec8e97a56bd1a07c796796f2d1b72fede6d..e57121019153174e873a97ede5743709e1ca0d55 100644 (file)
@@ -122,7 +122,8 @@ config MDIO_MVUSB
 
 config MDIO_MSCC_MIIM
        tristate "Microsemi MIIM interface support"
-       depends on HAS_IOMEM && REGMAP_MMIO
+       depends on HAS_IOMEM
+       select REGMAP_MMIO
        help
          This driver supports the MIIM (MDIO) interface found in the network
          switches of the Microsemi SoCs; it is recommended to switch on
index 57c68efa5ff81da1233274b66f849c0cf86b6beb..717c1d3aa95380da64eb5ae6fc1da6e4dfaa9d87 100644 (file)
@@ -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 */
 }
 
 /*
index c4cebacabcb5343d3e8c93fc3d4ad310f9728db7..cb782d81d84f2ad2df2f732267058c29db3a5a00 100644 (file)
@@ -1499,6 +1499,17 @@ multicast_write_done:
        return;
 }
 
+static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata,
+                                      struct net_device *netdev,
+                                      netdev_features_t features)
+{
+       if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
+           !(netdev->flags & IFF_PROMISC))
+               pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
+       else
+               pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
+}
+
 static void lan78xx_set_multicast(struct net_device *netdev)
 {
        struct lan78xx_net *dev = netdev_priv(netdev);
@@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev)
                }
        }
 
+       lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features);
+
        if (netdev_mc_count(dev->net)) {
                struct netdev_hw_addr *ha;
                int i;
@@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev,
        else
                pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_;
 
-       if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
-               pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
-       else
-               pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
+       lan78xx_update_vlan_filter(pdata, netdev, features);
 
        spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
 
index 5d4a1fd2b5244c4f72f68077549d5bb8b81448e2..19f6e1222d9315ccc8912a5afc3914f2f3335761 100644 (file)
@@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
        skb_trim(skb, skb->len - sizeof *trailer);
 
        if ((packet_len & 0x01) == 0) {
-               if (skb->data [packet_len] != PAD_BYTE) {
+               if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) {
                        dev->net->stats.rx_frame_errors++;
                        netdev_dbg(dev->net, "bad pad\n");
                        return 0;
index ce09d44fa73cf8fba9e43ad01a5ef1485c05dde9..873754be5174bcaa0096b91761b331b0c7b9c6e9 100644 (file)
@@ -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);
 
index 8fb595733b9c3603f5a12184c0b6cfdc9af0027b..b725c64e5b5c63a6531f10654cb7dddd8628c0c9 100644 (file)
@@ -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 */
index c11428485dccf5f456920b9ff057fd7d72b692cc..2b8a23865bfb29226fa03a916c500479f8895b38 100644 (file)
@@ -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
index b7bc94f7abd8aad6feb4ad1a06eaf0e64c260ae2..c8841f9b9ad918e26931afc53baad8d0221e866b 100644 (file)
@@ -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
index 1294a1d6528e2c87878fd729b92f51664fc145a4..9f491334c8d0435c5a18198ba947d01725004355 100644 (file)
@@ -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);
        }
 
index f124110944b7e97a4c76402146a82423dd143291..9bf7d4c207b9edf5e9fdc291c366b0ac44bc6fb1 100644 (file)
@@ -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);
        }
 }
 
index 27304a98787d6abee45c12d1520ecab674309efc..13d08022e414154a369efd318c008b6f00c1dbcf 100644 (file)
@@ -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));
 
index fb20fe31cd363bf250b1bd1124bd646e6a5f4d7a..42be6fa22f9c6aa973c2f2e2711fc84d1aab6b3c 100644 (file)
@@ -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);
 }
 
index c9daf893472fe28dcf176222630d6d6cb963d7a5..abc703441c5d85d4efac447dee2bc2e6921c279c 100644 (file)
@@ -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);
index 5a1a0287c1d58bf44b7e7a0711950ef2ba2fd7cf..b48f7febaf03fd55b401a634f280a3123afd193a 100644 (file)
@@ -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;
 
index cac191304bf5f29d8e37e8e0071337a42951756a..b846fe589c2ba0da2fb0ec34f792621bcfd31171 100644 (file)
@@ -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;
index fc35b60e422c03c0efd83ffd3a784efe993fbd65..be9df35acc33b7ff3107610a63ccc1659c0234d8 100644 (file)
@@ -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;
index 4fa14bb573add555a2880084237bb730987ef436..2596b9fcc7dd344daa40346f18dc78e4e0abf2a4 100644 (file)
@@ -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);
index 82fb230a73bb766edd01e12d8537817e30300bc1..4d94b7062f4404ce73c9672d222fb9cbeef8cd91 100644 (file)
@@ -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);
@@ -1494,6 +1494,14 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
 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);
index 79e1fd0a1fbdd7a8edb08eff77e70c7e2980ebae..d4783658b2c5dbb69ab6c715a23ac8814ef3afb0 100644 (file)
@@ -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;
index a0c36144eb0b23f7e3f9c7eec3378514308340f8..501071104a9fe631247458430f646380376fc31d 100644 (file)
@@ -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);
 
index 7f2c1608f2ce3946cab8eb2b3048923afaea90c4..bb167f03367bf7dc4a4876818486e74dc8d423cb 100644 (file)
@@ -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);
index 0dd8a6c85953446c45cfeb6b23ac05fc837009ca..956ff9b94526daa04bcb93997f47e07aa517630f 100644 (file)
@@ -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);
 
index d50ea62fa2f3a79c18a553047a57dfd39505dae2..a37083cdb9083f6db8a360ab2f57f76feb56f905 100644 (file)
 
 #include "pmc.h"
 
+static const struct amd_pmc_bit_map soc15_ip_blk_v3[] = {
+       {"VDDCR",       BIT(0)},
+       {"VDDCR_LP",    BIT(1)},
+       {"LSOCV",       BIT(2)},
+       {"DISPLAY",     BIT(3)},
+       {"VCN",         BIT(4)},
+       {"JPEG",        BIT(5)},
+       {"UMSCH",       BIT(6)},
+       {"VPE",         BIT(7)},
+       {"MPM",         BIT(8)},
+       {"NPU",         BIT(9)},
+       {"USB_HC0",     BIT(10)},
+       {"eUSB_HC0",    BIT(11)},
+       {"RT0_ADP_HC1", BIT(12)},
+       {"RT1_ADP_HC1", BIT(13)},
+       {"RT2_ADP_HC2", BIT(14)},
+       {"USB4_RT0",    BIT(15)},
+       {"USB4_RT1",    BIT(16)},
+       {"USB4-RT2",    BIT(17)},
+       {"LAPIC",       BIT(18)},
+};
+
 static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = {
        {"DISPLAY",     BIT(0)},
        {"CPU",         BIT(1)},
@@ -159,9 +181,9 @@ static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = {
        .smu_msg        = AMD_PMC_REGISTER_MSG_1AH_80H,
        .smu_arg        = AMD_PMC_REGISTER_ARG_1AH_80H,
        .smu_rsp        = AMD_PMC_REGISTER_RSP_1AH_80H,
-       .num_ips        = ARRAY_SIZE(soc15_ip_blk),
+       .num_ips        = ARRAY_SIZE(soc15_ip_blk_v3),
        .scratch_reg    = AMD_PMC_SCRATCH_REG_1AH,
-       .ips_ptr        = soc15_ip_blk,
+       .ips_ptr        = soc15_ip_blk_v3,
        .os_hint        = MSG_OS_HINT_RN,
 };
 
@@ -735,11 +757,11 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev)
        } else if (delay_suspend == 1) {
                if (!intermediate_wakeup)
                        dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n",
-                                dmi_get_system_info(DMI_SYS_VENDOR),
-                                dmi_get_system_info(DMI_PRODUCT_NAME),
-                                dmi_get_system_info(DMI_PRODUCT_FAMILY),
-                                dmi_get_system_info(DMI_BOARD_VENDOR),
-                                dmi_get_system_info(DMI_BOARD_NAME));
+                                dmi_get_system_info(DMI_SYS_VENDOR) ?: "(Unknown)",
+                                dmi_get_system_info(DMI_PRODUCT_NAME) ?: "(Unknown)",
+                                dmi_get_system_info(DMI_PRODUCT_FAMILY) ?: "(Unknown)",
+                                dmi_get_system_info(DMI_BOARD_VENDOR) ?: "(Unknown)",
+                                dmi_get_system_info(DMI_BOARD_NAME) ?: "(Unknown)");
                return true;
        }
        return false;
index 65166b50a2c3b91898625b82fd77f74bd5a4afcb..322bfa647c2dc2735b9b4e77d5d46cad1a35c8aa 100644 (file)
@@ -1997,13 +1997,13 @@ static const struct dmi_system_id power_limits[] = {
                .driver_data = &(struct power_data) {
                        .ac_data = &(struct power_limits) {
                                .ppt_pl1_spl_min = 30,
-                               .ppt_pl1_spl_max = 90,
+                               .ppt_pl1_spl_max = 120,
                                .ppt_pl2_sppt_min = 65,
-                               .ppt_pl2_sppt_def = 110,
-                               .ppt_pl2_sppt_max = 125,
+                               .ppt_pl2_sppt_def = 140,
+                               .ppt_pl2_sppt_max = 145,
                                .ppt_pl3_fppt_min = 65,
-                               .ppt_pl3_fppt_def = 110,
-                               .ppt_pl3_fppt_max = 125,
+                               .ppt_pl3_fppt_def = 140,
+                               .ppt_pl3_fppt_max = 145,
                                .nv_temp_target_min = 75,
                                .nv_temp_target_max = 87,
                                .nv_dynamic_boost_min = 5,
index b0d06a80e89ef00827872e71339b23f98ca30c25..3a373184519da40fa308a3bb5ffd6ec60cd9e839 100644 (file)
@@ -300,6 +300,10 @@ static int bitland_mifs_wmi_suspend(struct device *dev)
        enum platform_profile_option profile;
        int ret;
 
+       /* Skip event device */
+       if (!data->pp_dev)
+               return 0;
+
        ret = laptop_profile_get(data->pp_dev, &profile);
        if (ret == 0)
                data->saved_profile = profile;
@@ -311,6 +315,10 @@ static int bitland_mifs_wmi_resume(struct device *dev)
 {
        struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
 
+       /* Skip event device */
+       if (!data->pp_dev)
+               return 0;
+
        dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile);
        return laptop_profile_set(dev, data->saved_profile);
 }
index 19e992d2ee3b845bc9382bcd494a5d96f9c6ac44..99d100e1d923f51122e47775c0ea443ff30105cb 100644 (file)
@@ -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),
 };
index 1afc78b034fad8fa09a3e8527586387156fc2543..243ce939ba6807e2cde5237f7c9672c7b9247d9e 100644 (file)
@@ -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[] = {
index e1cfd42234734f3c13872e79001840743b1b8bd4..f0a6339affd7588290fb4a2d875c708ebefed3e6 100644 (file)
@@ -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);
index dc5d67767336e0bc24c35efba07b884b91cb4c20..1797929dfe56632e0fdda9e323c0e0c16c217d38 100644 (file)
@@ -248,7 +248,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1,
        ret = regulator_lock_nested(rdev1, ww_ctx);
        WARN_ON(ret);
        ret = regulator_lock_nested(rdev2, ww_ctx);
-       if (ret != -EDEADLOCK) {
+       if (ret != -EDEADLK) {
                WARN_ON(ret);
                goto exit;
        }
@@ -264,7 +264,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1,
                swap(held, contended);
                ret = regulator_lock_nested(contended, ww_ctx);
 
-               if (ret != -EDEADLOCK) {
+               if (ret != -EDEADLK) {
                        WARN_ON(ret);
                        break;
                }
index 73d511eb1c1dceff75217b9dbc702246a1e309c7..768cdce0d4ecfba377c2ec4467a6a31d7d8576c1 100644 (file)
 #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,
index 952852bbe92341ceb9d0c1736238357e9eb90af2..b170506eec578f0dac49069265633fec3f448242 100644 (file)
@@ -329,6 +329,7 @@ static const struct of_device_id mt6316_regulator_match[] = {
        { .compatible = "mediatek,mt6316d-regulator", .data = (void *)MT6316_TYPE_4PHASE },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, mt6316_regulator_match);
 
 static struct spmi_driver mt6316_regulator_driver = {
        .driver = {
index 0aebcbda0a196343d32c85fafc887bab909cb4f4..aa6a8eb7ac4b440d740b63e6e52d1e055212a408 100644 (file)
@@ -927,6 +927,7 @@ static const struct of_device_id mt6363_regulator_match[] = {
        { .compatible = "mediatek,mt6363-regulator" },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, mt6363_regulator_match);
 
 static struct platform_driver mt6363_regulator_driver = {
        .driver = {
index dd01fe11c5cb38572079750cda968270cf25780e..a3cb8244d76a6f71473b562da6b0ecc877192de7 100644 (file)
@@ -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:
index 2544de6576e446caa851618bb586acfc1e47f616..2f6df7707cad1b6653201dbce18a2fe7b76cfcbb 100644 (file)
@@ -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;
index 9841f5e057b2a0ce65513d2d2db4c1cbdbf39f6d..2e87f320cf118471586bb18a682f5aef621ac5ed 100644 (file)
@@ -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/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h
deleted file mode 100644 (file)
index e69de29..0000000
index 2df399c537c13ccb3dd107f5d775f00c0cdd865a..8c9d423129c076be425c300c65b60bdf13b9d314 100644 (file)
@@ -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)
 {
index 1838032f648621a9e95d4b9595c09fb7e3275136..b79c6a7ea79122a413568a36268ad7e0f84719ce 100644 (file)
@@ -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;
        }
 
index e6addab66a6031ea9ccf65b97ac153355812e40d..6a871a59c9096750d91f7d0abb80bf9d3b273102 100644 (file)
@@ -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;
                }
                /*
index e047747d4ecf8163c939be3f5d43f370204c236e..46cc8e3c79a26eba366fc05b45d736f6e43310ee 100644 (file)
@@ -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) {
index a1b116cd4723d57cacd043350afd1e8177d15b7e..8edad1830abe4d83770d0ebc83bc519b2e8f6c9d 100644 (file)
@@ -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;
        }
 
index 82af59c913e9310baf48db81d926888f7668e3db..23355f12fbffa4006af3b3c0f65f7b15d290fd5a 100644 (file)
@@ -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);
 
index 147127fb4db9cc4e9ef1de2ee6af3ac83a7fc381..453a2232452dba2f0486b8af4f4a5a24222d90b6 100644 (file)
@@ -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);
index 22e2e3223440d630731d3e122aed244764e381e9..daeb3693fe559a5fe9d7478ff585896e490877b6 100644 (file)
@@ -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
index 7a193cc04e5b6fd1f72a32693a7827f41b7a8c80..3dbf2ca59536c9dfe735cd5451d72d24334116f8 100644 (file)
@@ -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);
index 74cd4e8a61c2a089332825ca7684d74d46e6cfcc..5408f002e6c01f1f62f824dddf8e855be5306261 100644 (file)
@@ -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;
index 5b0e9dae231fbe4ed2e4e7e53b1a4a73a1ad82df..6af69ccaa295c5aff84e2601ca53ea37999025d1 100644 (file)
@@ -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;
 }
index aaba1a3ad57783a6ac125623b78e7bf9b4009b79..ecb0be39469651749d3e80bfc69da2b296e75038 100644 (file)
@@ -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);
index bd70a7ed8067dcd0c62287c5ba0d0da5728b9dce..f7d848fec9ab6d1ff124acbe25bf647bd0a77ee6 100644 (file)
@@ -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,
index d0bbe1bb979c863cbc1503ffd1ba2f3ef4faf940..54f805a6b5ce559476d7ef2b221e40a0cd0d9d4e 100644 (file)
@@ -583,9 +583,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len)
        cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
 
        while (cnt < in_len) {
+               if (cnt + 2 > in_len)
+                       break;
+               if (cnt + 2 + in_ie[cnt + 1] > in_len)
+                       break;
                authmode = in_ie[cnt];
 
                if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
+                   in_ie[cnt + 1] >= 8 &&
                    (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
                     !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
                        if (wapi_ie)
@@ -615,9 +620,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie
        cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
 
        while (cnt < in_len) {
+               if (cnt + 2 > in_len)
+                       break;
+               if (cnt + 2 + in_ie[cnt + 1] > in_len)
+                       break;
                authmode = in_ie[cnt];
 
                if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
+                   in_ie[cnt + 1] >= 4 &&
                    (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
                        if (wpa_ie)
                                memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
@@ -698,6 +708,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att
        if (len_attr)
                *len_attr = 0;
 
+       if (wps_ielen < 6)
+               return attr_ptr;
+
        if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) ||
                (memcmp(wps_ie + 2, wps_oui, 4))) {
                return attr_ptr;
@@ -708,6 +721,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att
 
        while (attr_ptr - wps_ie < wps_ielen) {
                /*  4 = 2(Attribute ID) + 2(Length) */
+               if (attr_ptr + 4 > wps_ie + wps_ielen)
+                       break;
                u16 attr_id = get_unaligned_be16(attr_ptr);
                u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
                u16 attr_len = attr_data_len + 4;
index a86d6f97cf02d7cba106cce23807e6e726862dc5..a443b3530fb986c586094bf95fc3fd4b82b38ffc 100644 (file)
@@ -677,6 +677,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
        if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
                return _FAIL;
 
+       if (len < WLAN_HDR_A3_LEN)
+               return _FAIL;
+
        sa = GetAddr2Ptr(pframe);
 
        auth_mode = psecuritypriv->dot11AuthAlgrthm;
@@ -688,6 +691,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
                prxattrib->hdrlen = WLAN_HDR_A3_LEN;
                prxattrib->encrypt = _WEP40_;
 
+               if (len < WLAN_HDR_A3_LEN + 8)
+                       return _FAIL;
+
                iv = pframe+prxattrib->hdrlen;
                prxattrib->key_index = ((iv[3]>>6)&0x3);
 
@@ -787,7 +793,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
                        p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len,
                                        len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4);
 
-                       if (!p || ie_len <= 0) {
+                       if (!p || ie_len != 128) {
                                status = WLAN_STATUS_CHALLENGE_FAIL;
                                goto auth_fail;
                        }
@@ -1365,7 +1371,11 @@ unsigned int OnAssocRsp(struct adapter *padapter, union recv_frame *precv_frame)
        /* to handle HT, WMM, rate adaptive, update MAC reg */
        /* for not to handle the synchronous IO in the tasklet */
        for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) {
+               if (i + sizeof(*pIE) > pkt_len)
+                       break;
                pIE = (struct ndis_80211_var_ie *)(pframe + i);
+               if (i + sizeof(*pIE) + pIE->length > pkt_len)
+                       break;
 
                switch (pIE->element_id) {
                case WLAN_EID_VENDOR_SPECIFIC:
@@ -2855,7 +2865,11 @@ void issue_assocreq(struct adapter *padapter)
 
        /* vendor specific IE, such as WPA, WMM, WPS */
        for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) {
+               if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length)
+                       break;
                pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i);
+               if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length)
+                       break;
 
                switch (pIE->element_id) {
                case WLAN_EID_VENDOR_SPECIFIC:
@@ -5183,7 +5197,11 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
 
        /* sizeof(struct ndis_802_11_fix_ie) */
        for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) {
+               if (i + sizeof(*pIE) > pnetwork->ie_length)
+                       break;
                pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i);
+               if (i + sizeof(*pIE) + pIE->length > pnetwork->ie_length)
+                       break;
 
                switch (pIE->element_id) {
                case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */
index 1d37c2d5b10d9b654203146439db0a76fd5b54e3..a4de538722b56e5ee13e7a2bb3506342b4dd8b4c 100644 (file)
@@ -909,7 +909,8 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE)
 
        pmlmeinfo->HT_caps_enable = 1;
 
-       for (i = 0; i < (pIE->length); i++) {
+       for (i = 0; i < umin(pIE->length,
+                            sizeof(pmlmeinfo->HT_caps.u.HT_cap)); i++) {
                if (i != 2) {
                        /* Commented by Albert 2010/07/12 */
                        /* Got the endian issue here. */
@@ -1262,7 +1263,11 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru
        len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN);
 
        for (i = 0; i < len;) {
+               if (i + sizeof(*pIE) > len)
+                       break;
                pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i);
+               if (i + sizeof(*pIE) + pIE->length > len)
+                       break;
 
                switch (pIE->element_id) {
                case WLAN_EID_VENDOR_SPECIFIC:
@@ -1287,7 +1292,7 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru
                        break;
                }
 
-               i += (pIE->length + 2);
+               i += sizeof(*pIE) + pIE->length;
        }
 }
 
@@ -1303,15 +1308,23 @@ unsigned int is_ap_in_tkip(struct adapter *padapter)
                for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) {
                        pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i);
 
+                       if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length)
+                               break;
+                       if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length)
+                               break;
+
                        switch (pIE->element_id) {
                        case WLAN_EID_VENDOR_SPECIFIC:
-                               if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)))
+                               if (pIE->length >= 16 &&
+                                   !memcmp(pIE->data, RTW_WPA_OUI, 4) &&
+                                   !memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4))
                                        return true;
 
                                break;
 
                        case WLAN_EID_RSN:
-                               if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4))
+                               if (pIE->length >= 12 &&
+                                   !memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4))
                                        return true;
                                break;
 
@@ -1319,7 +1332,7 @@ unsigned int is_ap_in_tkip(struct adapter *padapter)
                                break;
                        }
 
-                       i += (pIE->length + 2);
+                       i += sizeof(*pIE) + pIE->length;
                }
 
                return false;
index 1484336d7551a08b26fb1d3be388e4bc15a828af..6a97afd89dc78a0867ea6517b58e07ffa93ead9a 100644 (file)
@@ -1446,6 +1446,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel
 
        pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen);
        if (pwpa && wpa_ielen > 0) {
+               if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
                if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
                        padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
                        padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
@@ -1455,6 +1459,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel
 
        pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen);
        if (pwpa2 && wpa2_ielen > 0) {
+               if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
                if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
                        padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
                        padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
index d5bb1c7932fc31a4145d03ded3a0f461c3e4a69e..4260ed5f4e974ec7350f1aa4b1093d215f8f8290 100644 (file)
@@ -24,9 +24,11 @@ void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile)
 int _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, unsigned int rlen)
 {
        int ret;
+       unsigned int remain = rtw_remainder_len(pfile);
 
-       if (rtw_remainder_len(pfile) < rlen)
-               return -EINVAL;
+       /* clamp to bytes remaining; the coalesce loop relies on short reads */
+       if (rlen > remain)
+               rlen = remain;
 
        if (rmem) {
                ret = skb_copy_bits(pfile->pkt, pfile->buf_len - pfile->pkt_len, rmem, rlen);
index 8abaa3165fbb7bc8920e331590c2f0f847ad736f..434cf760ade6c82eb70db51d86dff658621bf894 100644 (file)
@@ -1239,6 +1239,7 @@ static void __exit fake_exit(void)
 {
        struct list_head *pos = NULL;
        struct list_head *tmplist;
+       struct vme_lm_resource *lm;
        struct vme_master_resource *master_image;
        struct vme_slave_resource *slave_image;
        int i;
@@ -1268,6 +1269,13 @@ static void __exit fake_exit(void)
        vme_unregister_bridge(fake_bridge);
 
        fake_crcsr_exit(fake_bridge);
+       /* resources are stored in link list */
+       list_for_each_safe(pos, tmplist, &fake_bridge->lm_resources) {
+               lm = list_entry(pos, struct vme_lm_resource, list);
+               list_del(pos);
+               kfree(lm);
+       }
+
        /* resources are stored in link list */
        list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) {
                slave_image = list_entry(pos, struct vme_slave_resource, list);
index 4cf3486646ceeaa24c3aeacacc46bd76e92adfd0..c695ad9b4ca265a0f4caa8c8be4de9bab0544c5c 100644 (file)
@@ -2534,6 +2534,7 @@ static void tsi148_remove(struct pci_dev *pdev)
 {
        struct list_head *pos = NULL;
        struct list_head *tmplist;
+       struct vme_lm_resource *lm;
        struct vme_master_resource *master_image;
        struct vme_slave_resource *slave_image;
        struct vme_dma_resource *dma_ctrlr;
@@ -2590,6 +2591,13 @@ static void tsi148_remove(struct pci_dev *pdev)
 
        tsi148_crcsr_exit(tsi148_bridge, pdev);
 
+       /* resources are stored in link list */
+       list_for_each_safe(pos, tmplist, &tsi148_bridge->lm_resources) {
+               lm = list_entry(pos, struct vme_lm_resource, list);
+               list_del(pos);
+               kfree(lm);
+       }
+
        /* resources are stored in link list */
        list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) {
                dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
index 11e25c2f6b0aa06264b177cfcb3da47dc87c0ee0..a472a38ef613a635915f3039281b42675464c8de 100644 (file)
@@ -156,6 +156,17 @@ static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
 {
        void *image_ptr;
 
+       /*
+        * The slave window (image_size) can exceed the fixed kern_buf
+        * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf.
+        * *ppos is >= 0 here (checked by the caller), so the
+        * subtraction below cannot wrap.
+        */
+       if (*ppos >= image[minor].size_buf)
+               return 0;
+       if (count > image[minor].size_buf - *ppos)
+               count = image[minor].size_buf - *ppos;
+
        image_ptr = image[minor].kern_buf + *ppos;
        if (copy_to_user(buf, image_ptr, (unsigned long)count))
                return -EFAULT;
@@ -168,6 +179,17 @@ static ssize_t buffer_from_user(unsigned int minor, const char __user *buf,
 {
        void *image_ptr;
 
+       /*
+        * The slave window (image_size) can exceed the fixed kern_buf
+        * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf.
+        * *ppos is >= 0 here (checked by the caller), so the
+        * subtraction below cannot wrap.
+        */
+       if (*ppos >= image[minor].size_buf)
+               return 0;
+       if (count > image[minor].size_buf - *ppos)
+               count = image[minor].size_buf - *ppos;
+
        image_ptr = image[minor].kern_buf + *ppos;
        if (copy_from_user(image_ptr, buf, (unsigned long)count))
                return -EFAULT;
index 87c5d26a5089561c7bb7ef551b1be9693ada6313..2853b95b2c59769d97f9fde2d0e8c538f2164caf 100644 (file)
@@ -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;
index 763e6d26e187725164033e6ae43c8c34d336fdab..f0886ea290345fc478523f5fec7aaee487411a4e 100644 (file)
@@ -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);
index 11790f2c5d80fe9f68a664a3a2a7103e66b074d3..1a77b4bb62b009bd26da429eb31c61b61d5a4268 100644 (file)
@@ -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)
index 8ec03863606e4b0b2244de504d8b58e2b183548d..f88809ff370b73fd955cdf03cfacee93c3b92013 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/rational.h>
+#include <linux/util_macros.h>
 
 #include <linux/dma/hsu.h>
 
@@ -368,8 +369,16 @@ static const struct mid8250_board dnv_board = {
        .freq = 133333333,
        .base_baud = 115200,
        .bar = 1,
-       .setup = dnv_setup,
-       .exit = dnv_exit,
+       /*
+        * Errata:
+        * HSUART May Stop Functioning when DMA is Active.
+        *
+        * - Denverton document #572409, rev 3.4, DNV60
+        * - Ice Lake Xeon D document #714070, ICXD65
+        * - Snowridge document #731931, SNR44
+        */
+       .setup = PTR_IF(false, dnv_setup),
+       .exit = PTR_IF(false, dnv_exit),
 };
 
 static const struct pci_device_id pci_ids[] = {
index c552c6b9a037ff0fb2135c449991683f1db76ba6..3c7775df27effd309419b413c791b46688d4aacd 100644 (file)
@@ -944,11 +944,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
                                dev_err(p->port.dev, "teardown incomplete\n");
                }
        }
+
+       dma->rx_running = 0;
        if (!count)
                goto out;
        ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
 
-       dma->rx_running = 0;
        p->port.icount.rx += ret;
        p->port.icount.buf_overrun += count - ret;
 out:
index 630deb7dd3449d1be06afaf45884b5b1bc6daead..e94a0802cbddc2ef3af3e2787f770cc60a2235e1 100644 (file)
@@ -2000,8 +2000,14 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
 
        tx_ready = wait_for_lsr(up, bits);
 
-       /* Wait up to 1s for flow control if necessary */
-       if (uart_console_hwflow_active(&up->port)) {
+       /*
+        * Wait up to 1s for flow control if necessary.
+        * When 'no_console_suspend' is active (in the window between
+        * suspend() and resume()), flow control is temporarily ignored
+        * because the canary workaround is not reliable in all situations,
+        * leading to flow control timeouts for every character.
+        */
+       if (uart_console_hwflow_active(&up->port) && !up->canary) {
                for (tmout = 1000000; tmout; tmout--) {
                        unsigned int msr = serial_in(up, UART_MSR);
                        up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
index 59e71306a5d46583fc8af194a4ed57f72fa927a7..5fe12577ce8b887bd03698808d38fabdae2b9754 100644 (file)
@@ -1243,6 +1243,17 @@ static int max310x_gpio_set(struct gpio_chip *chip, unsigned int offset,
        return 0;
 }
 
+static int max310x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+       struct max310x_port *s = gpiochip_get_data(chip);
+       struct uart_port *port = &s->p[offset / 4].port;
+       unsigned int val;
+
+       val = max310x_port_read(port, MAX310X_GPIOCFG_REG);
+
+       return val & BIT(offset % 4) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
 static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
 {
        struct max310x_port *s = gpiochip_get_data(chip);
@@ -1446,6 +1457,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
        s->gpio.owner           = THIS_MODULE;
        s->gpio.parent          = dev;
        s->gpio.label           = devtype->name;
+       s->gpio.get_direction   = max310x_gpio_get_direction;
        s->gpio.direction_input = max310x_gpio_direction_input;
        s->gpio.get             = max310x_gpio_get;
        s->gpio.direction_output= max310x_gpio_direction_output;
index 2e999cb9c97447255c14d632ccbbbbfc65972f77..bfa44b01c3e9795b9d2da2e6bf8d4cb33ed4ae84 100644 (file)
@@ -1228,7 +1228,8 @@ static int msm_startup(struct uart_port *port)
        data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
        msm_write(port, data, MSM_UART_MR1);
 
-       if (msm_port->is_uartdm) {
+       /* Disable DMA for console to prevent PIO/DMA collisions */
+       if (msm_port->is_uartdm && !uart_console(port)) {
                msm_request_tx_dma(msm_port, msm_port->uart.mapbase);
                msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
        }
index dfdea0842149500e70ff328002f60d3177fdf11b..763a3f1b7be08ac43953c7fe6083b5e19c412328 100644 (file)
@@ -765,16 +765,22 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
 /*
  * Compute xterm-style modifier parameter for CSI sequences.
  * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
+ *
+ * Only the canonical modifier weights are counted. The left/right variants
+ * (KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR) and KG_ALTGR are commonly
+ * repurposed as keymap layout-group or level selectors rather than as plain
+ * modifiers (for instance XKB-derived keymaps select the layout group with
+ * KG_SHIFTL/KG_SHIFTR), so counting them would encode a spurious modifier.
  */
 static int csi_modifier_param(void)
 {
        int mod = 1;
 
-       if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR)))
+       if (shift_state & BIT(KG_SHIFT))
                mod += 1;
-       if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR)))
+       if (shift_state & BIT(KG_ALT))
                mod += 2;
-       if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR)))
+       if (shift_state & BIT(KG_CTRL))
                mod += 4;
        return mod;
 }
index 309ae51b4906396ed3e5d68b98a0c0dc26e67ae4..377a3c54b9f56e0e0e859f8554d7047cbad79ec0 100644 (file)
@@ -89,16 +89,18 @@ TRACE_EVENT(ufshcd_clk_gating,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __field(int, state)
        ),
 
        TP_fast_assign(
+               __assign_str(dev_name);
                __entry->hba = hba;
                __entry->state = state;
        ),
 
        TP_printk("%s: gating state changed to %s",
-               dev_name(__entry->hba->dev),
+               __get_str(dev_name),
                __print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES))
 );
 
@@ -111,6 +113,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __string(state, state)
                __string(clk, clk)
                __field(u32, prev_state)
@@ -119,6 +122,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __assign_str(state);
                __assign_str(clk);
                __entry->prev_state = prev_state;
@@ -126,7 +130,7 @@ TRACE_EVENT(ufshcd_clk_scaling,
        ),
 
        TP_printk("%s: %s %s from %u to %u Hz",
-               dev_name(__entry->hba->dev), __get_str(state), __get_str(clk),
+               __get_str(dev_name), __get_str(state), __get_str(clk),
                __entry->prev_state, __entry->curr_state)
 );
 
@@ -138,16 +142,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __string(state, state)
        ),
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __assign_str(state);
        ),
 
        TP_printk("%s: auto bkops - %s",
-               dev_name(__entry->hba->dev), __get_str(state))
+               __get_str(dev_name), __get_str(state))
 );
 
 DECLARE_EVENT_CLASS(ufshcd_profiling_template,
@@ -158,6 +164,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __string(profile_info, profile_info)
                __field(s64, time_us)
                __field(int, err)
@@ -165,13 +172,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template,
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __assign_str(profile_info);
                __entry->time_us = time_us;
                __entry->err = err;
        ),
 
        TP_printk("%s: %s: took %lld usecs, err %d",
-               dev_name(__entry->hba->dev), __get_str(profile_info),
+               __get_str(dev_name), __get_str(profile_info),
                __entry->time_us, __entry->err)
 );
 
@@ -200,6 +208,7 @@ DECLARE_EVENT_CLASS(ufshcd_template,
                __field(s64, usecs)
                __field(int, err)
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __field(int, dev_state)
                __field(int, link_state)
        ),
@@ -208,13 +217,14 @@ DECLARE_EVENT_CLASS(ufshcd_template,
                __entry->usecs = usecs;
                __entry->err = err;
                __entry->hba = hba;
+               __assign_str(dev_name);
                __entry->dev_state = dev_state;
                __entry->link_state = link_state;
        ),
 
        TP_printk(
                "%s: took %lld usecs, dev_state: %s, link_state: %s, err %d",
-               dev_name(__entry->hba->dev),
+               __get_str(dev_name),
                __entry->usecs,
                __print_symbolic(__entry->dev_state, UFS_PWR_MODES),
                __print_symbolic(__entry->link_state, UFS_LINK_STATES),
@@ -279,6 +289,7 @@ TRACE_EVENT(ufshcd_command,
        TP_STRUCT__entry(
                __field(struct scsi_device *, sdev)
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(&sdev->sdev_dev))
                __field(enum ufs_trace_str_t, str_t)
                __field(unsigned int, tag)
                __field(u32, doorbell)
@@ -291,6 +302,7 @@ TRACE_EVENT(ufshcd_command,
        ),
 
        TP_fast_assign(
+               __assign_str(dev_name);
                __entry->sdev = sdev;
                __entry->hba = hba;
                __entry->str_t = str_t;
@@ -307,7 +319,7 @@ TRACE_EVENT(ufshcd_command,
        TP_printk(
                "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d",
                show_ufs_cmd_trace_str(__entry->str_t),
-               dev_name(&__entry->sdev->sdev_dev), __entry->tag,
+               __get_str(dev_name), __entry->tag,
                __entry->doorbell, __entry->transfer_len, __entry->intr,
                __entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode),
                (u32)__entry->group_id, __entry->hwq_id
@@ -322,6 +334,7 @@ TRACE_EVENT(ufshcd_uic_command,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __field(enum ufs_trace_str_t, str_t)
                __field(u32, cmd)
                __field(u32, arg1)
@@ -331,6 +344,7 @@ TRACE_EVENT(ufshcd_uic_command,
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __entry->str_t = str_t;
                __entry->cmd = cmd;
                __entry->arg1 = arg1;
@@ -340,7 +354,7 @@ TRACE_EVENT(ufshcd_uic_command,
 
        TP_printk(
                "%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x",
-               show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev),
+               show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name),
                __entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3
        )
 );
@@ -353,6 +367,7 @@ TRACE_EVENT(ufshcd_upiu,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __field(enum ufs_trace_str_t, str_t)
                __array(unsigned char, hdr, 12)
                __array(unsigned char, tsf, 16)
@@ -361,6 +376,7 @@ TRACE_EVENT(ufshcd_upiu,
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __entry->str_t = str_t;
                memcpy(__entry->hdr, hdr, sizeof(__entry->hdr));
                memcpy(__entry->tsf, tsf, sizeof(__entry->tsf));
@@ -369,7 +385,7 @@ TRACE_EVENT(ufshcd_upiu,
 
        TP_printk(
                "%s: %s: HDR:%s, %s:%s",
-               show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev),
+               show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name),
                __print_hex(__entry->hdr, sizeof(__entry->hdr)),
                show_ufs_cmd_trace_tsf(__entry->tsf_t),
                __print_hex(__entry->tsf, sizeof(__entry->tsf))
@@ -384,16 +400,18 @@ TRACE_EVENT(ufshcd_exception_event,
 
        TP_STRUCT__entry(
                __field(struct ufs_hba *, hba)
+               __string(dev_name, dev_name(hba->dev))
                __field(u16, status)
        ),
 
        TP_fast_assign(
                __entry->hba = hba;
+               __assign_str(dev_name);
                __entry->status = status;
        ),
 
        TP_printk("%s: status 0x%x",
-               dev_name(__entry->hba->dev), __entry->status
+               __get_str(dev_name), __entry->status
        )
 );
 
index d610cdcef7d0921444e8be9808f986b2411d1d73..4e71ed679a76121694a5d17119717de29ea9f295 100644 (file)
@@ -594,7 +594,9 @@ static int uea_send_modem_cmd(struct usb_device *usb,
 static void uea_upload_pre_firmware(const struct firmware *fw_entry,
                                                                void *context)
 {
-       struct usb_device *usb = context;
+       struct usb_interface *intf = context;
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct completion *fw_done = usb_get_intfdata(intf);
        const u8 *pfw;
        u8 value;
        u32 crc = 0;
@@ -663,15 +665,17 @@ err_fw_corrupted:
        uea_err(usb, "firmware is corrupted\n");
 err:
        release_firmware(fw_entry);
+       complete(fw_done);
 }
 
 /*
  * uea_load_firmware - Load usb firmware for pre-firmware devices.
  */
-static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
+static int uea_load_firmware(struct usb_interface *intf, unsigned int ver)
 {
        int ret;
        char *fw_name = EAGLE_FIRMWARE;
+       struct usb_device *usb = interface_to_usbdev(intf);
 
        uea_info(usb, "pre-firmware device, uploading firmware\n");
 
@@ -694,7 +698,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
        }
 
        ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
-                                       GFP_KERNEL, usb,
+                                       GFP_KERNEL, intf,
                                        uea_upload_pre_firmware);
        if (ret)
                uea_err(usb, "firmware %s is not available\n", fw_name);
@@ -2555,8 +2559,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        usb_reset_device(usb);
 
-       if (UEA_IS_PREFIRM(id))
-               return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
+       if (UEA_IS_PREFIRM(id)) {
+               struct completion *fw_done;
+
+               /* Wait for the firmware load to be done, in .disconnect() */
+               fw_done = kzalloc_obj(*fw_done);
+               if (!fw_done)
+                       return -ENOMEM;
+
+               init_completion(fw_done);
+               usb_set_intfdata(intf, fw_done);
+
+               ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id));
+               if (ret)
+                       kfree(fw_done);
+
+               return ret;
+       }
 
        ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
        if (ret == 0) {
@@ -2586,6 +2605,13 @@ static void uea_disconnect(struct usb_interface *intf)
                usbatm_usb_disconnect(intf);
                mutex_unlock(&uea_mutex);
                uea_info(usb, "ADSL device removed\n");
+       } else if (usb->config->desc.bNumInterfaces == 1) {
+               struct completion *fw_done = usb_get_intfdata(intf);
+
+               uea_dbg(usb, "pre-firmware device, waiting firmware upload\n");
+               wait_for_completion(fw_done);
+               uea_dbg(usb, "pre-firmware device, finished waiting\n");
+               kfree(fw_done);
        }
 }
 
index 5d8cdc91927dd5fb88a040c9ba7bdff5940ced9f..83f3384b735dfb345dfbc1e99dd7b32e7a4bdeb2 100644 (file)
@@ -631,6 +631,8 @@ cleanup_rings:
                }
        }
 
+       cdnsp_free_stream_ctx(pdev, pep);
+
 cleanup_stream_rings:
        kfree(pep->stream_info.stream_rings);
 
index 49ab02f2587225dece4bd755a14f6c0460ecd202..7bc5329fa3ed39e9702c5c57137068386829cd69 100644 (file)
@@ -1816,6 +1816,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
        .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
        },
+       { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */
+       .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+       },
        { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
index 9b69148128e5bf8f1c0fb69633eb5f0d3d92d41f..7e43429e996e7ab3d181607603ec277f3fe00b0d 100644 (file)
@@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
        ulpi->dev.parent = dev; /* needed early for ops */
        ulpi->dev.bus = &ulpi_bus;
        ulpi->dev.type = &ulpi_dev_type;
+
+       device_initialize(&ulpi->dev);
+
        dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
 
        ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
 
        ret = ulpi_of_register(ulpi);
-       if (ret) {
-               kfree(ulpi);
+       if (ret)
                return ret;
-       }
 
        ret = ulpi_read_id(ulpi);
-       if (ret) {
-               of_node_put(ulpi->dev.of_node);
-               kfree(ulpi);
+       if (ret)
                return ret;
-       }
 
-       ret = device_register(&ulpi->dev);
-       if (ret) {
-               put_device(&ulpi->dev);
+       ret = device_add(&ulpi->dev);
+       if (ret)
                return ret;
-       }
 
        root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
        debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
@@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev,
        ulpi->ops = ops;
 
        ret = ulpi_register(dev, ulpi);
-       if (ret)
+       if (ret) {
+               put_device(&ulpi->dev);
                return ERR_PTR(ret);
-
+       }
 
        return ulpi;
 }
index 24960ba9caa915f12a4f5582269808fdebd1ee11..5262e11c12cd3aa112c6913024b1c6c05bc1440c 100644 (file)
@@ -3148,7 +3148,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                delay = HUB_LONG_RESET_TIME;
        }
 
-       dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
+       dev_err_ratelimited(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
 
 done:
        if (status == 0) {
index 87810eff974ef876b55df274126aa10f33b0c554..87ee2d938bc0386ce444e1ed9f43898f96330b5b 100644 (file)
@@ -296,6 +296,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* CarrolTouch 4500U */
        { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* Samsung T5 EVO Portable SSD */
+       { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM },
+
        /* Samsung Android phone modem - ID conflict with SPH-I500 */
        { USB_DEVICE(0x04e8, 0x6601), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -576,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* VLI disk */
        { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
 
+       /* VIA Labs, Inc. USB2.0 Hub */
+       { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM },
+
        /* Raydium Touchscreen */
        { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
 
index 517aa7f1486da6c2f1bb976c0faa7881a2a3a0c3..ceb49f2f8004188ca30008ae5b7596226485052d 100644 (file)
@@ -789,9 +789,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc)
 
        if (dwc->enable_usb2_transceiver_delay) {
                for (index = 0; index < dwc->num_usb2_ports; index++) {
-                       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
+                       reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
                        reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
-                       dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+                       dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
                }
        }
 }
index 55e144ba8cfc6c30dc28b4e808a3a23d7291c33c..4d611c08e8a4cf09d51fb5b6255f0c5624600e25 100644 (file)
@@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
 
        ret = priv->drvdata->usb_init(priv);
        if (ret)
-               return ret;
+               goto err_rearm;
 
        /* Init PHYs */
        for (i = 0 ; i < PHY_COUNT ; ++i) {
                ret = phy_init(priv->phys[i]);
                if (ret)
-                       return ret;
+                       goto err_rearm;
        }
 
        /* Set PHY Power */
        for (i = 0 ; i < PHY_COUNT ; ++i) {
                ret = phy_power_on(priv->phys[i]);
                if (ret)
-                       return ret;
+                       goto err_rearm;
        }
 
        if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
                ret = regulator_enable(priv->vbus);
                if (ret)
-                       return ret;
+                       goto err_rearm;
        }
 
        if (priv->drvdata->usb_post_init) {
                ret = priv->drvdata->usb_post_init(priv);
                if (ret)
-                       return ret;
+                       goto err_rearm;
        }
 
        return 0;
+
+err_rearm:
+       reset_control_rearm(priv->reset);
+       return ret;
 }
 
 static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
index 3d4ca68e584c7ba5069910aeed985d06a9cca16c..1082e9c9afaa8995fba047da4b53150b6838812b 100644 (file)
@@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
        }
 }
 
+static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc,
+                                          struct usb_gadget_driver **driver,
+                                          struct usb_gadget **gadget)
+{
+       if (!dwc->async_callbacks || !dwc->gadget_driver ||
+           !dwc->gadget_driver->disconnect)
+               return false;
+
+       *driver = dwc->gadget_driver;
+       *gadget = dwc->gadget;
+
+       return true;
+}
+
 static void dwc3_disconnect_gadget(struct dwc3 *dwc)
 {
-       if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
+       struct usb_gadget_driver *driver;
+       struct usb_gadget *gadget;
+
+       if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
                spin_unlock(&dwc->lock);
-               dwc->gadget_driver->disconnect(dwc->gadget);
+               driver->disconnect(gadget);
                spin_lock(&dwc->lock);
        }
 }
 
+static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc)
+{
+       struct usb_gadget_driver *driver;
+       struct usb_gadget *gadget;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
+               spin_unlock_irqrestore(&dwc->lock, flags);
+               return;
+       }
+
+       spin_unlock_irqrestore(&dwc->lock, flags);
+       driver->disconnect(gadget);
+}
+
 static void dwc3_suspend_gadget(struct dwc3 *dwc)
 {
        if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
@@ -4838,7 +4871,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit);
 
 int dwc3_gadget_suspend(struct dwc3 *dwc)
 {
-       unsigned long flags;
        int ret;
 
        ret = dwc3_gadget_soft_disconnect(dwc);
@@ -4852,10 +4884,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
                return -EAGAIN;
        }
 
-       spin_lock_irqsave(&dwc->lock, flags);
-       if (dwc->gadget_driver)
-               dwc3_disconnect_gadget(dwc);
-       spin_unlock_irqrestore(&dwc->lock, flags);
+       dwc3_disconnect_gadget_sleepable(dwc);
 
        return 0;
 }
index 1a48329a4e089629215ed64ad8935016a6229aff..956be5b5651027daaf9e158c19d45da9fcfe65e6 100644 (file)
@@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb,
        return 0;
 
 fail:
-       iso_sched_free(stream, sched);
-       urb->hcpriv = NULL;
        return status;
 }
 
@@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb,
        else
                usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
 done_not_linked:
+       if (status < 0) {
+               iso_sched_free(stream, urb->hcpriv);
+               urb->hcpriv = NULL;
+       }
        spin_unlock_irqrestore(&fotg210->lock, flags);
 done:
        return status;
index dc3664374596351c39f5364845ccd5f4b8558403..df39e3487c1f3b03c7105f8933a7faf201bd6069 100644 (file)
@@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                                if (cdev->config)
                                        config = cdev->config;
                                else
-                                       config = list_first_entry(
+                                       config = list_first_entry_or_null(
                                                        &cdev->configs,
-                                               struct usb_configuration, list);
+                                                       struct usb_configuration,
+                                                       list);
                                if (!config)
                                        goto done;
 
index 75912ce6ab55641c60ea289bcc82f8567cea5c08..44218be1e6768e0d902dd6ebcda9f3313f251a7f 100644 (file)
@@ -288,6 +288,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
 static void ffs_release_dev(struct ffs_dev *ffs_dev);
 static int ffs_ready(struct ffs_data *ffs);
 static void ffs_closed(struct ffs_data *ffs);
+static void ffs_reset_work(struct work_struct *work);
 
 /* Misc helper functions ****************************************************/
 
@@ -1374,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)
 
        mutex_unlock(&epfile->dmabufs_mutex);
 
-       __ffs_epfile_read_buffer_free(epfile);
        ffs_data_closed(epfile->ffs);
 
        return 0;
@@ -1704,6 +1704,7 @@ static int ffs_dmabuf_transfer(struct file *file,
        resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
 
        dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
+       dma_fence_put(&fence->base);
        dma_resv_unlock(dmabuf->resv);
 
        /* Now that the dma_fence is in place, queue the transfer. */
@@ -2221,6 +2222,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
        init_waitqueue_head(&ffs->ev.waitq);
        init_waitqueue_head(&ffs->wait);
        init_completion(&ffs->ep0req_completion);
+       INIT_WORK(&ffs->reset_work, ffs_reset_work);
 
        /* XXX REVISIT need to update it in some places, or do we? */
        ffs->ev.can_stall = 1;
@@ -2364,6 +2366,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
                        sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
                else
                        sprintf(epfile->name, "ep%u", i);
+               epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
                err = ffs_sb_create_file(ffs->sb, epfile->name,
                                         epfile, &ffs_epfile_operations);
                if (err) {
@@ -2389,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb,
 
        for (; count; --count, ++epfile) {
                BUG_ON(mutex_is_locked(&epfile->mutex));
+               __ffs_epfile_read_buffer_free(epfile);
                simple_remove_by_name(root, epfile->name, clear_one);
        }
 
@@ -2453,7 +2457,6 @@ static int ffs_func_eps_enable(struct ffs_function *func)
                ret = usb_ep_enable(ep->ep);
                if (!ret) {
                        epfile->ep = ep;
-                       epfile->in = usb_endpoint_dir_in(ep->ep->desc);
                        epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc);
                } else {
                        break;
@@ -3775,7 +3778,6 @@ static int ffs_func_set_alt(struct usb_function *f,
        if (ffs->state == FFS_DEACTIVATED) {
                ffs->state = FFS_CLOSING;
                spin_unlock_irqrestore(&ffs->eps_lock, flags);
-               INIT_WORK(&ffs->reset_work, ffs_reset_work);
                schedule_work(&ffs->reset_work);
                return -ENODEV;
        }
@@ -3806,7 +3808,6 @@ static void ffs_func_disable(struct usb_function *f)
        if (ffs->state == FFS_DEACTIVATED) {
                ffs->state = FFS_CLOSING;
                spin_unlock_irqrestore(&ffs->eps_lock, flags);
-               INIT_WORK(&ffs->reset_work, ffs_reset_work);
                schedule_work(&ffs->reset_work);
                return;
        }
index e4f7828ae75df7c6dd0a9c2b0ff620460e4485cd..837f753d0cae594b66fbcd51b3ffcc291221c952 100644 (file)
@@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd)
                ret = 0;
                /* Change the printer status to show that it's on-line. */
                dev->printer_status |= PRINTER_SELECTED;
+               kref_get(&dev->kref);
        }
 
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       kref_get(&dev->kref);
-
        return ret;
 }
 
index 3da54a7d7aba857e3e992b749d3bb3f5388a56d8..a2fd239b7ad3331958dbccbdfec090c7fac8236f 100644 (file)
@@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params,
 static int rndis_query_response(struct rndis_params *params,
                                rndis_query_msg_type *buf)
 {
+       u32 BufLength, BufOffset;
        rndis_query_cmplt_type *resp;
        rndis_resp_t *r;
 
@@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params,
        if (!params->dev)
                return -ENOTSUPP;
 
+       BufLength = le32_to_cpu(buf->InformationBufferLength);
+       BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+       if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
+           (BufOffset > RNDIS_MAX_TOTAL_SIZE) ||
+           (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
+               return -EINVAL;
+
        /*
         * we need more memory:
         * gen_ndis_query_resp expects enough space for
@@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params,
        resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
 
        if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
-                       le32_to_cpu(buf->InformationBufferOffset)
-                                       + 8 + (u8 *)buf,
-                       le32_to_cpu(buf->InformationBufferLength),
-                       r)) {
+                               BufOffset + 8 + (u8 *)buf,
+                               BufLength, r)) {
                /* OID not supported */
                resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
                resp->MessageLength = cpu_to_le32(sizeof *resp);
@@ -1074,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port,
        /* tmp points to a struct rndis_packet_msg_type */
        __le32 *tmp = (void *)skb->data;
 
+       /* Need at least MessageType, MessageLength, DataOffset, DataLength */
+       if (skb->len < 16) {
+               dev_kfree_skb_any(skb);
+               return -EINVAL;
+       }
+
        /* MessageType, MessageLength */
        if (cpu_to_le32(RNDIS_MSG_PACKET)
                        != get_unaligned(tmp++)) {
index 60340ff9edbf0aceb35a1ec3e9d60a14f1f9953c..f6da12b553a0eee8faeaddb90aacd33e7a6f4ac0 100644 (file)
@@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type;
 /**
  * struct usb_udc - describes one usb device controller
  * @driver: the gadget driver pointer. For use by the class code
- * @dev: the child device to the actual controller
  * @gadget: the gadget. For use by the class code
+ * @gadget_release: the gadget's release routine
+ * @dev: the child device to the actual controller
  * @list: for use by the udc class driver
  * @vbus: for udcs who care about vbus status, this value is real vbus status;
  * for udcs who do not care about vbus status, this value is always true
@@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type;
 struct usb_udc {
        struct usb_gadget_driver        *driver;
        struct usb_gadget               *gadget;
+       void                            (*gadget_release)(struct device *dev);
        struct device                   dev;
        struct list_head                list;
        bool                            vbus;
@@ -1362,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev)
        dev_vdbg(dev, "%s\n", __func__);
 }
 
+static void usb_gadget_release(struct device *dev)
+{
+       struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+       struct usb_udc *udc = gadget->udc;
+       /* Cache the gadget's release routine to prevent UAF */
+       void (*release)(struct device *dev) = udc->gadget_release;
+
+       put_device(&udc->dev);
+       release(dev);
+}
+
 /**
  * usb_initialize_gadget - initialize a gadget and its embedded struct device
  * @parent: the parent device to this udc. Usually the controller driver's
@@ -1418,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget)
        mutex_init(&udc->connect_lock);
 
        udc->started = false;
+       /*
+        * Align decoupled lifecycles: take a UDC reference to ensure it
+        * remains allocated until the gadget is released, requiring an
+        * override of the gadget's release routine to drop it.
+        */
+       udc->gadget_release = gadget->dev.release;
+       gadget->dev.release = usb_gadget_release;
+       get_device(&udc->dev);
 
        mutex_lock(&udc_lock);
        list_add_tail(&udc->list, &udc_list);
@@ -1462,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget)
        mutex_lock(&udc_lock);
        list_del(&udc->list);
        mutex_unlock(&udc_lock);
+       /*
+        * Revert the override and drop the UDC reference to prevent
+        * leaking the UDC if the gadget was statically allocated.
+        */
+       gadget->dev.release = udc->gadget_release;
+       put_device(&udc->dev);
 
  err_put_udc:
        put_device(&udc->dev);
index a241337c9af86495a972902a61360a36af281361..57d07d1c2dfa6d885198e95100d8dff5f9135169 100644 (file)
@@ -1623,6 +1623,7 @@ iso_stream_schedule(
                        status = 1;     /* and give it back immediately */
                        iso_sched_free(stream, sched);
                        sched = NULL;
+                       urb->hcpriv = NULL;
                }
        }
        urb->error_count = skip / period;
@@ -1653,8 +1654,6 @@ iso_stream_schedule(
        return status;
 
  fail:
-       iso_sched_free(stream, sched);
-       urb->hcpriv = NULL;
        return status;
 }
 
@@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb,
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
        }
  done_not_linked:
+       if (status < 0) {
+               iso_sched_free(stream, urb->hcpriv);
+               urb->hcpriv = NULL;
+       }
        spin_unlock_irqrestore(&ehci->lock, flags);
  done:
        return status;
@@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb,
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
        }
  done_not_linked:
+       if (status < 0) {
+               iso_sched_free(stream, urb->hcpriv);
+               urb->hcpriv = NULL;
+       }
        spin_unlock_irqrestore(&ehci->lock, flags);
  done:
        return status;
index 4ae47edd4b8b4ece1a1de6229539086ac2f2165a..b044977f6f5673492fe68d452cf5744928d3f7e7 100644 (file)
@@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev)
 
        remove_debug_file(sl811);
        usb_remove_hcd(hcd);
+       device_wakeup_disable(hcd->self.controller);
 
        /* some platforms may use IORESOURCE_IO */
        res = platform_get_resource(dev, IORESOURCE_MEM, 1);
index b1cabf5582fa1e6e7d55d0d0056946839e4da1cf..48ee6a4f9e1c0757ec3a15fd6e47df6d57883dd4 100644 (file)
@@ -646,6 +646,31 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
 
 static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state)
 {
+       if (dbc->state == new_state)
+               return;
+
+       switch (new_state) {
+       case DS_ENABLED:
+               /*
+                * DbC pm usage is 1 here, both when moved from disconnect or
+                * configured states, or when setting initial DbC enable state.
+                * Just enable pending put
+                */
+               dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n");
+               dbc->pending_rpm_put = 1;
+               break;
+       case DS_CONNECTED:
+               if (dbc->pending_rpm_put)
+                       /* DbC pm usage still 1, just remove pending put */
+                       dbc->pending_rpm_put = 0;
+               else
+                       /* DbC pm usage was put to 0, call get */
+                       pm_runtime_get(dbc->dev);
+               break;
+       default:
+               break;
+       }
+
        dbc->state_timestamp = jiffies;
        dbc->state = new_state;
 }
@@ -681,7 +706,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
 
        WARN_ON(!dbc);
 
-       pm_runtime_get_sync(dbc->dev); /* note this was self.controller */
+       pm_runtime_get(dbc->dev);
 
        spin_lock_irqsave(&dbc->lock, flags);
        ret = xhci_do_dbc_start(dbc);
@@ -706,6 +731,7 @@ err_unlock:
 static void xhci_dbc_stop(struct xhci_dbc *dbc)
 {
        unsigned long           flags;
+       bool                    need_rpm_put = false;
 
        WARN_ON(!dbc);
 
@@ -731,12 +757,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
 
        spin_lock_irqsave(&dbc->lock, flags);
        writel(0, &dbc->regs->control);
+
+       if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED ||
+           dbc->pending_rpm_put)
+               need_rpm_put = true;
+
+       dbc->pending_rpm_put = 0;
+
        xhci_dbc_set_state(dbc, DS_DISABLED);
        spin_unlock_irqrestore(&dbc->lock, flags);
 
        xhci_dbc_mem_cleanup(dbc);
 
-       pm_runtime_put(dbc->dev); /* note, was self.controller */
+       if (need_rpm_put)
+               pm_runtime_put(dbc->dev);
 }
 
 static void
@@ -908,6 +942,12 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
                        dev_info(dbc->dev, "DbC connected\n");
                } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) {
                        dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n");
+               } else if (dbc->pending_rpm_put &&
+                          time_is_before_jiffies(dbc->state_timestamp +
+                               msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) {
+                       dbc->pending_rpm_put = 0;
+                       dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n");
+                       pm_runtime_put(dbc->dev);
                }
 
                return EVT_DONE;
@@ -1096,6 +1136,9 @@ static ssize_t dbc_show(struct device *dev,
        if (dbc->state >= ARRAY_SIZE(dbc_state_strings))
                return sysfs_emit(buf, "unknown\n");
 
+       if (dbc->resume_required)
+               return sysfs_emit(buf, "suspended\n");
+
        return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]);
 }
 
@@ -1110,12 +1153,25 @@ static ssize_t dbc_store(struct device *dev,
        dbc = xhci->dbc;
 
        if (sysfs_streq(buf, "enable")) {
+               pm_runtime_get_sync(dbc->dev);
+
                mutex_lock(&dbc->enable_mutex);
+               /*
+                * DbC may already be enabled here if xhci was suspended with
+                * dbc->resume_required set, and resumed by pm_runtime_get_sync()
+                * above. In this case we end up calling xhci_dbc_start() twice,
+                * second time returns an error but is harmless
+                */
                xhci_dbc_start(dbc);
+
                mutex_unlock(&dbc->enable_mutex);
+               pm_runtime_put(dbc->dev);
        } else if (sysfs_streq(buf, "disable")) {
                mutex_lock(&dbc->enable_mutex);
+
+               dbc->resume_required = 0;
                xhci_dbc_stop(dbc);
+
                mutex_unlock(&dbc->enable_mutex);
        } else {
                return -EINVAL;
index df7aca8bfe992c003616250c7d83c65173c02153..5b18efb2c1eab58cb3ce5f062ef1e45373e963d0 100644 (file)
@@ -114,6 +114,8 @@ struct dbc_ep {
 #define DBC_POLL_INTERVAL_MAX          5000    /* milliseconds */
 #define DBC_XFER_INACTIVITY_TIMEOUT    10      /* milliseconds */
 #define DBC_ENUMERATION_TIMEOUT                2000    /* milliseconds */
+#define DBC_AUTOSUSPEND_DELAY          15000   /* milliseconds */
+
 /*
  * Private structure for DbC hardware state:
  */
@@ -166,6 +168,7 @@ struct xhci_dbc {
        unsigned long                   xfer_timestamp;
        unsigned long                   state_timestamp;
        unsigned                        resume_required:1;
+       unsigned                        pending_rpm_put:1;
        struct dbc_ep                   eps[2];
 
        const struct dbc_driver         *driver;
index 23153e136d4b842b7740453d07bf9b6a713b7d7e..a5deeee4d5dce4e568194d356dae529ec9d54a0c 100644 (file)
@@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
        if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
                goto err;
 
+       kvfree(pages);
+
        /*
         * Save first segment dma address to sg dma_address field for the sideband
         * client to have access to the IOVA of the ring.
index 6922cc5496c18257928965808dd911cda9bdbec0..f44ccee5fa07882d87c0efd40233c39feb543c40 100644 (file)
@@ -3785,6 +3785,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
        struct xhci_virt_device *vdev;
        struct xhci_command *command;
        struct xhci_input_control_ctx *ctrl_ctx;
+       struct xhci_stream_info *stream_info[EP_CTX_PER_DEV];
        unsigned int ep_index;
        unsigned long flags;
        u32 changed_ep_bitmask;
@@ -3845,10 +3846,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
        if (ret < 0)
                return ret;
 
+       /*
+        * dma_free_coherent() called by xhci_free_stream_info() may sleep,
+        * so save stream_info pointers and clear references under lock,
+        * then free the memory outside lock.
+        */
        spin_lock_irqsave(&xhci->lock, flags);
        for (i = 0; i < num_eps; i++) {
                ep_index = xhci_get_endpoint_index(&eps[i]->desc);
-               xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
+               stream_info[i] = vdev->eps[ep_index].stream_info;
                vdev->eps[ep_index].stream_info = NULL;
                /* FIXME Unset maxPstreams in endpoint context and
                 * update deq ptr to point to normal string ring.
@@ -3858,6 +3864,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
        }
        spin_unlock_irqrestore(&xhci->lock, flags);
 
+       for (i = 0; i < num_eps; i++)
+               xhci_free_stream_info(xhci, stream_info[i]);
+
        return 0;
 }
 
index d8016540953fcd4f560c5d33731349b983801570..9c06f7775301f588b034c14a29d653db069a798d 100644 (file)
@@ -320,7 +320,6 @@ bail:
        mutex_unlock(&dev->lock);
 destruction:
        mutex_unlock(&chaoskey_list_lock);
-       usb_dbg(interface, "release success");
        return rv;
 }
 
index 0f6b3464c2d6a81f52aba31f8d82ea8ed79e2eeb..3e37adf2bb5734db10516b097e176f4ff6de8a56 100644 (file)
@@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table);
 
 /* structure to hold all of our device specific stuff */
 struct usb_idmouse {
+       struct kref kref;
 
        struct usb_device *udev; /* save off the usb device pointer */
        struct usb_interface *interface; /* the interface for this device */
@@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf)
        return 0;
 }
 
-static inline void idmouse_delete(struct usb_idmouse *dev)
+static inline void idmouse_delete(struct kref *kref)
 {
+       struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref);
+
        kfree(dev->bulk_in_buffer);
        kfree(dev);
 }
@@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file)
                /* increment our usage count for the driver */
                ++dev->open;
 
+               kref_get(&dev->kref);
+
                /* save our object in the file's private structure */
                file->private_data = dev;
 
@@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file)
 
        /* lock our device */
        mutex_lock(&dev->lock);
-
        --dev->open;
+       mutex_unlock(&dev->lock);
+
+       kref_put(&dev->kref, idmouse_delete);
 
-       if (!dev->present) {
-               /* the device was unplugged before the file was released */
-               mutex_unlock(&dev->lock);
-               idmouse_delete(dev);
-       } else {
-               mutex_unlock(&dev->lock);
-       }
        return 0;
 }
 
@@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface,
        if (dev == NULL)
                return -ENOMEM;
 
+       kref_init(&dev->kref);
        mutex_init(&dev->lock);
        dev->udev = udev;
        dev->interface = interface;
@@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface,
        result = usb_find_bulk_in_endpoint(iface_desc, &endpoint);
        if (result) {
                dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n");
-               idmouse_delete(dev);
-               return result;
+               goto err_put_kref;
        }
 
        dev->orig_bi_size = usb_endpoint_maxp(endpoint);
@@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface,
        dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
        dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
        if (!dev->bulk_in_buffer) {
-               idmouse_delete(dev);
-               return -ENOMEM;
+               result = -ENOMEM;
+               goto err_put_kref;
        }
 
        /* allow device read, write and ioctl */
@@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface,
        if (result) {
                /* something prevented us from registering this device */
                dev_err(&interface->dev, "Unable to allocate minor number.\n");
-               idmouse_delete(dev);
-               return result;
+               goto err_put_kref;
        }
 
        /* be noisy */
        dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
 
        return 0;
+
+err_put_kref:
+       kref_put(&dev->kref, idmouse_delete);
+
+       return result;
 }
 
 static void idmouse_disconnect(struct usb_interface *interface)
@@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface)
        /* prevent device read, write and ioctl */
        dev->present = 0;
 
-       /* if the device is opened, idmouse_release will clean this up */
-       if (!dev->open) {
-               mutex_unlock(&dev->lock);
-               idmouse_delete(dev);
-       } else {
-               /* unlock */
-               mutex_unlock(&dev->lock);
-       }
+       mutex_unlock(&dev->lock);
+
+       kref_put(&dev->kref, idmouse_delete);
 
        dev_info(&interface->dev, "disconnected\n");
 }
index 22504c0a28416fbee1700d8ea91caea83aca8073..de2b236ef9030d8be15d4154b55a9f7dab7c0f7f 100644 (file)
@@ -72,6 +72,7 @@ static struct usb_driver iowarrior_driver;
 
 /* Structure to hold all of our device specific stuff */
 struct iowarrior {
+       struct kref kref;
        struct mutex mutex;                     /* locks this structure */
        struct usb_device *udev;                /* save off the usb device pointer */
        struct usb_interface *interface;        /* the interface for this device */
@@ -240,8 +241,10 @@ static void iowarrior_write_callback(struct urb *urb)
 /*
  *     iowarrior_delete
  */
-static inline void iowarrior_delete(struct iowarrior *dev)
+static inline void iowarrior_delete(struct kref *kref)
 {
+       struct iowarrior *dev = container_of(kref, struct iowarrior, kref);
+
        kfree(dev->int_in_buffer);
        usb_free_urb(dev->int_in_urb);
        kfree(dev->read_queue);
@@ -637,6 +640,9 @@ static int iowarrior_open(struct inode *inode, struct file *file)
        }
        /* increment our usage count for the driver */
        ++dev->opened;
+
+       kref_get(&dev->kref);
+
        /* save our object in the file's private structure */
        file->private_data = dev;
        retval = 0;
@@ -652,7 +658,6 @@ out:
 static int iowarrior_release(struct inode *inode, struct file *file)
 {
        struct iowarrior *dev;
-       int retval = 0;
 
        dev = file->private_data;
        if (!dev)
@@ -660,29 +665,18 @@ static int iowarrior_release(struct inode *inode, struct file *file)
 
        /* lock our device */
        mutex_lock(&dev->mutex);
+       dev->opened = 0;        /* we're closing now */
 
-       if (dev->opened <= 0) {
-               retval = -ENODEV;       /* close called more than once */
-               mutex_unlock(&dev->mutex);
-       } else {
-               dev->opened = 0;        /* we're closing now */
-               retval = 0;
-               if (dev->present) {
-                       /*
-                          The device is still connected so we only shutdown
-                          pending read-/write-ops.
-                        */
-                       usb_kill_urb(dev->int_in_urb);
-                       wake_up_interruptible(&dev->read_wait);
-                       wake_up_interruptible(&dev->write_wait);
-                       mutex_unlock(&dev->mutex);
-               } else {
-                       /* The device was unplugged, cleanup resources */
-                       mutex_unlock(&dev->mutex);
-                       iowarrior_delete(dev);
-               }
+       if (dev->present) {
+               usb_kill_urb(dev->int_in_urb);
+               wake_up_interruptible(&dev->read_wait);
+               wake_up_interruptible(&dev->write_wait);
        }
-       return retval;
+       mutex_unlock(&dev->mutex);
+
+       kref_put(&dev->kref, iowarrior_delete);
+
+       return 0;
 }
 
 static __poll_t iowarrior_poll(struct file *file, poll_table * wait)
@@ -767,6 +761,7 @@ static int iowarrior_probe(struct usb_interface *interface,
        if (!dev)
                return retval;
 
+       kref_init(&dev->kref);
        mutex_init(&dev->mutex);
 
        atomic_set(&dev->intr_idx, 0);
@@ -885,7 +880,8 @@ static int iowarrior_probe(struct usb_interface *interface,
        return retval;
 
 error:
-       iowarrior_delete(dev);
+       kref_put(&dev->kref, iowarrior_delete);
+
        return retval;
 }
 
@@ -905,21 +901,18 @@ static void iowarrior_disconnect(struct usb_interface *interface)
        /* prevent device read, write and ioctl */
        dev->present = 0;
 
+       /* write urbs are not stopped on close() so kill unconditionally */
+       usb_kill_anchored_urbs(&dev->submitted);
+
        if (dev->opened) {
-               /* There is a process that holds a filedescriptor to the device ,
-                  so we only shutdown read-/write-ops going on.
-                  Deleting the device is postponed until close() was called.
-                */
                usb_kill_urb(dev->int_in_urb);
-               usb_kill_anchored_urbs(&dev->submitted);
                wake_up_interruptible(&dev->read_wait);
                wake_up_interruptible(&dev->write_wait);
-               mutex_unlock(&dev->mutex);
-       } else {
-               /* no process is using the device, cleanup now */
-               mutex_unlock(&dev->mutex);
-               iowarrior_delete(dev);
        }
+
+       mutex_unlock(&dev->mutex);
+
+       kref_put(&dev->kref, iowarrior_delete);
 }
 
 /* usb specific object needed to register this driver with the usb subsystem */
index c74f142f6637a8e1149de9d4562b172d39cc9c5b..71132a15e77111d3cf4ad83312978b9a499feaf2 100644 (file)
@@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in
 
 /* Structure to hold all of our device specific stuff */
 struct ld_usb {
+       struct kref             kref;
        struct mutex            mutex;          /* locks this structure */
        struct usb_interface    *intf;          /* save off the usb interface pointer */
        unsigned long           disconnected:1;
@@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev)
 /*
  *     ld_usb_delete
  */
-static void ld_usb_delete(struct ld_usb *dev)
+static void ld_usb_delete(struct kref *kref)
 {
+       struct ld_usb *dev = container_of(kref, struct ld_usb, kref);
+
        /* free data structures */
        usb_free_urb(dev->interrupt_in_urb);
        usb_free_urb(dev->interrupt_out_urb);
@@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file)
                goto unlock_exit;
        }
 
+       kref_get(&dev->kref);
+
        /* save device in the file's private structure */
        file->private_data = dev;
 
@@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file)
 
        mutex_lock(&dev->mutex);
 
-       if (dev->open_count != 1) {
-               retval = -ENODEV;
+       if (dev->disconnected)
                goto unlock_exit;
-       }
-       if (dev->disconnected) {
-               /* the device was unplugged before the file was released */
-               mutex_unlock(&dev->mutex);
-               /* unlock here as ld_usb_delete frees dev */
-               ld_usb_delete(dev);
-               goto exit;
-       }
 
        /* wait until write transfer is finished */
        if (dev->interrupt_out_busy)
@@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file)
 
 unlock_exit:
        mutex_unlock(&dev->mutex);
-
+       kref_put(&dev->kref, ld_usb_delete);
 exit:
        return retval;
 }
@@ -659,6 +655,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
        dev = kzalloc_obj(*dev);
        if (!dev)
                goto exit;
+
+       kref_init(&dev->kref);
        mutex_init(&dev->mutex);
        spin_lock_init(&dev->rbsl);
        dev->intf = intf;
@@ -740,7 +738,7 @@ exit:
        return retval;
 
 error:
-       ld_usb_delete(dev);
+       kref_put(&dev->kref, ld_usb_delete);
 
        return retval;
 }
@@ -768,18 +766,18 @@ static void ld_usb_disconnect(struct usb_interface *intf)
 
        mutex_lock(&dev->mutex);
 
-       /* if the device is not opened, then we clean up right now */
-       if (!dev->open_count) {
-               mutex_unlock(&dev->mutex);
-               ld_usb_delete(dev);
-       } else {
-               dev->disconnected = 1;
+       dev->disconnected = 1;
+
+       if (dev->open_count) {
                /* wake up pollers */
                wake_up_interruptible_all(&dev->read_wait);
                wake_up_interruptible_all(&dev->write_wait);
-               mutex_unlock(&dev->mutex);
        }
 
+       mutex_unlock(&dev->mutex);
+
+       kref_put(&dev->kref, ld_usb_delete);
+
        dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
                 (minor - USB_LD_MINOR_BASE));
 }
index 052ffc2e71eea49474ccdcf958f6586fc56b3332..18dd4115befb2f84e5254ecfd1ddbf60874f5ace 100644 (file)
@@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table);
 
 /* Structure to hold all of our device specific stuff */
 struct lego_usb_tower {
+       struct kref             kref;
        struct mutex            lock;           /* locks this structure */
        struct usb_device       *udev;          /* save off the usb device pointer */
        unsigned char           minor;          /* the starting minor number for this device */
@@ -220,7 +221,6 @@ struct lego_usb_tower {
 /* local function prototypes */
 static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos);
 static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
-static inline void tower_delete(struct lego_usb_tower *dev);
 static int tower_open(struct inode *inode, struct file *file);
 static int tower_release(struct inode *inode, struct file *file);
 static __poll_t tower_poll(struct file *file, poll_table *wait);
@@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev,
 /*
  *     tower_delete
  */
-static inline void tower_delete(struct lego_usb_tower *dev)
+static inline void tower_delete(struct kref *kref)
 {
+       struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref);
+
        /* free data structures */
        usb_free_urb(dev->interrupt_in_urb);
        usb_free_urb(dev->interrupt_out_urb);
@@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file)
 
        dev->open_count = 1;
 
+       kref_get(&dev->kref);
+
 unlock_exit:
        mutex_unlock(&dev->lock);
 
@@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file)
 
        mutex_lock(&dev->lock);
 
-       if (dev->disconnected) {
-               /* the device was unplugged before the file was released */
-
-               /* unlock here as tower_delete frees dev */
-               mutex_unlock(&dev->lock);
-               tower_delete(dev);
-               goto exit;
-       }
+       if (dev->disconnected)
+               goto out_unlock;
 
        /* wait until write transfer is finished */
        if (dev->interrupt_out_busy) {
@@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file)
 
        dev->open_count = 0;
 
+out_unlock:
        mutex_unlock(&dev->lock);
+       kref_put(&dev->kref, tower_delete);
 exit:
        return retval;
 }
@@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_
        if (!dev)
                goto exit;
 
+       kref_init(&dev->kref);
        mutex_init(&dev->lock);
        dev->udev = usb_get_dev(udev);
        spin_lock_init(&dev->read_buffer_lock);
@@ -828,7 +829,7 @@ exit:
        return retval;
 
 error:
-       tower_delete(dev);
+       kref_put(&dev->kref, tower_delete);
        return retval;
 }
 
@@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface)
 
        mutex_lock(&dev->lock);
 
-       /* if the device is not opened, then we clean up right now */
-       if (!dev->open_count) {
-               mutex_unlock(&dev->lock);
-               tower_delete(dev);
-       } else {
-               dev->disconnected = 1;
+       dev->disconnected = 1;
+
+       if (dev->open_count) {
                /* wake up pollers */
                wake_up_interruptible_all(&dev->read_wait);
                wake_up_interruptible_all(&dev->write_wait);
-               mutex_unlock(&dev->lock);
        }
 
+       mutex_unlock(&dev->lock);
+
+       kref_put(&dev->kref, tower_delete);
+
        dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n",
                 (minor - LEGO_USB_TOWER_MINOR_BASE));
 }
index 02d1e0760f0c45cf1125afee88a54645154c9d46..3c2474dca8101c3e603a47473234cf2fc38489c2 100644 (file)
@@ -344,6 +344,10 @@ read:
        if (ibuf_len < bpkt_len)
                return -ENOSPC;
 
+       /* The device must not claim more payload than it actually sent. */
+       if (bpkt_len > act - sizeof(*bpkt))
+               return -EPROTO;
+
        memcpy(ibuf, bpkt->data, bpkt_len);
 
        return bpkt_len;
@@ -518,7 +522,7 @@ static int usbio_resume(struct usb_interface *intf)
 static void usbio_disconnect(struct usb_interface *intf)
 {
        struct usbio_device *usbio = usb_get_intfdata(intf);
-       struct usbio_client *client;
+       struct usbio_client *client, *next;
 
        /* Wakeup any clients waiting for a reply */
        usbio->rxdat_len = 0;
@@ -535,7 +539,7 @@ static void usbio_disconnect(struct usb_interface *intf)
        usb_kill_urb(usbio->urb);
        usb_free_urb(usbio->urb);
 
-       list_for_each_entry_reverse(client, &usbio->cli_list, link) {
+       list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) {
                auxiliary_device_delete(&client->auxdev);
                auxiliary_device_uninit(&client->auxdev);
        }
index b7d3c44b970e95ec75799b8711ed937af404b4cf..1ce48f5832d719d37cd0f4795d58b2f55b9691b7 100644 (file)
@@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf,
         * here. */
        ret = get_1284_register(pp, 0, &reg, GFP_KERNEL);
        dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
-       if (ret < 0)
+       if (ret < 0) {
+               priv->pp = NULL;
+               parport_del_port(pp);
                goto probe_abort;
+       }
 
        ret = usb_find_last_int_in_endpoint(interface, &epd);
        if (!ret) {
index da29f467943f0d6c2c0c79aa14235045935ad505..f224f2ee379abede867082817c4ce8c5ff5c0b65 100644 (file)
@@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep,
 
        if (mtu3_prepare_transfer(mep)) {
                ret = -EAGAIN;
+               usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
                goto error;
        }
 
index 6899aebfd6ae9440530263629935ccea2e8f8d39..dea039163661b2e41e8b453cad8c740a1f094da3 100644 (file)
@@ -392,12 +392,14 @@ static int digi_write_oob_command(struct usb_serial_port *port,
                        len &= ~3;
                memcpy(oob_port->write_urb->transfer_buffer, buf, len);
                oob_port->write_urb->transfer_buffer_length = len;
+
                ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
-               if (ret == 0) {
-                       oob_priv->dp_write_urb_in_use = 1;
-                       count -= len;
-                       buf += len;
-               }
+               if (ret)
+                       break;
+
+               oob_priv->dp_write_urb_in_use = 1;
+               count -= len;
+               buf += len;
        }
        spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
        if (ret)
@@ -427,20 +429,22 @@ static int digi_write_inb_command(struct usb_serial_port *port,
        int len;
        struct digi_port *priv = usb_get_serial_port_data(port);
        unsigned char *data = port->write_urb->transfer_buffer;
+       unsigned long expire;
        unsigned long flags;
 
        dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
                priv->dp_port_num, count);
 
        if (timeout)
-               timeout += jiffies;
-       else
-               timeout = ULONG_MAX;
+               expire = jiffies + timeout;
 
        spin_lock_irqsave(&priv->dp_port_lock, flags);
        while (count > 0 && ret == 0) {
-               while (priv->dp_write_urb_in_use &&
-                      time_before(jiffies, timeout)) {
+               while (priv->dp_write_urb_in_use) {
+                       if (timeout && time_after(jiffies, expire)) {
+                               ret = -ETIMEDOUT;
+                               break;
+                       }
                        cond_wait_interruptible_timeout_irqrestore(
                                &priv->write_wait, DIGI_RETRY_TIMEOUT,
                                &priv->dp_port_lock, flags);
@@ -449,6 +453,9 @@ static int digi_write_inb_command(struct usb_serial_port *port,
                        spin_lock_irqsave(&priv->dp_port_lock, flags);
                }
 
+               if (ret)
+                       break;
+
                /* len must be a multiple of 4 and small enough to */
                /* guarantee the write will send buffered data first, */
                /* so commands are in order with data and not split */
@@ -1069,6 +1076,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
        unsigned char buf[32];
        struct digi_port *priv = usb_get_serial_port_data(port);
        struct ktermios not_termios;
+       int throttled;
 
        /* be sure the device is started up */
        if (digi_startup_device(port->serial) != 0)
@@ -1096,6 +1104,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
                not_termios.c_iflag = ~tty->termios.c_iflag;
                digi_set_termios(tty, port, &not_termios);
        }
+
+       spin_lock_irq(&priv->dp_port_lock);
+       throttled = priv->dp_throttle_restart;
+       priv->dp_throttled = 0;
+       priv->dp_throttle_restart = 0;
+       spin_unlock_irq(&priv->dp_port_lock);
+
+       if (throttled) {
+               ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
+               if (ret) {
+                       dev_err(&port->dev, "failed to submit read urb: %d\n", ret);
+                       return ret;
+               }
+       }
+
        return 0;
 }
 
index 3b99f9676c35200f65fd63c354bf5dba9bd7fe6d..f05bcce6060029e52f72abf01aa6224f52b3e7fd 100644 (file)
@@ -516,7 +516,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port)
        if (count == room)
                schedule_work(&priv->unthrottle_work);
 
-       return count;
+       return 0;
 }
 
 static void keyspan_pda_write_bulk_callback(struct urb *urb)
index 4c4009b8a46d77dddad09b4ff7483c2cd1488449..7275f4e7f569e468299200bbb168e4fc8aadd3a6 100644 (file)
@@ -1325,6 +1325,22 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30),     /* Telit FE990D50 (RNDIS) */
+         .driver_info = NCTRL(6) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30),     /* Telit FE990D50 (rmnet) */
+         .driver_info = NCTRL(5) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30),     /* Telit FE990D50 (MBIM) */
+         .driver_info = NCTRL(6) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30),     /* Telit FE990D50 (ECM) */
+         .driver_info = NCTRL(6) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) },
        { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff),    /* Telit LE910C1-EUX */
         .driver_info = NCTRL(0) | RSVD(3) },
        { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff),    /* Telit LE910C1-EUX (ECM) */
index 8770de01a384ddb3dccc98565fc36e1584b5e7a6..ed49a3bc859ce5f039d212874dcadb755e2ab462 100644 (file)
@@ -2305,7 +2305,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us)
 
        /*US_DEBUG(usb_stor_show_command(us, srb)); */
        scsi_set_resid(srb, 0);
-       if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready)))
+       if (unlikely(!(info->SD_Status & SD_Ready) &&
+                    !(info->MS_Status & MS_Ready)))
                result = ene_init(us);
        if (result == USB_STOR_XFER_GOOD) {
                result = USB_STOR_TRANSPORT_ERROR;
index fa83fe0defe2dacf13d37919ca5e91b696d2ad69..064c7fc8e368a832e5f43079d212ff4f83d2dcf8 100644 (file)
@@ -570,7 +570,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags)
                        US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
                        US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
                        US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS |
-                       US_FL_ALWAYS_SYNC);
+                       US_FL_ALWAYS_SYNC | US_FL_NO_SAME);
 
        p = quirks;
        while (*p) {
index 604868ebf4221ffb727bbe9466b49c0580a7beec..41df115912b93f5146595d5316710e37b93721fc 100644 (file)
@@ -1537,7 +1537,9 @@ static int anx7411_i2c_probe(struct i2c_client *client)
        if (anx7411_typec_check_connection(plat))
                dev_err(dev, "check status\n");
 
-       pm_runtime_enable(dev);
+       ret = devm_pm_runtime_enable(dev);
+       if (ret)
+               goto free_wq;
 
        return 0;
 
index 0977581ad1b6e971764286762d2ea620deabe13d..0595e8cb83aa6765429a9255d42b1fa4214ba925 100644 (file)
@@ -1619,6 +1619,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
                return -EINVAL;
 
        ret = port->ops->pd_set(port, pd);
+       put_device(&pd->dev);
        if (ret)
                return ret;
 
index db5e4a4c0a99699c4c0f715d281cdc85f1e68c77..9b908c46bd7df9fe78ac0a5eeeb9b03d6587ba80 100644 (file)
@@ -275,9 +275,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode)
 static void *typec_mux_match(const struct fwnode_handle *fwnode,
                             const char *id, void *data)
 {
-       struct typec_mux_dev **mux_devs = data;
        struct device *dev;
-       int i;
 
        /*
         * Device graph (OF graph) does not give any means to identify the
@@ -293,14 +291,6 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode,
        dev = class_find_device(&typec_mux_class, NULL, fwnode,
                                mux_fwnode_match);
 
-       /* Skip duplicates */
-       for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
-               if (to_typec_mux_dev(dev) == mux_devs[i]) {
-                       put_device(dev);
-                       return NULL;
-               }
-
-
        return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
 }
 
@@ -326,8 +316,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode)
                return ERR_PTR(-ENOMEM);
 
        count = fwnode_connection_find_matches(fwnode, "mode-switch",
-                                              (void **)mux_devs,
-                                              typec_mux_match,
+                                              NULL, typec_mux_match,
                                               (void **)mux_devs,
                                               ARRAY_SIZE(mux_devs));
        if (count <= 0) {
index f52443638ee28060c7d20687753193d6321d486b..64e0a61b776a183971403525780d150402a668a0 100644 (file)
@@ -206,12 +206,12 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state
                                CONN_STATUS_1_DP_HPD_LEVEL;
 
                        switch (state->mode)  {
+                       case TYPEC_DP_STATE_D:
+                               cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
+                               fallthrough;
                        case TYPEC_DP_STATE_C:
                                cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED |
                                        CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D;
-                               fallthrough;
-                       case TYPEC_DP_STATE_D:
-                               cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED;
                                break;
                        default: /* MODE_E */
                                break;
index 9d3b1fcf7e27b9f6922fe2ae93a7c84eaa9b823c..af8356df6b98fa77737a9c537bf61a4daaddb848 100644 (file)
@@ -294,6 +294,8 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip)
        return 0;
 }
 
+static void rt1711h_unregister_tcpci_port(void *tcpci);
+
 static int rt1711h_probe(struct i2c_client *client)
 {
        int ret;
@@ -339,6 +341,10 @@ static int rt1711h_probe(struct i2c_client *client)
        if (IS_ERR_OR_NULL(chip->tcpci))
                return PTR_ERR(chip->tcpci);
 
+       ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci);
+       if (ret)
+               return ret;
+
        ret = devm_request_threaded_irq(chip->dev, client->irq, NULL,
                                        rt1711h_irq,
                                        IRQF_ONESHOT | IRQF_TRIGGER_LOW,
@@ -356,11 +362,9 @@ static int rt1711h_probe(struct i2c_client *client)
        return 0;
 }
 
-static void rt1711h_remove(struct i2c_client *client)
+static void rt1711h_unregister_tcpci_port(void *tcpci)
 {
-       struct rt1711h_chip *chip = i2c_get_clientdata(client);
-
-       tcpci_unregister_port(chip->tcpci);
+       tcpci_unregister_port(tcpci);
 }
 
 static const struct rt1711h_chip_info rt1711h = {
@@ -393,7 +397,6 @@ static struct i2c_driver rt1711h_i2c_driver = {
                .of_match_table = rt1711h_of_match,
        },
        .probe = rt1711h_probe,
-       .remove = rt1711h_remove,
        .id_table = rt1711h_id,
 };
 module_i2c_driver(rt1711h_i2c_driver);
index 7ef746a90a17746ff892e94e1049d4f1a8712776..89eec20a2064ce921ff404489676589f78653295 100644 (file)
@@ -2000,6 +2000,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
                return;
        }
 
+       if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) {
+               tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index);
+               return;
+       }
+
        for (i = 1; i < cnt; i++) {
                if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) {
                        /* Already logged in svdm_consume_svids() */
@@ -3088,7 +3093,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
        if (svdm_version < 0)
                return svdm_version;
 
-       header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
+       header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE);
        header |= VDO_OPOS(altmode->mode);
 
        return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP);
@@ -3136,7 +3141,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl
        if (svdm_version < 0)
                return svdm_version;
 
-       header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
+       header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE);
        header |= VDO_OPOS(altmode->mode);
 
        return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME);
index 67a0991a7b76907aa483c4ce0e811af0a762f7ea..7067f2561b8465c4bfbb49bb03298103579701db 100644 (file)
@@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp)
         * that Multi-function is preferred.
         */
        if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) {
-               dp->data.status |= DP_STATUS_CON_UFP_D;
+               dp->data.status |= DP_STATUS_CON_DFP_D;
 
                if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D))
                        dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC;
        } else {
-               dp->data.status |= DP_STATUS_CON_DFP_D;
+               dp->data.status |= DP_STATUS_CON_UFP_D;
 
                if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D))
                        dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC;
@@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp)
 
 static int ucsi_displayport_configure(struct ucsi_dp *dp)
 {
-       u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
        u64 command;
 
        if (!dp->override)
                return 0;
 
-       command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
+       command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf);
 
        return ucsi_send_command(dp->con->ucsi, command, NULL, 0);
 }
index 92166a3725b166af20ae062488c70cee64cca69c..6a6723e8fb127fb9969f70475179c4bbfd3ecd51 100644 (file)
@@ -2017,6 +2017,26 @@ static void ucsi_resume_work(struct work_struct *work)
        }
 }
 
+int ucsi_suspend(struct ucsi *ucsi)
+{
+       int i;
+
+       /*
+        * Cancel pending work so it cannot access the firmware after the ACPI
+        * EC is stopped for suspend; state is re-read on resume.
+        */
+       cancel_delayed_work_sync(&ucsi->work);
+
+       if (!ucsi->connector)
+               return 0;
+
+       for (i = 0; i < ucsi->cap.num_connectors; i++)
+               cancel_work_sync(&ucsi->connector[i].work);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ucsi_suspend);
+
 int ucsi_resume(struct ucsi *ucsi)
 {
        if (ucsi->connector)
index 325ed1e5ca804c8562f507c77339ebb24aa04831..6e1608d88ec3d323862f94315e56ed768a197d16 100644 (file)
@@ -582,6 +582,7 @@ int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command,
                                   void *msg_out, size_t msg_out_size);
 
 void ucsi_altmode_update_active(struct ucsi_connector *con);
+int ucsi_suspend(struct ucsi *ucsi);
 int ucsi_resume(struct ucsi *ucsi);
 
 void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
index 60b12961e1a47b71e094847920a5e97b583e3ac0..18286d3e9cc59a9c604162c9559275e61a0e78ef 100644 (file)
@@ -263,6 +263,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev)
                                   ucsi_acpi_notify);
 }
 
+static int ucsi_acpi_suspend(struct device *dev)
+{
+       struct ucsi_acpi *ua = dev_get_drvdata(dev);
+
+       return ucsi_suspend(ua->ucsi);
+}
+
 static int ucsi_acpi_resume(struct device *dev)
 {
        struct ucsi_acpi *ua = dev_get_drvdata(dev);
@@ -270,7 +277,8 @@ static int ucsi_acpi_resume(struct device *dev)
        return ucsi_resume(ua->ucsi);
 }
 
-static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend,
+                               ucsi_acpi_resume);
 
 static const struct acpi_device_id ucsi_acpi_match[] = {
        { "PNP0CA0", 0 },
index d46ca942026e588b28a0814528dcb404a75ea536..91c2958a708c003169478832d5d05356605e8786 100644 (file)
@@ -1521,8 +1521,8 @@ static void ucsi_ccg_remove(struct i2c_client *client)
        cancel_work_sync(&uc->work);
        pm_runtime_disable(uc->dev);
        ucsi_unregister(uc->ucsi);
-       ucsi_destroy(uc->ucsi);
        free_irq(uc->irq, uc);
+       ucsi_destroy(uc->ucsi);
 }
 
 static const struct of_device_id ucsi_ccg_of_match_table[] = {
index ad669d2f8b9ce3e8b31a8b61d96bd957794a3025..ca1b534cb183d9857f931e5f7d39dc33550c236e 100644 (file)
@@ -84,6 +84,8 @@ struct gaokun_ucsi_port {
        struct auxiliary_device *bridge;
 
        struct typec_mux *typec_mux;
+       struct typec_mux_state state;
+       struct typec_altmode dp_alt;
 
        int idx;
        enum gaokun_ucsi_ccx ccx;
@@ -292,24 +294,22 @@ static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec)
 static void gaokun_ucsi_handle_usb_mode(struct gaokun_ucsi_port *port)
 {
        struct gaokun_ucsi *uec = port->ucsi;
-       struct typec_mux_state state = {};
-       struct typec_altmode dp_alt = {};
        int idx = port->idx, ret;
 
        /*
         * For every typec port on this platform, the only mode-switch is
         * controlled by its qmp combo phy which consumes svid and mode only.
         */
-       dp_alt.svid = port->svid;
-       state.mode = port->mode;
-       state.alt = &dp_alt;
+       port->dp_alt.svid = port->svid;
+       port->state.mode = port->mode;
+       port->state.alt = &port->dp_alt;
 
        if (idx >= uec->num_ports) {
                dev_warn(uec->dev, "altmode port out of range: %d\n", idx);
                return;
        }
 
-       ret = typec_mux_set(port->typec_mux, &state);
+       ret = typec_mux_set(port->typec_mux, &port->state);
        if (ret)
                dev_err(uec->dev, "failed to set mux %d\n", ret);
 
index faf61c9c6a9894269f3b171467e7bad238406248..5ef0e7d9b23ad4718d2a31a671629f26dd06d4eb 100644 (file)
@@ -38,7 +38,6 @@ struct vep {
 
 struct vrequest {
        struct usb_request req;
-       struct vudc *udc;
        struct list_head req_entry; /* Request queue */
 };
 
index c5f079c5a1ea5cc4899e63c39eb8e4060dd7af7d..5ef88117965d1a22f0cb9b9ccfca3329e0a223af 100644 (file)
@@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req,
 static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 {
        struct vep *ep;
-       struct vrequest *req;
        struct vudc *udc;
        struct vrequest *lst;
        unsigned long flags;
@@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
                return ret;
 
        ep = to_vep(_ep);
-       req = to_vrequest(_req);
-       udc = req->udc;
+       udc = ep_to_vudc(ep);
 
        if (!udc->driver)
                return -ESHUTDOWN;
index 088b3a0e6ce6cc776ea144edd53fa73dc785bb91..581ac799d97495114a79872a3b3bee3d2b65e83d 100644 (file)
@@ -1017,10 +1017,6 @@ static int virtballoon_probe(struct virtio_device *vdev)
                unsigned int capacity;
 
                capacity = virtqueue_get_vring_size(vb->reporting_vq);
-               if (capacity < PAGE_REPORTING_CAPACITY) {
-                       err = -ENOSPC;
-                       goto out_unregister_oom;
-               }
 
                vb->pr_dev_info.order = PAGE_REPORTING_ORDER_UNSPECIFIED;
 
@@ -1041,6 +1037,7 @@ static int virtballoon_probe(struct virtio_device *vdev)
                vb->pr_dev_info.order = 5;
 #endif
 
+               vb->pr_dev_info.capacity = capacity;
                err = page_reporting_register(&vb->pr_dev_info);
                if (err)
                        goto out_unregister_oom;
index e33f95c91b096b4d855cf37feb3505444e32ca24..c7036e0e41bda1e4b30cc7427c248dc91b30e925 100644 (file)
@@ -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;
                }
 
index 23c3eeb58dc1d4ee6a9bb0d203c3108e5a911a52..1be632c742bdde74b213d71f46d81d39675f5280 100644 (file)
@@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache,
        if (node) {
                ASSERT(list_empty(&node->list));
                ASSERT(list_empty(&node->lower));
-               ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start);
+               ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu",
+                      node->eb ? node->eb->start : 0,
+                      node->eb ? btrfs_header_level(node->eb) : 0,
+                      node->eb ? btrfs_header_owner(node->eb) : 0);
                cache->nr_nodes--;
                btrfs_put_root(node->root);
                kfree(node);
index d5d81f9546c379f556734c5e44497928daae8d16..7fdc6c3fd0666cd29435ee14f7dae43cc51e715b 100644 (file)
@@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode)
        if (inode->flags & BTRFS_INODE_NODATACOW ||
            inode->flags & BTRFS_INODE_NODATASUM)
                return false;
+       if (btrfs_is_data_reloc_root(inode->root))
+               return false;
        return true;
 }
 
index 7d604524e83c33ed70d601038d987926d5448205..de5785117a47f09f227e3bfa04302e6a843826dc 100644 (file)
@@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e
 
                btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
                percpu_counter_add_batch(&fs_info->dirty_metadata_bytes,
-                                        -eb->len,
+                                        -(s64)eb->len,
                                         fs_info->dirty_metadata_batch);
                ret = true;
        } else {
@@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
                return;
 
        buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY);
-       percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len,
+       percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len,
                                 fs_info->dirty_metadata_batch);
 
        for (int i = 0; i < num_extent_folios(eb); i++) {
index fce9c5cc012283dddb891c0e45b06180878a6148..6ad7b39ae358b24388f742c50165ff6d04979345 100644 (file)
@@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
                        goto next;
                }
 
-               flags = em->flags;
                /*
                 * In case we split the extent map, we want to preserve the
                 * EXTENT_FLAG_LOGGING flag on our extent map, but we don't want
                 * it on the new extent maps.
                 */
-               em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING);
+               flags = em->flags & ~EXTENT_FLAG_LOGGING;
+               em->flags &= ~EXTENT_FLAG_PINNED;
                modified = !list_empty(&em->list);
 
                /*
index 9f6454e9db818635eb5a2cc97dcde6bcdc804d00..cf50fd623f41a8013fc3a70c76fb93ee20a893a9 100644 (file)
@@ -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;
 }
index 6009b1477232dbb3b4aaa0ee9c52af2b00ed373e..e2af75a205ea533224a0f780073cafc5f329db7d 100644 (file)
@@ -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;
 
index 272598f6ae77afc85be4de905ef2ff3d9e296061..b446c3014b2403fc1dde68bc3dd443d91ecc74bc 100644 (file)
@@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
        stat->result_mask |= STATX_SUBVOL;
 
        spin_lock(&BTRFS_I(inode)->lock);
-       delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes;
+       delalloc_bytes = S_ISREG(inode->i_mode) ?
+                        BTRFS_I(inode)->new_delalloc_bytes : 0;
        inode_bytes = inode_get_bytes(inode);
        spin_unlock(&BTRFS_I(inode)->lock);
        stat->blocks = (ALIGN(inode_bytes, blocksize) +
index 9d47d16394fc569fa62a4769eeff0aaab16fe252..68b33f365fda676fd49b2e2b7ed5bf34f687dc6a 100644 (file)
@@ -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);
@@ -2042,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode,
                        ret = -ENOENT;
                        goto out;
                }
+               ret = 0;
        }
 
 out:
index 6e4aa22853aba20530e7cb17fca83cb7d1e2a164..1531adb117d15a975735593fc03c44535a89a2a6 100644 (file)
@@ -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;
index 87e60a2d4bd819dcdd223abd36fa24cb337eab5c..53e726119ca7021fd6954674b90b61f5c0ac4457 100644 (file)
@@ -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);
index adc956432d2f1501d27ebeed3e7c437715361bf7..bb77d46376d4bb2c1033f478f1a5ee422375e253 100644 (file)
@@ -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;
        }
 
index fb85bc8b345c798e3f61981ccc0b1b38672a4acd..fc5c14b5adad707a6c43671376400f3ebdad10aa 100644 (file)
@@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc)
                btrfs_err(fs_info,
                            "Duplicate root found for start=%llu while inserting into relocation tree",
                            node->bytenr);
+               kfree(node);
                return -EEXIST;
        }
 
@@ -719,21 +720,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);
 }
 
 /*
@@ -892,6 +891,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
        leaf = path->nodes[0];
        fi = btrfs_item_ptr(leaf, path->slots[0],
                            struct btrfs_file_extent_item);
+       if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) {
+               btrfs_print_leaf(leaf);
+               btrfs_err(fs_info,
+       "unexpected inline file extent item for data reloc inode %llu key offset %llu",
+                         btrfs_ino(BTRFS_I(reloc_inode)), bytenr);
+               return -EUCLEAN;
+       }
 
        /*
         * The cluster-boundary key searched above is always written by
@@ -1520,6 +1526,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static void clear_reloc_root(struct btrfs_root *root)
+{
+       root->reloc_root = NULL;
+       /*
+        * Need barrier to ensure clear_bit() only happens after
+        * root->reloc_root = NULL. Pairs with have_reloc_root().
+        */
+       smp_wmb();
+       clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
+}
+
 static int clean_dirty_subvols(struct reloc_control *rc)
 {
        struct btrfs_root *root;
@@ -1534,13 +1551,7 @@ static int clean_dirty_subvols(struct reloc_control *rc)
                        struct btrfs_root *reloc_root = root->reloc_root;
 
                        list_del_init(&root->reloc_dirty_list);
-                       root->reloc_root = NULL;
-                       /*
-                        * Need barrier to ensure clear_bit() only happens after
-                        * root->reloc_root = NULL. Pairs with have_reloc_root.
-                        */
-                       smp_wmb();
-                       clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
+                       clear_reloc_root(root);
                        if (reloc_root) {
                                /*
                                 * btrfs_drop_snapshot drops our ref we hold for
@@ -1912,24 +1923,43 @@ again:
                                 * corruption, e.g. bad reloc tree key offset.
                                 */
                                ret = -EINVAL;
+                               btrfs_put_root(root);
                                goto out;
                        }
                        ret = merge_reloc_root(rc, root);
-                       btrfs_put_root(root);
                        if (ret) {
-                               if (list_empty(&reloc_root->root_list))
+                               /*
+                                * Clear the reloc root since below we will call
+                                * free_reloc_roots(), otherwise we leave
+                                * root->reloc_root pointing to a freed reloc
+                                * root and trigger a use-after-free during
+                                * unmount or elsewhere.
+                                */
+                               clear_reloc_root(root);
+                               btrfs_put_root(root);
+                               /*
+                                * We are adding the reloc_root to the local
+                                * reloc_roots list, so we add a ref for this
+                                * list which will be dropped below by the call
+                                * to free_reloc_roots().
+                                */
+                               if (list_empty(&reloc_root->root_list)) {
                                        list_add_tail(&reloc_root->root_list,
                                                      &reloc_roots);
+                                       btrfs_grab_root(reloc_root);
+                               }
+                               /* Now drop the ref for root->reloc_root. */
+                               btrfs_put_root(reloc_root);
                                goto out;
                        }
+                       btrfs_put_root(root);
                } else {
                        if (!IS_ERR(root)) {
                                if (root->reloc_root == reloc_root) {
-                                       root->reloc_root = NULL;
+                                       clear_reloc_root(root);
+                                       /* Drop the ref for root->reloc_root. */
                                        btrfs_put_root(reloc_root);
                                }
-                               clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE,
-                                         &root->state);
                                btrfs_put_root(root);
                        }
 
index 56060acac2e9cda12f45e6f8ab23d67f5c978b8b..2a9397be8116c9f2ff0e2d1b97f28b7f90137e78 100644 (file)
@@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info,
        folio_mark_dirty(folio);
 }
 
+static void folio_clear_tags(struct folio *folio)
+{
+       struct address_space *mapping = folio_mapping(folio);
+       XA_STATE(xas, &mapping->i_pages, folio->index);
+       unsigned long flags;
+
+       ASSERT(folio_test_locked(folio));
+       ASSERT(mapping);
+       ASSERT(mapping_use_writeback_tags(mapping));
+
+       xas_lock_irqsave(&xas, flags);
+       xas_load(&xas);
+       xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY);
+       xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE);
+       xas_unlock_irqrestore(&xas, flags);
+}
+
 /*
  * Extra clear_and_test function for subpage dirty bitmap.
  *
@@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
        unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
                                                        writeback, start, len);
        unsigned long flags;
-       bool keep_write;
 
        spin_lock_irqsave(&bfs->lock, flags);
        bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
@@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
         * folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it,
         * assume writeback is complete, and exit too early â€” violating sync
         * ordering guarantees.
+        *
+        * Instead we manually clear the DIRTY and TOWRITE tags after the folio
+        * is no longer dirty.
         */
-       keep_write = folio_test_dirty(folio);
        if (!folio_test_writeback(folio))
-               __folio_start_writeback(folio, keep_write);
+               __folio_start_writeback(folio, true);
+       if (!folio_test_dirty(folio))
+               folio_clear_tags(folio);
        spin_unlock_irqrestore(&bfs->lock, flags);
 }
 
index cb3e676a81cc47951b5b54dd446af030a64710f8..0ce91396b517f57670be1fe354d24f448364c480 100644 (file)
@@ -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",
index 0868c12fc15b43fca5b7719ca4fe025af95b1fd1..a1a20a5ba548f57f41df2fbc1e4b43200fd2d431 100644 (file)
 
 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);
index 86fa5c6a0c708795ddeaa01a05565ba861e32f94..9d8f862f309feb8d52dbab497d50dc6cb522277a 100644 (file)
@@ -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))
index bab52161355247c122d673216f2a869fd69d3cd6..5811556a7b71fbfd54b0430d2ae69ca16adf60a4 100644 (file)
@@ -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;
index 99ed9228a677be754b815753ba9e5dbf82efd423..2772675bd35a361e493e928f016260326a5cd7f6 100644 (file)
@@ -249,13 +249,13 @@ static inline unsigned char fat_checksum(const __u8 *name)
        return s;
 }
 
-static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus)
+static inline sector_t fat_clus_to_blknr(const struct msdos_sb_info *sbi, int clus)
 {
        return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus
                + sbi->data_start;
 }
 
-static inline void fat_get_blknr_offset(struct msdos_sb_info *sbi,
+static inline void fat_get_blknr_offset(const struct msdos_sb_info *sbi,
                                loff_t i_pos, sector_t *blknr, int *offset)
 {
        *blknr = i_pos >> sbi->dir_per_block_bits;
index 4eeed9dca5494033b154d7b29f16f3197535bcf1..9583ce66dca3cdc1f783577c35bbd7676cb9bf2f 100644 (file)
@@ -22,7 +22,7 @@ static void fat_checksum_test(struct kunit *test)
 
 static void fat_clus_to_blknr_test(struct kunit *test)
 {
-       struct msdos_sb_info sbi = {
+       static const struct msdos_sb_info sbi = {
                .sec_per_clus = 4,
                .data_start = 100,
        };
@@ -34,7 +34,7 @@ static void fat_clus_to_blknr_test(struct kunit *test)
 
 static void fat_get_blknr_offset_test(struct kunit *test)
 {
-       struct msdos_sb_info sbi = {
+       static const struct msdos_sb_info sbi = {
                .dir_per_block = 16,
                .dir_per_block_bits = 4,
        };
index c7b723c18620c9fd096bd781a67842c73bc814bd..c7caffb31935c7d989b84c021dacc9f8344c2cf4 100644 (file)
@@ -3346,6 +3346,8 @@ static int nfs_open_permission_mask(int openflags)
                        mask |= MAY_READ;
                if ((openflags & O_ACCMODE) != O_RDONLY)
                        mask |= MAY_WRITE;
+               if (openflags & O_TRUNC)
+                       mask |= MAY_WRITE;
        }
 
        return mask;
index acaeff7ddfdf70f4d6126566b248c19d55f08302..e4533f5836321d8634a3604a6e0d10cb3b11d3aa 100644 (file)
@@ -851,17 +851,19 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
 }
 
 /*
- * Record the page as unstable (an extra writeback period) and mark its
- * inode as dirty.
+ * Record the request's range as unstable (an extra writeback period) and
+ * mark its inode as dirty.
  */
-static inline void nfs_folio_mark_unstable(struct folio *folio,
+static inline void nfs_folio_mark_unstable(struct nfs_page *req,
                                           struct nfs_commit_info *cinfo)
 {
+       struct folio *folio = nfs_page_to_folio(req);
+
        if (folio && !cinfo->dreq) {
                struct inode *inode = folio->mapping->host;
-               long nr = folio_nr_pages(folio);
+               long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE);
 
-               /* This page is really still in write-back - just that the
+               /* This range is really still in write-back - just that the
                 * writeback is happening on the server now.
                 */
                node_stat_mod_folio(folio, NR_WRITEBACK, nr);
index 0ff43dbcb7cd77aeab5fe0048182cce6cf724199..648c95b78eeab73ab4481e2531ed6f303879b34c 100644 (file)
@@ -1199,7 +1199,7 @@ pnfs_layout_mark_request_commit(struct nfs_page *req,
 
        nfs_request_add_commit_list_locked(req, list, cinfo);
        mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
-       nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo);
+       nfs_folio_mark_unstable(req, cinfo);
        return;
 out_resched:
        mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
index fcffb8c9e9df70d318d0fbd7136f1bd060cf11d1..d2b03ceaeb4f195fe165bd7ec794e16799c7041a 100644 (file)
@@ -807,7 +807,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo)
        mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
        nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo);
        mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
-       nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo);
+       nfs_folio_mark_unstable(req, cinfo);
 }
 EXPORT_SYMBOL_GPL(nfs_request_add_commit_list);
 
@@ -866,10 +866,12 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
        nfs_request_add_commit_list(req, cinfo);
 }
 
-static void nfs_folio_clear_commit(struct folio *folio)
+static void nfs_folio_clear_commit(struct nfs_page *req)
 {
+       struct folio *folio = nfs_page_to_folio(req);
+
        if (folio) {
-               long nr = folio_nr_pages(folio);
+               long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE);
 
                node_stat_mod_folio(folio, NR_WRITEBACK, -nr);
                bdi_wb_stat_mod(folio->mapping->host, WB_WRITEBACK, -nr);
@@ -889,7 +891,7 @@ static void nfs_clear_request_commit(struct nfs_commit_info *cinfo,
                        nfs_request_remove_commit_list(req, cinfo);
                }
                mutex_unlock(&NFS_I(inode)->commit_mutex);
-               nfs_folio_clear_commit(nfs_page_to_folio(req));
+               nfs_folio_clear_commit(req);
        }
 }
 
@@ -1741,7 +1743,7 @@ void nfs_retry_commit(struct list_head *page_list,
                req = nfs_list_entry(page_list->next);
                nfs_list_remove_request(req);
                nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx);
-               nfs_folio_clear_commit(nfs_page_to_folio(req));
+               nfs_folio_clear_commit(req);
                nfs_unlock_and_release_request(req);
        }
 }
@@ -1813,7 +1815,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
                req = nfs_list_entry(data->pages.next);
                nfs_list_remove_request(req);
                folio = nfs_page_to_folio(req);
-               nfs_folio_clear_commit(folio);
+               nfs_folio_clear_commit(req);
 
                dprintk("NFS:       commit (%s/%llu %d@%lld)",
                        nfs_req_openctx(req)->dentry->d_sb->s_id,
index d0486f4a47ba5a9a030ccf05ea7ecbb3de20079c..fa92e31d19d6f78672d8e376079728625d673201 100644 (file)
@@ -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);
index f2bb5650604657aea48b0389307df89879818e4b..173de4cbee0f7380db5fdd05ece9a1003116377e 100644 (file)
@@ -249,6 +249,8 @@ static int ntfs_writepages(struct address_space *mapping,
                .wbc            = wbc,
                .ops            = &ntfs_writeback_ops,
        };
+       bool need_iput = false;
+       int ret;
 
        if (NVolShutdown(ni->vol))
                return -EIO;
@@ -265,7 +267,20 @@ static int ntfs_writepages(struct address_space *mapping,
                return -EOPNOTSUPP;
        }
 
-       return iomap_writepages(&wpc);
+       /*
+        * Prevent eviction in writeback to avoid deadlock in
+        * ntfs_drop_big_inode().
+        */
+       if ((ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) &&
+           igrab(inode))
+               need_iput = true;
+
+       ret = iomap_writepages(&wpc);
+
+       if (need_iput)
+               iput(inode);
+
+       return ret;
 }
 
 static int ntfs_swap_activate(struct swap_info_struct *sis,
index dd8828098511831d55823ca9621d667ee5f86a4c..239b7bcbaedf4c2d0bd51529a93c45bbd0eb0352 100644 (file)
@@ -175,7 +175,10 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea
                                err = -EIO;
                        goto err_out;
                }
-               WARN_ON(!ctx->attr->non_resident);
+               if (unlikely(!ctx->attr->non_resident)) {
+                       err = -EIO;
+                       goto err_out;
+               }
        }
        a = ctx->attr;
        /*
@@ -5325,6 +5328,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6
        ret = ntfs_attr_map_whole_runlist(ni);
        if (ret) {
                up_write(&ni->runlist.lock);
+               kfree(hole_rl);
                return ret;
        }
 
@@ -5536,6 +5540,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
        s64 old_data_size;
        s64 vcn_start, vcn_end, vcn_uninit, vcn, try_alloc_cnt;
        s64 lcn, alloc_cnt;
+       s64 rl_lcn, rl_length, rl_vcn;
        int err = 0;
        struct runlist_element *rl;
        bool balloc;
@@ -5615,19 +5620,23 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
        while (vcn < vcn_uninit) {
                down_read(&ni->runlist.lock);
                rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL);
-               up_read(&ni->runlist.lock);
                if (IS_ERR(rl)) {
+                       up_read(&ni->runlist.lock);
                        err = PTR_ERR(rl);
                        goto out;
                }
+               rl_lcn = rl->lcn;
+               rl_length = rl->length;
+               rl_vcn = rl->vcn;
+               up_read(&ni->runlist.lock);
 
-               if (rl->lcn > 0) {
-                       vcn += rl->length - (vcn - rl->vcn);
-               } else if (rl->lcn == LCN_DELALLOC || rl->lcn == LCN_HOLE) {
-                       try_alloc_cnt = min(rl->length - (vcn - rl->vcn),
+               if (rl_lcn > 0) {
+                       vcn += rl_length - (vcn - rl_vcn);
+               } else if (rl_lcn == LCN_DELALLOC || rl_lcn == LCN_HOLE) {
+                       try_alloc_cnt = min(rl_length - (vcn - rl_vcn),
                                            vcn_uninit - vcn);
 
-                       if (rl->lcn == LCN_DELALLOC) {
+                       if (rl_lcn == LCN_DELALLOC) {
                                vcn += try_alloc_cnt;
                                continue;
                        }
@@ -5642,11 +5651,14 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
                                if (err)
                                        goto out;
 
-                               err = ntfs_dio_zero_range(VFS_I(ni),
-                                                         lcn << vol->cluster_size_bits,
-                                                         alloc_cnt << vol->cluster_size_bits);
-                               if (err > 0)
-                                       goto out;
+                               if (balloc) {
+                                       err = ntfs_dio_zero_range(VFS_I(ni),
+                                                                 lcn << vol->cluster_size_bits,
+                                                                 alloc_cnt <<
+                                                                 vol->cluster_size_bits);
+                                       if (err > 0)
+                                               goto out;
+                               }
 
                                if (signal_pending(current))
                                        goto out;
index afb13038ba425fc4a1778efa372bedc0897777b9..be3086d3433816778d42b9cd5a5e241c47d4be5b 100644 (file)
@@ -57,6 +57,15 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
        struct ntfs_inode *attr_ni;
        int err;
 
+       /*
+        * generic_shutdown_super() clears SB_ACTIVE before evicting cached
+        * inodes. Do not look up the attribute-list inode after SB_ACTIVE has
+        * been cleared; it may already be I_FREEING, and waiting on it can
+        * self-deadlock.
+        */
+       if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE))
+               return -EIO;
+
        attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
        if (IS_ERR(attr_vi)) {
                err = PTR_ERR(attr_vi);
index 4b6bd5f30c65ea422dc119ef4b2303b4859d3d8d..6fa9ae3377cb89d6a0cdfa9ef41badb2dabeef01 100644 (file)
 __le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'),
                cpu_to_le16('3'),       cpu_to_le16('0'), 0 };
 
+static inline u64 ntfs_check_mref(u64 mref)
+{
+       if (IS_ERR_MREF(mref))
+               return ERR_MREF(-EIO);
+       return mref;
+}
+
 /*
  * ntfs_lookup_inode_by_name - find an inode in a directory given its name
  * @dir_ni:    ntfs inode of the directory in which to search for the name
@@ -178,7 +185,7 @@ found_it:
                        mref = le64_to_cpu(ie->data.dir.indexed_file);
                        ntfs_attr_put_search_ctx(ctx);
                        unmap_mft_record(dir_ni);
-                       return mref;
+                       return ntfs_check_mref(mref);
                }
                /*
                 * For a case insensitive mount, we also perform a case
@@ -273,7 +280,7 @@ found_it:
                if (name) {
                        ntfs_attr_put_search_ctx(ctx);
                        unmap_mft_record(dir_ni);
-                       return name->mref;
+                       return ntfs_check_mref(name->mref);
                }
                ntfs_debug("Entry not found.");
                err = -ENOENT;
@@ -413,7 +420,7 @@ found_it2:
                        mref = le64_to_cpu(ie->data.dir.indexed_file);
                        kfree(kaddr);
                        iput(ia_vi);
-                       return mref;
+                       return ntfs_check_mref(mref);
                }
                /*
                 * For a case insensitive mount, we also perform a case
@@ -538,7 +545,7 @@ found_it2:
        if (name) {
                kfree(kaddr);
                iput(ia_vi);
-               return name->mref;
+               return ntfs_check_mref(name->mref);
        }
        ntfs_debug("Entry not found.");
        err = -ENOENT;
index c5f2cf75b750e998fcc8ec0b339cd2dffbe8dd57..faa7ee920a3ad073a8c672d5c8c2523f7fe3850b 100644 (file)
@@ -110,6 +110,10 @@ static int ntfs_ib_write(struct ntfs_index_context *icx, struct index_block *ib)
        ret = ntfs_inode_attr_pwrite(VFS_I(icx->ia_ni),
                        ntfs_ib_vcn_to_pos(icx, vcn), icx->block_size,
                        (u8 *)ib, icx->sync_write);
+
+       /* Perform data restoration before returning */
+       post_write_mst_fixup((struct ntfs_record *)ib);
+
        if (ret != icx->block_size) {
                ntfs_debug("Failed to write index block %lld, inode %llu",
                                vcn, (unsigned long long)icx->idx_ni->mft_no);
@@ -147,7 +151,6 @@ int ntfs_icx_ib_sync_write(struct ntfs_index_context *icx)
                icx->ib = NULL;
                icx->ib_dirty = false;
        } else {
-               post_write_mst_fixup((struct ntfs_record *)icx->ib);
                icx->sync_write = false;
        }
 
index c2715521e5628657b547b9cc9e7d37fc8e6f0c3d..7381a18cfadd97738cd012cb43dfd768706b5ac5 100644 (file)
@@ -1191,6 +1191,15 @@ no_data_attr_special_case:
            !S_ISFIFO(vi->i_mode) && !S_ISSOCK(vi->i_mode) && !S_ISLNK(vi->i_mode))
                vi->i_flags |= S_IMMUTABLE;
 
+       /*
+        * System files such as $Bitmap and $MFT are maintained by the driver
+        * itself, and writing them from userspace corrupts the volume.
+        * Always make them immutable regardless of the sys_immutable option.
+        * Directories are skipped so the root and $Extend stay usable.
+        */
+       if (ni->mft_no < FILE_first_user && S_ISREG(vi->i_mode))
+               vi->i_flags |= S_IMMUTABLE;
+
        /*
         * The number of 512-byte blocks used on disk (for stat). This is in so
         * far inaccurate as it doesn't account for any named streams or other
index a5019e80951b8438f796041d46e3775a7e846683..fd20d7abd6f5ec7edc423d00bf0a82ed2e66701c 100644 (file)
@@ -2637,7 +2637,6 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w
        s64 vcn = ntfs_pidx_to_cluster(vol, folio->index);
        s64 end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size);
        unsigned int folio_sz;
-       struct runlist_element *rl = NULL;
        loff_t i_size = i_size_read(vi);
 
        ntfs_debug("Entering for inode 0x%llx, attribute type 0x%x, folio index 0x%lx.",
@@ -2682,6 +2681,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w
                                        &tni, &ref_inos[nr_ref_inos])) {
                        unsigned int mft_record_off = 0;
                        s64 vcn_off = vcn;
+                       s64 rl_len = 0;
 
                        /*
                         * The record should be written.  If a locked ntfs
@@ -2701,8 +2701,12 @@ flush_bio:
                        }
 
                        if (vol->cluster_size < folio_size(folio)) {
+                               struct runlist_element *rl;
+
                                down_write(&ni->runlist.lock);
                                rl = ntfs_attr_vcn_to_rl(ni, vcn_off, &lcn);
+                               if (!IS_ERR(rl))
+                                       rl_len = rl->length - (vcn_off - rl->vcn);
                                up_write(&ni->runlist.lock);
                                if (IS_ERR(rl) || lcn < 0) {
                                        err = -EIO;
@@ -2733,7 +2737,7 @@ flush_bio:
 
                        if (vol->cluster_size == NTFS_BLOCK_SIZE &&
                            (mft_record_off ||
-                            (rl && rl->length - (vcn_off - rl->vcn) == 1) ||
+                            rl_len == 1 ||
                             mft_ofs + NTFS_BLOCK_SIZE >= PAGE_SIZE))
                                folio_sz = NTFS_BLOCK_SIZE;
                        else
index a19626a135bd7fe957c46f85b491c54ca68e5c10..5ff25e9aaa325ae0e61dcdb6c8bf47471c26ef24 100644 (file)
@@ -1266,6 +1266,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
        struct ntfs_volume *vol = NTFS_SB(sb);
        struct ntfs_inode *old_ni, *new_ni = NULL;
        struct ntfs_inode *old_dir_ni = NTFS_I(old_dir), *new_dir_ni = NTFS_I(new_dir);
+       bool new_dir_first = false;
 
        if (NVolShutdown(old_dir_ni->vol))
                return -EIO;
@@ -1301,36 +1302,39 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
        old_inode = old_dentry->d_inode;
        new_inode = new_dentry->d_inode;
        old_ni = NTFS_I(old_inode);
+       if (new_inode)
+               new_ni = NTFS_I(new_inode);
+       if (old_dir != new_dir)
+               new_dir_first = is_subdir(new_dentry->d_parent,
+                                         old_dentry->d_parent);
 
        if (!(vol->vol_flags & VOLUME_IS_DIRTY))
                ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY);
 
        mutex_lock_nested(&old_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
-       mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
+       if (new_ni)
+               mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2);
+
+       if (old_dir == new_dir) {
+               mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
+       } else if (new_dir_first) {
+               mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
+               mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
+       } else {
+               mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
+               mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
+       }
 
-       if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni)) {
+       if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni) ||
+           (new_ni && NInoBeingDeleted(new_ni)) ||
+           (old_dir != new_dir && NInoBeingDeleted(new_dir_ni))) {
                err = -ENOENT;
-               goto unlock_old;
+               goto err_out;
        }
 
        is_dir = S_ISDIR(old_inode->i_mode);
 
        if (new_inode) {
-               new_ni = NTFS_I(new_inode);
-               mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2);
-               if (old_dir != new_dir) {
-                       mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
-                       if (NInoBeingDeleted(new_dir_ni)) {
-                               err = -ENOENT;
-                               goto err_out;
-                       }
-               }
-
-               if (NInoBeingDeleted(new_ni)) {
-                       err = -ENOENT;
-                       goto err_out;
-               }
-
                if (is_dir) {
                        struct mft_record *ni_mrec;
 
@@ -1348,14 +1352,6 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
                err = ntfs_delete(new_ni, new_dir_ni, uname_new, new_name_len, false);
                if (err)
                        goto err_out;
-       } else {
-               if (old_dir != new_dir) {
-                       mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
-                       if (NInoBeingDeleted(new_dir_ni)) {
-                               err = -ENOENT;
-                               goto err_out;
-                       }
-               }
        }
 
        err = __ntfs_link(old_ni, new_dir_ni, uname_new, new_name_len);
@@ -1386,13 +1382,17 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
        inode_inc_iversion(new_dir);
 
 err_out:
-       if (old_dir != new_dir)
+       if (old_dir == new_dir) {
+               mutex_unlock(&old_dir_ni->mrec_lock);
+       } else if (new_dir_first) {
+               mutex_unlock(&old_dir_ni->mrec_lock);
                mutex_unlock(&new_dir_ni->mrec_lock);
-       if (new_inode)
+       } else {
+               mutex_unlock(&new_dir_ni->mrec_lock);
+               mutex_unlock(&old_dir_ni->mrec_lock);
+       }
+       if (new_ni)
                mutex_unlock(&new_ni->mrec_lock);
-
-unlock_old:
-       mutex_unlock(&old_dir_ni->mrec_lock);
        mutex_unlock(&old_ni->mrec_lock);
        if (uname_new)
                kmem_cache_free(ntfs_name_cache, uname_new);
index f9b2c2c906cd7281132f55e3afb253fc58182f0a..7d938714343573b90e1ed78ed36a904e51088abc 100644 (file)
@@ -173,7 +173,7 @@ u64 stable_page_flags(const struct page *page)
                u |= 1 << KPF_MMAP;
        if (is_anon) {
                u |= 1 << KPF_ANON;
-               if (mapping & FOLIO_MAPPING_KSM)
+               if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM)
                        u |= 1 << KPF_KSM;
        }
 
index af2cbab14497ee7b3eeaf5a9cf863addd8d8778d..cc9966ff6cdfb67bf90adba2fe26aff6ded4673e 100644 (file)
@@ -74,6 +74,8 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx);
 
 static void rdtgroup_destroy_root(void);
 
+static void mon_put_kn_priv(void);
+
 struct dentry *debugfs_resctrl;
 
 /*
@@ -585,14 +587,20 @@ unlock:
  *
  * On resource group creation via a mkdir, an extra kernfs_node reference is
  * taken to ensure that the rdtgroup structure remains accessible for the
- * rdtgroup_kn_unlock() calls where it is removed.
+ * rdtgroup_kn_unlock() calls where it is removed. The default group is
+ * statically allocated: it does not have an extra reference but will have
+ * RDT_DELETED set on unmount to support safe access to its associated files
+ * via rdtgroup_kn_lock_live/rdtgroup_kn_unlock().
  *
- * Drop the extra reference here, then free the rdtgroup structure.
+ * For all but the default group: drop the extra reference, then free the
+ * rdtgroup structure.
  *
  * Return: void
  */
 static void rdtgroup_remove(struct rdtgroup *rdtgrp)
 {
+       if (rdtgrp == &rdtgroup_default)
+               return;
        kernfs_put(rdtgrp->kn);
        kfree(rdtgrp);
 }
@@ -2812,6 +2820,12 @@ static int rdt_get_tree(struct fs_context *fc)
                goto out;
        }
 
+       /* Avoid races from pending operations from a previous mount */
+       if (atomic_read(&rdtgroup_default.waitcount) != 0) {
+               ret = -EBUSY;
+               goto out;
+       }
+
        ret = setup_rmid_lru_list();
        if (ret)
                goto out;
@@ -2893,6 +2907,7 @@ out_mondata:
                kernfs_remove(kn_mondata);
 out_mongrp:
        if (resctrl_arch_mon_capable()) {
+               mon_put_kn_priv();
                rdtgroup_unassign_cntrs(&rdtgroup_default);
                kernfs_remove(kn_mongrp);
        }
@@ -3069,10 +3084,6 @@ static void rmdir_all_sub(void)
                if (rdtgrp == &rdtgroup_default)
                        continue;
 
-               if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
-                   rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED)
-                       rdtgroup_pseudo_lock_remove(rdtgrp);
-
                /*
                 * Give any CPUs back to the default group. We cannot copy
                 * cpu_online_mask because a CPU might have executed the
@@ -3083,7 +3094,13 @@ static void rmdir_all_sub(void)
 
                rdtgroup_unassign_cntrs(rdtgrp);
 
-               free_rmid(rdtgrp->closid, rdtgrp->mon.rmid);
+               if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
+                   rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
+                       rdtgroup_pseudo_lock_remove(rdtgrp);
+               } else {
+                       /* Pseudo-locked group's RMID is freed during setup. */
+                       free_rmid(rdtgrp->closid, rdtgrp->mon.rmid);
+               }
 
                kernfs_remove(rdtgrp->kn);
                list_del(&rdtgrp->rdtgroup_list);
@@ -3174,6 +3191,7 @@ static void resctrl_fs_teardown(void)
        mon_put_kn_priv();
        rdt_pseudo_lock_release();
        rdtgroup_default.mode = RDT_MODE_SHAREABLE;
+       rdtgroup_default.flags = RDT_DELETED;
        closid_exit();
        schemata_list_destroy();
        rdtgroup_destroy_root();
@@ -4274,6 +4292,7 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx)
 
        ctx->kfc.root = rdt_root;
        rdtgroup_default.kn = kernfs_root_to_node(rdt_root);
+       rdtgroup_default.flags = 0;
 
        return 0;
 }
index 84e7e366b0ff41bd0c5fb20068e39d0c05c28dc3..d6494e1d93ccf61032ad53cbf1288830c75ee240 100644 (file)
@@ -56,6 +56,7 @@ struct cifs_sb_info {
        struct smb3_fs_context *ctx;
        atomic_t active;
        atomic_t mnt_cifs_flags;
+       atomic_t outstanding_rreq;      /* nr of rreqs not yet fully deinitialized */
        struct delayed_work prune_tlinks;
        struct rcu_head rcu;
 
index 07cf0e5782337d9d26801db4a8929927d749ea3b..9424281a7674cbd3239c16bfcc936d2057ae07ec 100644 (file)
@@ -962,7 +962,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
                                 */
                                fattr->cf_mode &= ~07777;
                                fattr->cf_mode |=
-                                       le32_to_cpu(ppace[i]->sid.sub_auth[2]);
+                                       le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
                                break;
                        } else {
                                if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
index ea4fc0fa68cacbaacc2a73237410173571c9b6ac..66b9104e7ca20607111e7dc30e5a84f6e6e68955 100644 (file)
@@ -311,6 +311,18 @@ static void cifs_kill_sb(struct super_block *sb)
                /* Wait for all opened files to release */
                flush_workqueue(deferredclose_wq);
 
+               /*
+                * Wait for all in-flight netfs I/O requests to finish their
+                * cleanup_work so that any cifsFileInfo final puts they queue
+                * to fileinfo_put_wq/serverclose_wq have been queued, then
+                * drain the workqueue so the cfile dentry refs are dropped to
+                * avoid the busy dentry warning.
+                */
+               wait_var_event(&cifs_sb->outstanding_rreq,
+                              !atomic_read(&cifs_sb->outstanding_rreq));
+               flush_workqueue(serverclose_wq);
+               flush_workqueue(fileinfo_put_wq);
+
                /* finally release root dentry */
                dput(cifs_sb->root);
                cifs_sb->root = NULL;
@@ -1478,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 */
index 99f9e6dca62b60b056a31b67c6874ffaae35f076..08e94633a9c1e28ceacfde197a57d0cc4a938023 100644 (file)
@@ -250,6 +250,7 @@ struct cifs_open_info_data {
        bool adjust_tz;
        bool reparse_point;
        bool contains_posix_file_info;
+       bool unknown_nlink;
        struct {
                /* ioctl response buffer */
                struct {
index c4ababcb51a388e90336ba78331a8577ee1d93c7..00168839c12319900689d5b5c88cc7ad75cc74ef 100644 (file)
@@ -317,7 +317,7 @@ int generate_smb311signingkey(struct cifs_ses *ses,
 
 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
+void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc);
 bool couldbe_mf_symlink(const struct cifs_fattr *fattr);
 int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
                     struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
index d39175cdf1b18107457e747d3301ed5a26eb0a73..40162d5554eaccbc95fd8649027b48ae66758313 100644 (file)
@@ -5829,38 +5829,6 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
-static int
-CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
-                    const char *fileName, const FILE_BASIC_INFO *data,
-                    const struct nls_table *nls_codepage,
-                    struct cifs_sb_info *cifs_sb)
-{
-       int oplock = 0;
-       struct cifs_open_parms oparms;
-       struct cifs_fid fid;
-       int rc;
-
-       oparms = (struct cifs_open_parms) {
-               .tcon = tcon,
-               .cifs_sb = cifs_sb,
-               .desired_access = GENERIC_WRITE,
-               .create_options = cifs_create_options(cifs_sb, 0),
-               .disposition = FILE_OPEN,
-               .path = fileName,
-               .fid = &fid,
-       };
-
-       rc = CIFS_open(xid, &oparms, &oplock, NULL);
-       if (rc)
-               goto out;
-
-       rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
-       CIFSSMBClose(xid, tcon, fid.netfid);
-out:
-
-       return rc;
-}
-
 int
 CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
                   const char *fileName, const FILE_BASIC_INFO *data,
@@ -5939,10 +5907,6 @@ SetTimesRetry:
        if (rc == -EAGAIN)
                goto SetTimesRetry;
 
-       if (rc == -EOPNOTSUPP)
-               return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
-                                           nls_codepage, cifs_sb);
-
        return rc;
 }
 
index 85aec302c89e106f3c991dd0166c8933bc78edd4..ba749ec25a59fa99a4901a24956c0f3a1b982169 100644 (file)
@@ -3485,6 +3485,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
 
        spin_lock_init(&cifs_sb->tlink_tree_lock);
        cifs_sb->tlink_tree = RB_ROOT;
+       atomic_set(&cifs_sb->outstanding_rreq, 0);
 
        cifs_dbg(FYI, "file mode: %04ho  dir mode: %04ho\n",
                 ctx->file_mode, ctx->dir_mode);
@@ -3875,7 +3876,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
         * After reconnecting to a different server, unique ids won't match anymore, so we disable
         * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
         */
-       cifs_autodisable_serverino(cifs_sb);
+       cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
        /*
         * Force the use of prefix path to support failover on DFS paths that resolve to targets
         * that have different prefix paths.
index 83f8cf2f8d2bb680e7c5e59fa065a1ac5c88cab8..8cd93cd2f00f9862a2aa65ad7f48b1fffec9f0cd 100644 (file)
@@ -363,10 +363,10 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
 {
        struct cache_dfs_tgt *t;
 
-       t = kmalloc_obj(*t, GFP_ATOMIC);
+       t = kmalloc_obj(*t, GFP_KERNEL);
        if (!t)
                return ERR_PTR(-ENOMEM);
-       t->name = kstrdup(name, GFP_ATOMIC);
+       t->name = kstrdup(name, GFP_KERNEL);
        if (!t->name) {
                kfree(t);
                return ERR_PTR(-ENOMEM);
@@ -626,7 +626,7 @@ static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_in
 
        target = READ_ONCE(ce->tgthint);
        if (target) {
-               th = kstrdup(target->name, GFP_ATOMIC);
+               th = kstrdup(target->name, GFP_KERNEL);
                if (!th)
                        return -ENOMEM;
        }
@@ -760,11 +760,11 @@ static int setup_referral(const char *path, struct cache_entry *ce,
 
        memset(ref, 0, sizeof(*ref));
 
-       ref->path_name = kstrdup(path, GFP_ATOMIC);
+       ref->path_name = kstrdup(path, GFP_KERNEL);
        if (!ref->path_name)
                return -ENOMEM;
 
-       ref->node_name = kstrdup(target, GFP_ATOMIC);
+       ref->node_name = kstrdup(target, GFP_KERNEL);
        if (!ref->node_name) {
                rc = -ENOMEM;
                goto err_free_path;
@@ -1328,7 +1328,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
         * After reconnecting to a different server, unique ids won't match anymore, so we disable
         * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
         */
-       cifs_autodisable_serverino(cifs_sb);
+       cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
        /*
         * Force the use of prefix path to support failover on DFS paths that resolve to targets
         * that have different prefix paths.
index 5a25635bc62a951018c601690b0cc294ceb47601..968740e7c9c3aa71d33aa4b0edbc99dc57ef2175 100644 (file)
@@ -288,6 +288,7 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file)
                return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin);
        }
 
+       atomic_inc(&cifs_sb->outstanding_rreq);
        return 0;
 }
 
@@ -302,16 +303,20 @@ static void cifs_rreq_done(struct netfs_io_request *rreq)
        /* we do not want atime to be less than mtime, it broke some apps */
        atime = inode_set_atime_to_ts(inode, current_time(inode));
        mtime = inode_get_mtime(inode);
-       if (timespec64_compare(&atime, &mtime))
+       if (timespec64_compare(&atime, &mtime) < 0)
                inode_set_atime_to_ts(inode, inode_get_mtime(inode));
 }
 
 static void cifs_free_request(struct netfs_io_request *rreq)
 {
        struct cifs_io_request *req = container_of(rreq, struct cifs_io_request, rreq);
+       struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
 
        if (req->cfile)
                cifsFileInfo_put(req->cfile);
+
+       if (atomic_dec_and_test(&cifs_sb->outstanding_rreq))
+               wake_up_var(&cifs_sb->outstanding_rreq);
 }
 
 static void cifs_free_subrequest(struct netfs_io_subrequest *subreq)
index 1dbcfd163ff06904800baff5073ece799cf011af..deed04dd9b914fb8a8e743c6d9b8c7491534f9fe 100644 (file)
@@ -909,6 +909,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
        struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
        memset(fattr, 0, sizeof(*fattr));
+       if (data->unknown_nlink)
+               fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
        fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
        if (info->DeletePending)
                fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
@@ -1145,7 +1147,7 @@ static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_blo
                        fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
                else {
                        fattr->cf_uniqueid = iunique(sb, ROOT_I);
-                       cifs_autodisable_serverino(cifs_sb);
+                       cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via get_srv_inum", rc);
                }
                return;
        }
@@ -1642,7 +1644,7 @@ retry_iget5_locked:
                        fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
 
                        if (inode_has_hashed_dentries(inode)) {
-                               cifs_autodisable_serverino(CIFS_SB(sb));
+                               cifs_autodisable_serverino(CIFS_SB(sb), "Inode number collision detected", 0);
                                iput(inode);
                                fattr->cf_uniqueid = iunique(sb, ROOT_I);
                                goto retry_iget5_locked;
@@ -1708,8 +1710,9 @@ struct inode *cifs_root_iget(struct super_block *sb)
 iget_root:
        if (!rc) {
                if (fattr.cf_flags & CIFS_FATTR_JUNCTION) {
+                       cifs_dbg(VFS, "Removing junction mark and disabling 'serverino' to prevent inode collisions\n");
                        fattr.cf_flags &= ~CIFS_FATTR_JUNCTION;
-                       cifs_autodisable_serverino(cifs_sb);
+                       cifs_autodisable_serverino(cifs_sb, "Cannot retrieve attributes for junction point", rc);
                }
                inode = cifs_iget(sb, &fattr);
        }
@@ -2812,9 +2815,7 @@ cifs_revalidate_mapping(struct inode *inode)
        }
 
 skip_invalidate:
-       clear_bit_unlock(CIFS_INO_LOCK, flags);
-       smp_mb__after_atomic();
-       wake_up_bit(flags, CIFS_INO_LOCK);
+       clear_and_wake_up_bit(CIFS_INO_LOCK, flags);
 
        return rc;
 }
index dd127917a34053e2ce86a443e842c65e37d6289c..8d5d6aca742aa137c56a807364bbb59969e1a297 100644 (file)
@@ -234,7 +234,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
        struct cifs_open_parms oparms;
        struct cifs_io_parms io_parms = {0};
        int buf_type = CIFS_NO_BUFFER;
-       struct cifs_open_info_data query_data;
+       struct cifs_open_info_data query_data = {};
 
        oparms = (struct cifs_open_parms) {
                .tcon = tcon,
@@ -320,7 +320,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
        int buf_type = CIFS_NO_BUFFER;
        __le16 *utf16_path;
        __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
-       struct smb2_file_all_info *pfile_info = NULL;
+       struct cifs_open_info_data data = {};
 
        oparms = (struct cifs_open_parms) {
                .tcon = tcon,
@@ -336,20 +336,12 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
        if (utf16_path == NULL)
                return -ENOMEM;
 
-       pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
-                            GFP_KERNEL);
-
-       if (pfile_info == NULL) {
-               kfree(utf16_path);
-               return  -ENOMEM;
-       }
-
-       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &data, NULL,
                       NULL, NULL);
        if (rc)
                goto qmf_out_open_fail;
 
-       if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+       if (data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
                /* it's not a symlink */
                rc = -ENOENT; /* Is there a better rc to return? */
                goto qmf_out;
@@ -367,7 +359,6 @@ qmf_out:
        SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
 qmf_out_open_fail:
        kfree(utf16_path);
-       kfree(pfile_info);
        return rc;
 }
 
index 0c54b9b79a2ce5b40b2543277f0b299bed3b1251..e4bac2a0b85dcfead7812fa811b53f8ff3871b19 100644 (file)
@@ -278,7 +278,7 @@ dump_smb(void *buf, int smb_buf_length)
 }
 
 void
-cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
+cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc)
 {
        unsigned int sbflags = cifs_sb_flags(cifs_sb);
 
@@ -290,6 +290,10 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
 
                atomic_andnot(CIFS_MOUNT_SERVER_INUM, &cifs_sb->mnt_cifs_flags);
                cifs_sb->mnt_cifs_serverino_autodisabled = true;
+               if (rc)
+                       cifs_dbg(VFS, "%s: %d\n", reason, rc);
+               else
+                       cifs_dbg(VFS, "%s\n", reason);
                cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n",
                         tcon ? tcon->tree_name : "new server");
                cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n");
@@ -752,6 +756,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
                node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
 
                /* copy DfsPath */
+               if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
+                       rc = -EINVAL;
+                       goto parse_DFS_referrals_exit;
+               }
                temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
                max_len = data_end - temp;
                node->path_name = cifs_strndup_from_utf16(temp, max_len,
@@ -762,6 +770,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
                }
 
                /* copy link target UNC */
+               if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
+                       rc = -EINVAL;
+                       goto parse_DFS_referrals_exit;
+               }
                temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
                max_len = data_end - temp;
                node->node_name = cifs_strndup_from_utf16(temp, max_len,
index a50c86bbe60f3fc4d25e4cfaadefd428f137f69d..ee5996e6d7d8c70a4ec3f0cadf595baeca63993b 100644 (file)
@@ -415,7 +415,7 @@ ffirst_retry:
        if (rc == 0) {
                cifsFile->invalidHandle = false;
        } else if (rc == -EOPNOTSUPP && (sbflags & CIFS_MOUNT_SERVER_INUM)) {
-               cifs_autodisable_serverino(cifs_sb);
+               cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via query_dir_first", rc);
                goto ffirst_retry;
        }
 error_exit:
@@ -1029,7 +1029,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
                fattr.cf_uniqueid = de.ino;
        } else {
                fattr.cf_uniqueid = iunique(sb, ROOT_I);
-               cifs_autodisable_serverino(cifs_sb);
+               cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number from readdir", 0);
        }
 
        if ((sbflags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr))
index cd1e1eaee67a6b372f46abc74b13175fe4f3623c..5cc5b0410d4894bd9014329ea65a3e85e322e557 100644 (file)
@@ -67,6 +67,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
        char *sym = NULL;
        struct kvec iov;
        bool directory;
+       int path_len;
        int rc = 0;
 
        if (strlen(symname) > REPARSE_SYM_PATH_MAX)
@@ -168,7 +169,21 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
        if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/')
                sym[0] = sym[1] = sym[2] = sym[5] = '_';
 
-       path = cifs_convert_path_to_utf16(sym, cifs_sb);
+       /*
+        * On a POSIX paths mount the symlink target is stored verbatim, so
+        * convert it with cifs_strndup_to_utf16().  cifs_convert_path_to_utf16()
+        * must not be used here: it strips a leading path separator (it is
+        * meant for share-relative SMB paths), which would corrupt an absolute
+        * POSIX symlink target such as "/foo/bar".  Using NO_MAP_UNI_RSVD also
+        * matches the readback path in smb2_parse_native_symlink().
+        */
+       if (sbflags & CIFS_MOUNT_POSIX_PATHS)
+               path = cifs_strndup_to_utf16(sym, strlen(sym), &path_len,
+                                            cifs_sb->local_nls,
+                                            NO_MAP_UNI_RSVD);
+       else
+               path = cifs_convert_path_to_utf16(sym, cifs_sb);
+
        if (!path) {
                rc = -ENOMEM;
                goto out;
index 74530088d17d0316bb8998d52039b8982748c0e0..ab3d09613c91912ae150b1cc5bcc6a5c95f793cc 100644 (file)
@@ -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
index 2caaf11228ef5384ff248a2d1850ceb0c91e7961..903c46f7129132efc33785093ec234564015d393 100644 (file)
@@ -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");
index d34b3d99f6ed3a26bd3540ccb2ea614dd550ab7f..dc5a8c1da623166c5be6e988d8ba133b8f1aea2e 100644 (file)
@@ -505,21 +505,27 @@ static int
 cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
                        struct cifs_sb_info *cifs_sb, const char *full_path)
 {
-       int rc;
-       FILE_ALL_INFO *file_info;
+       int rc = -EOPNOTSUPP;
+       FILE_ALL_INFO file_info;
 
-       file_info = kmalloc_obj(FILE_ALL_INFO);
-       if (file_info == NULL)
-               return -ENOMEM;
+       if (tcon->ses->capabilities & CAP_NT_SMBS)
+               rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info,
+                                     0 /* not legacy */, cifs_sb->local_nls,
+                                     cifs_remap(cifs_sb));
 
-       rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
-                             0 /* not legacy */, cifs_sb->local_nls,
-                             cifs_remap(cifs_sb));
+       /*
+        * Non-UNICODE variant of fallback functions below expands wildcards,
+        * so they cannot be used for querying paths with wildcard characters.
+        * Therefore for such paths returns -ENOENT as they cannot exist.
+        */
+       if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
+           !(tcon->ses->capabilities & CAP_UNICODE) &&
+           strpbrk(full_path, "*?\"><"))
+               rc = -ENOENT;
 
        if (rc == -EOPNOTSUPP || rc == -EINVAL)
-               rc = SMBQueryInformation(xid, tcon, full_path, file_info,
+               rc = SMBQueryInformation(xid, tcon, full_path, &file_info,
                                cifs_sb->local_nls, cifs_remap(cifs_sb));
-       kfree(file_info);
        return rc;
 }
 
@@ -949,7 +955,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
        struct cifs_open_parms oparms;
        struct cifsFileInfo *open_file;
        FILE_BASIC_INFO new_buf;
-       struct cifs_open_info_data query_data;
+       struct cifs_open_info_data query_data = {};
        __le64 write_time = buf->LastWriteTime;
        struct cifsInodeInfo *cinode = CIFS_I(inode);
        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
index 6860eff3169329de5c7877824a832f8bea729188..5ef919bce52d0a334139e60dfbaa440b0be1f710 100644 (file)
@@ -154,8 +154,6 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
        __le16 *smb2_path;
        __u8 smb2_oplock;
        struct cifs_open_info_data *data = buf;
-       struct smb2_file_all_info file_info = {};
-       struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
        struct kvec err_iov = {};
        int err_buftype = CIFS_NO_BUFFER;
        struct cifs_fid *fid = oparms->fid;
@@ -182,14 +180,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
        }
        smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
 
-       rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+       rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
                       &err_buftype);
        if (rc == -EACCES && retry_without_read_attributes) {
                free_rsp_buf(err_buftype, err_iov.iov_base);
                memset(&err_iov, 0, sizeof(err_iov));
                err_buftype = CIFS_NO_BUFFER;
                oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
-               rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+               rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
                               &err_buftype);
        }
        if (rc && data) {
@@ -202,9 +200,9 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
                                                         oparms->path,
                                                         &data->symlink_target);
                        if (!rc) {
-                               memset(smb2_data, 0, sizeof(*smb2_data));
+                               memset(&data->fi, 0, sizeof(data->fi));
                                oparms->create_options |= OPEN_REPARSE_POINT;
-                               rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
+                               rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data,
                                               NULL, NULL, NULL);
                                oparms->create_options &= ~OPEN_REPARSE_POINT;
                        }
@@ -238,23 +236,22 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
                rc = 0;
        }
 
-       if (smb2_data) {
+       if (data) {
                /* if open response does not have IndexNumber field - get it */
-               if (smb2_data->IndexNumber == 0) {
+               if (data->fi.IndexNumber == 0) {
                        rc = SMB2_get_srv_num(xid, oparms->tcon,
                                      fid->persistent_fid,
                                      fid->volatile_fid,
-                                     &smb2_data->IndexNumber);
+                                     &data->fi.IndexNumber);
                        if (rc) {
                                /*
                                 * let get_inode_info disable server inode
                                 * numbers
                                 */
-                               smb2_data->IndexNumber = 0;
+                               data->fi.IndexNumber = 0;
                                rc = 0;
                        }
                }
-               memcpy(&data->fi, smb2_data, sizeof(data->fi));
        }
 
        *oplock = smb2_oplock;
index 9ed21f7b618c5736c0ce3dbef1f44374e67fdbe9..d86f2460d0e5ec68419e4e8307e3153353a2759b 100644 (file)
@@ -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
index 0f8a44a5ed3cfc5dacdab770df135abdf653ffd5..44dc5e899cad0ed5daaccfb4f5eb72ab96655e06 100644 (file)
@@ -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");
index 2a7355ce1a07835bc0aea644fac25d3f5171230c..9068175e57cd0d2e82d92c7ae2792b0b93b8fa56 100644 (file)
@@ -19,6 +19,9 @@
 #include "nterr.h"
 #include "cached_dir.h"
 
+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)
 {
@@ -145,6 +148,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
        int command;
        __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;
@@ -228,7 +233,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
                }
        }
 
-       calc_len = smb2_calc_size(buf);
+       have_data = false;
+       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 */
@@ -247,8 +258,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
                /* Windows 7 server returns 24 bytes more */
                if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
                        return 0;
-               /* server can return one byte more due to implied bcc[0] */
-               if (calc_len == len + 1)
+               /*
+                * Server can return one byte more due to implied bcc[0].
+                * Allow it only when there is no data area; if data_length > 0
+                * the +1 gap indicates an overreported data length rather than
+                * the bcc[0] omission.
+                */
+               if (calc_len == len + 1 && !have_data)
                        return 0;
 
                /*
@@ -407,19 +423,28 @@ 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.
+ * 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.
  */
-unsigned int
-smb2_calc_size(void *buf)
+static unsigned int
+__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap)
 {
        struct smb2_pdu *pdu = buf;
        struct smb2_hdr *shdr = &pdu->hdr;
        int offset; /* the offset from the beginning of SMB to data area */
-       int data_length; /* the length of the variable length data area */
+       int data_length = 0; /* the length of the variable length data area */
        /* Structure Size has already been checked to make sure it is 64 */
        int len = le16_to_cpu(shdr->StructureSize);
 
+       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.
@@ -442,16 +467,27 @@ smb2_calc_size(void *buf)
                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;
                }
        }
 calc_size_exit:
        cifs_dbg(FYI, "SMB2 len %d\n", len);
+       if (have_data)
+               *have_data = (data_length > 0);
        return len;
 }
 
+unsigned int
+smb2_calc_size(void *buf)
+{
+       return __smb2_calc_size(buf, NULL, NULL);
+}
+
 /* Note: caller must free return buffer */
 __le16 *
 cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
index 06e9322a762ae43aa856805059674120500a73a3..cbd51a08e97e6e334e1da867e8664f257838945f 100644 (file)
@@ -1772,8 +1772,8 @@ replay_again:
                if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
                        qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
                if (qi.input_buffer_length > 0 &&
-                   le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
-                   > rsp_iov[1].iov_len) {
+                    size_add(le32_to_cpu(io_rsp->OutputOffset),
+                            qi.input_buffer_length) > rsp_iov[1].iov_len) {
                        rc = -EFAULT;
                        goto out;
                }
@@ -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,
@@ -3489,6 +3519,15 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
                goto out;
 
        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;
        }
 
@@ -5237,7 +5376,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
 {
        struct TCP_Server_Info *server = tcon->ses->server;
        struct cifs_open_parms oparms;
-       struct cifs_open_info_data idata;
+       struct cifs_open_info_data idata = {};
        struct cifs_io_parms io_parms = {};
        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
        struct cifs_fid fid;
@@ -5286,16 +5425,13 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
                data = (u8 *)symname_utf16;
                break;
        case S_IFSOCK:
-               type_len = 8;
-               strscpy(type, "LnxSOCK");
-               data = (u8 *)&pdev;
-               data_len = sizeof(pdev);
+               /* SFU socket is system file with one zero byte */
+               type_len = 1;
+               type[0] = '\0';
                break;
        case S_IFIFO:
-               type_len = 8;
-               strscpy(type, "LnxFIFO");
-               data = (u8 *)&pdev;
-               data_len = sizeof(pdev);
+               /* SFU fifo is system file which is empty */
+               type_len = 0;
                break;
        default:
                rc = -EPERM;
index 95c0efe9d43b1f355c01160ebeb2196069042dc1..4ce165e40657f3cd27024ef0c8aff773f122d8a7 100644 (file)
@@ -3287,7 +3287,7 @@ SMB2_open_free(struct smb_rqst *rqst)
 
 int
 SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
-         __u8 *oplock, struct smb2_file_all_info *buf,
+         __u8 *oplock, struct cifs_open_info_data *buf,
          struct create_posix_rsp *posix,
          struct kvec *err_iov, int *buftype)
 {
@@ -3302,6 +3302,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
        int rc = 0;
        int flags = 0;
        int retries = 0, cur_sleep = 0;
+       struct smb2_file_all_info *file_info = buf ? &buf->fi : NULL;
 
 replay_again:
        /* reinitialize for possible replay */
@@ -3370,21 +3371,22 @@ replay_again:
        oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
 
-       if (buf) {
-               buf->CreationTime = rsp->CreationTime;
-               buf->LastAccessTime = rsp->LastAccessTime;
-               buf->LastWriteTime = rsp->LastWriteTime;
-               buf->ChangeTime = rsp->ChangeTime;
-               buf->AllocationSize = rsp->AllocationSize;
-               buf->EndOfFile = rsp->EndofFile;
-               buf->Attributes = rsp->FileAttributes;
-               buf->NumberOfLinks = cpu_to_le32(1);
-               buf->DeletePending = 0; /* successful open = not delete pending */
+       if (file_info) {
+               file_info->CreationTime = rsp->CreationTime;
+               file_info->LastAccessTime = rsp->LastAccessTime;
+               file_info->LastWriteTime = rsp->LastWriteTime;
+               file_info->ChangeTime = rsp->ChangeTime;
+               file_info->AllocationSize = rsp->AllocationSize;
+               file_info->EndOfFile = rsp->EndofFile;
+               file_info->Attributes = rsp->FileAttributes;
+               file_info->NumberOfLinks = cpu_to_le32(1);
+               buf->unknown_nlink = true;
+               file_info->DeletePending = 0; /* successful open = not delete pending */
        }
 
 
        rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
-                                oparms->fid->lease_key, oplock, buf, posix);
+                                oparms->fid->lease_key, oplock, file_info, posix);
 
        trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
                             oparms->create_options, oparms->desired_access,
@@ -5947,6 +5949,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
                        0, 1, &data, &size);
 }
 
+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,
index 78a4e1c340f99536deb4fdd21d4b021bc731c122..2e9f7009682512c758f3f88c850e4e670fcf48e4 100644 (file)
@@ -136,7 +136,7 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
              struct cifs_tcon *tcon, const struct nls_table *cp);
 int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
 int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
-             __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf,
+             __le16 *path, __u8 *oplock, struct cifs_open_info_data *buf,
              struct create_posix_rsp *posix, struct kvec *err_iov,
              int *buftype);
 int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
@@ -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,
index 859849a42fec5e07ca68a4d1cd895ae7eaf68109..941db5a95564b6ae1b954cbd522e1fa44183c48f 100644 (file)
@@ -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;
index 86f521e849d5e235ea8e53551b0977cac5075f22..4e7b6f0e6b8cd88f67e1c41eda826dd3601a679c 100644 (file)
@@ -133,16 +133,17 @@ out:
  * @blen:              NTLMv2 blob length
  * @domain_name:       domain name
  * @cryptkey:          session crypto key
+ * @sess_key:          derived session key output buffer
  *
  * Return:     0 on success, error number on error
  */
 int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
                      struct ntlmv2_resp *ntlmv2, int blen, char *domain_name,
-                     char *cryptkey)
+                     char *cryptkey, char *sess_key)
 {
        char ntlmv2_hash[CIFS_ENCPWD_SIZE];
        char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
-       char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
+       char base_key[SMB2_NTLMV2_SESSKEY_SIZE];
        struct hmac_md5_ctx ctx;
        int rc;
 
@@ -165,7 +166,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
        /* Generate the session key */
        hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
                             ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
-                            sess_key);
+                            base_key);
 
        if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
                          CIFS_HMAC_MD5_HASH_SIZE)) {
@@ -173,12 +174,12 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
                goto out;
        }
 
-       memcpy(sess->sess_key, sess_key, sizeof(sess_key));
+       memcpy(sess_key, base_key, sizeof(base_key));
        rc = 0;
 out:
        memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
        memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp));
-       memzero_explicit(sess_key, sizeof(sess_key));
+       memzero_explicit(base_key, sizeof(base_key));
        return rc;
 }
 
@@ -189,12 +190,13 @@ out:
  * @blob_len:  length of the @authblob message
  * @conn:      connection
  * @sess:      session of connection
+ * @sess_key:  derived session key output buffer
  *
  * Return:     0 on success, error number on error
  */
 int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
                                   int blob_len, struct ksmbd_conn *conn,
-                                  struct ksmbd_session *sess)
+                                  struct ksmbd_session *sess, char *sess_key)
 {
        char *domain_name;
        unsigned int nt_off, dn_off;
@@ -234,7 +236,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
        ret = ksmbd_auth_ntlmv2(conn, sess,
                                (struct ntlmv2_resp *)((char *)authblob + nt_off),
                                nt_len - CIFS_ENCPWD_SIZE,
-                               domain_name, conn->ntlmssp.cryptkey);
+                               domain_name, conn->ntlmssp.cryptkey, sess_key);
        kfree(domain_name);
        if (ret)
                return ret;
@@ -257,8 +259,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
                if (!ctx_arc4)
                        return -ENOMEM;
 
-               arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
-               arc4_crypt(ctx_arc4, sess->sess_key,
+               arc4_setkey(ctx_arc4, sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
+               arc4_crypt(ctx_arc4, sess_key,
                           (char *)authblob + sess_key_off, sess_key_len);
                kfree_sensitive(ctx_arc4);
        }
@@ -400,7 +402,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
 
 #ifdef CONFIG_SMB_SERVER_KERBEROS5
 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
-                           int in_len, char *out_blob, int *out_len)
+                           int in_len, char *out_blob, int *out_len,
+                           char *sess_key)
 {
        struct ksmbd_spnego_authen_response *resp;
        struct ksmbd_login_response_ext *resp_ext = NULL;
@@ -448,14 +451,14 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
        } else {
                if (!ksmbd_compare_user(sess->user, user)) {
                        ksmbd_debug(AUTH, "different user tried to reuse session\n");
-                       retval = -EPERM;
+                       retval = -EKEYREJECTED;
                        ksmbd_free_user(user);
                        goto out;
                }
                ksmbd_free_user(user);
        }
 
-       memcpy(sess->sess_key, resp->payload, resp->session_key_len);
+       memcpy(sess_key, resp->payload, resp->session_key_len);
        memcpy(out_blob, resp->payload + resp->session_key_len,
               resp->spnego_blob_len);
        *out_len = resp->spnego_blob_len;
@@ -466,7 +469,8 @@ out:
 }
 #else
 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
-                           int in_len, char *out_blob, int *out_len)
+                           int in_len, char *out_blob, int *out_len,
+                           char *sess_key)
 {
        return -EOPNOTSUPP;
 }
@@ -525,7 +529,7 @@ struct derivation {
        bool binding;
 };
 
-static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+static void generate_key(struct ksmbd_conn *conn, const char *sess_key,
                         struct kvec label, struct kvec context, __u8 *key,
                         unsigned int key_size)
 {
@@ -536,7 +540,7 @@ static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
        unsigned char prfhash[SMB2_HMACSHA256_SIZE];
        struct hmac_sha256_ctx ctx;
 
-       hmac_sha256_init_usingrawkey(&ctx, sess->sess_key,
+       hmac_sha256_init_usingrawkey(&ctx, sess_key,
                                     SMB2_NTLMV2_SESSKEY_SIZE);
        hmac_sha256_update(&ctx, i, 4);
        hmac_sha256_update(&ctx, label.iov_base, label.iov_len);
@@ -559,18 +563,21 @@ static int generate_smb3signingkey(struct ksmbd_session *sess,
                                   const struct derivation *signing)
 {
        struct channel *chann;
-       char *key;
+       char *key, *sess_key;
 
        chann = lookup_chann_list(sess, conn);
        if (!chann)
                return 0;
 
-       if (conn->dialect >= SMB30_PROT_ID && signing->binding)
+       if (conn->dialect >= SMB30_PROT_ID && signing->binding) {
                key = chann->smb3signingkey;
-       else
+               sess_key = chann->sess_key;
+       } else {
                key = sess->smb3signingkey;
+               sess_key = sess->sess_key;
+       }
 
-       generate_key(conn, sess, signing->label, signing->context, key,
+       generate_key(conn, sess_key, signing->label, signing->context, key,
                     SMB3_SIGN_KEY_SIZE);
 
        if (!(conn->dialect >= SMB30_PROT_ID && signing->binding))
@@ -627,11 +634,11 @@ static void generate_smb3encryptionkey(struct ksmbd_conn *conn,
                                       struct ksmbd_session *sess,
                                       const struct derivation_twin *ptwin)
 {
-       generate_key(conn, sess, ptwin->encryption.label,
+       generate_key(conn, sess->sess_key, ptwin->encryption.label,
                     ptwin->encryption.context, sess->smb3encryptionkey,
                     SMB3_ENC_DEC_KEY_SIZE);
 
-       generate_key(conn, sess, ptwin->decryption.label,
+       generate_key(conn, sess->sess_key, ptwin->decryption.label,
                     ptwin->decryption.context,
                     sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
 
index 5767aabc63c9be030139e7d4007ad243d45dafde..f14b7c0332646830b659f283c07805e926b8c0ac 100644 (file)
@@ -41,17 +41,18 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov,
 void ksmbd_copy_gss_neg_header(void *buf);
 int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
                      struct ntlmv2_resp *ntlmv2, int blen, char *domain_name,
-                     char *cryptkey);
+                     char *cryptkey, char *sess_key);
 int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
                                   int blob_len, struct ksmbd_conn *conn,
-                                  struct ksmbd_session *sess);
+                                  struct ksmbd_session *sess, char *sess_key);
 int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
                                  int blob_len, struct ksmbd_conn *conn);
 unsigned int
 ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
                                   struct ksmbd_conn *conn);
 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
-                           int in_len, char *out_blob, int *out_len);
+                           int in_len, char *out_blob, int *out_len,
+                           char *sess_key);
 void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
                         int n_vec, char *sig);
 void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
index de58aed76cb42661d3cdbfd5c47d05f020218bc2..f99c86284ba3d0b625e534de69f79d7abcc42c6b 100644 (file)
@@ -255,7 +255,7 @@ static void free_channel_list(struct ksmbd_session *sess)
        down_write(&sess->chann_lock);
        xa_for_each(&sess->ksmbd_chann_list, index, chann) {
                xa_erase(&sess->ksmbd_chann_list, index);
-               kfree(chann);
+               kfree_sensitive(chann);
        }
 
        xa_destroy(&sess->ksmbd_chann_list);
@@ -449,7 +449,7 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
        if (!chann)
                return -ENOENT;
 
-       kfree(chann);
+       kfree_sensitive(chann);
        return 0;
 }
 
@@ -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);
                }
        }
 
index 6aebd385be8474898894359139d1a8a53948aec9..4637a8c8436d0212caf2dd4246ccc65d688fab40 100644 (file)
@@ -19,6 +19,7 @@
 struct ksmbd_file_table;
 
 struct channel {
+       char                    sess_key[CIFS_KEY_SIZE];
        __u8                    smb3signingkey[SMB3_SIGN_KEY_SIZE];
        struct ksmbd_conn       *conn;
 };
index 3c55ae5d6a11b68bc9712199c101ebfecd861778..79787099afdc68c0a4afb20f6535f45e89695ba9 100644 (file)
@@ -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);
index 36a5ea4828ad699328e5a4737c483ce0e96a9a49..f5baba9348405823cafa48fd8f430c15108cdb9e 100644 (file)
@@ -199,6 +199,12 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
                                else
                                        conn->ops->set_rsp_status(work,
                                                STATUS_USER_SESSION_DELETED);
+                               if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) {
+                                       struct smb2_hdr *rsp_hdr;
+
+                                       rsp_hdr = ksmbd_resp_buf_curr(work);
+                                       rsp_hdr->Flags |= SMB2_FLAGS_SIGNED;
+                               }
                                goto send;
                        } else if (rc > 0) {
                                rc = conn->ops->get_ksmbd_tcon(work);
@@ -237,8 +243,14 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
 
                if (work->sess &&
                    (work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
-                    conn->ops->is_sign_req(work, command)))
-                       conn->ops->set_sign_rsp(work);
+                    conn->ops->is_sign_req(work, command))) {
+                       if (command == SMB2_SESSION_SETUP_HE &&
+                           work->sess->dialect >= SMB30_PROT_ID &&
+                           conn->dialect < SMB30_PROT_ID)
+                               smb3_set_sign_rsp(work);
+                       else
+                               conn->ops->set_sign_rsp(work);
+               }
        } while (is_chained == true);
 
 send:
index c0c4edd092c2b5ca795b5cc2ff4496622c99e6e0..9f3629c86291bc375dd95e7e56f615646238b39d 100644 (file)
@@ -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 ||
index 097f51fc7ed6a5926462a19dab17da077fff7b22..bec692bca1ca8fa5eccd5d8c4f5d502f516c963b 100644 (file)
@@ -61,6 +61,9 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
        (FILE_ATTRIBUTE_MASK & ~(FILE_ATTRIBUTE_INTEGRITY_STREAM | \
                                 FILE_ATTRIBUTE_NO_SCRUB_DATA))
 
+/* Windows reports automatic write-time updates at roughly 15 ms resolution. */
+#define KSMBD_WRITE_TIME_RESOLUTION    (15ULL * 10000)
+
 /**
  * check_session_id() - check for valid session id in smb header
  * @conn:      connection instance
@@ -95,6 +98,47 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn
        return chann;
 }
 
+#define KSMBD_MAX_CHANNELS     32
+
+static int register_session_channel(struct ksmbd_session *sess,
+                                   struct ksmbd_conn *conn,
+                                   const char *sess_key)
+{
+       struct channel *chann, *old;
+       unsigned long index;
+       unsigned int count = 0;
+       int rc = 0;
+
+       down_write(&sess->chann_lock);
+       if (xa_load(&sess->ksmbd_chann_list, (long)conn))
+               goto out;
+
+       xa_for_each(&sess->ksmbd_chann_list, index, chann)
+               count++;
+       if (count >= KSMBD_MAX_CHANNELS) {
+               rc = -ENOSPC;
+               goto out;
+       }
+
+       chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
+       if (!chann) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       chann->conn = conn;
+       memcpy(chann->sess_key, sess_key, sizeof(chann->sess_key));
+       old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
+                      KSMBD_DEFAULT_GFP);
+       if (xa_is_err(old)) {
+               kfree_sensitive(chann);
+               rc = xa_err(old);
+       }
+out:
+       up_write(&sess->chann_lock);
+       return rc;
+}
+
 /**
  * smb2_get_ksmbd_tcon() - get tree connection information using a tree id.
  * @work:      smb work
@@ -1644,9 +1688,11 @@ static int ntlm_authenticate(struct ksmbd_work *work,
 {
        struct ksmbd_conn *conn = work->conn;
        struct ksmbd_session *sess = work->sess;
-       struct channel *chann = NULL, *old;
        struct ksmbd_user *user;
+       char channel_key[CIFS_KEY_SIZE] = {};
+       char *auth_key = conn->binding ? channel_key : sess->sess_key;
        u64 prev_id;
+       bool binding = conn->binding;
        int sz, rc;
 
        ksmbd_debug(SMB, "authenticate phase\n");
@@ -1688,7 +1734,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
 
                if (!ksmbd_compare_user(sess->user, user)) {
                        ksmbd_free_user(user);
-                       return -EPERM;
+                       return -EKEYREJECTED;
                }
                ksmbd_free_user(user);
        } else {
@@ -1705,11 +1751,13 @@ static int ntlm_authenticate(struct ksmbd_work *work,
                        sz = conn->mechTokenLen;
                else
                        sz = le16_to_cpu(req->SecurityBufferLength);
-               rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess);
+               rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess,
+                                                   auth_key);
                if (rc) {
                        set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD);
                        ksmbd_debug(SMB, "authentication failed\n");
-                       return -EPERM;
+                       rc = -EPERM;
+                       goto out;
                }
        }
 
@@ -1744,37 +1792,30 @@ static int ntlm_authenticate(struct ksmbd_work *work,
 
 binding_session:
        if (conn->dialect >= SMB30_PROT_ID) {
-               chann = lookup_chann_list(sess, conn);
-               if (!chann) {
-                       chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
-                       if (!chann)
-                               return -ENOMEM;
-
-                       chann->conn = conn;
-                       down_write(&sess->chann_lock);
-                       old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
-                                       KSMBD_DEFAULT_GFP);
-                       up_write(&sess->chann_lock);
-                       if (xa_is_err(old)) {
-                               kfree(chann);
-                               return xa_err(old);
-                       }
-               }
+               rc = register_session_channel(sess, conn, auth_key);
+               if (rc)
+                       goto out;
        }
 
        if (conn->ops->generate_signingkey) {
                rc = conn->ops->generate_signingkey(sess, conn);
                if (rc) {
                        ksmbd_debug(SMB, "SMB3 signing key generation failed\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
        }
 
        if (!ksmbd_conn_lookup_dialect(conn)) {
                pr_err("fail to verify the dialect\n");
-               return -ENOENT;
+               rc = -ENOENT;
+               goto out;
        }
-       return 0;
+       rc = 0;
+out:
+       if (binding)
+               memzero_explicit(channel_key, sizeof(channel_key));
+       return rc;
 }
 
 #ifdef CONFIG_SMB_SERVER_KERBEROS5
@@ -1785,8 +1826,10 @@ 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;
-       struct channel *chann = NULL, *old;
+       char channel_key[CIFS_KEY_SIZE] = {};
+       char *auth_key = conn->binding ? channel_key : sess->sess_key;
        u64 prev_sess_id;
+       bool binding = conn->binding;
        int in_len, out_len;
        int retval;
 
@@ -1799,10 +1842,12 @@ static int krb5_authenticate(struct ksmbd_work *work,
                (le16_to_cpu(rsp->SecurityBufferOffset) + 4);
 
        retval = ksmbd_krb5_authenticate(sess, in_blob, in_len,
-                                        out_blob, &out_len);
+                                        out_blob, &out_len, auth_key);
        if (retval) {
                ksmbd_debug(SMB, "krb5 authentication failed\n");
-               return -EINVAL;
+               if (retval != -EKEYREJECTED)
+                       retval = -EINVAL;
+               goto out;
        }
 
        /* Check previous session */
@@ -1839,37 +1884,30 @@ static int krb5_authenticate(struct ksmbd_work *work,
 
 binding_session:
        if (conn->dialect >= SMB30_PROT_ID) {
-               chann = lookup_chann_list(sess, conn);
-               if (!chann) {
-                       chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
-                       if (!chann)
-                               return -ENOMEM;
-
-                       chann->conn = conn;
-                       down_write(&sess->chann_lock);
-                       old = xa_store(&sess->ksmbd_chann_list, (long)conn,
-                                       chann, KSMBD_DEFAULT_GFP);
-                       up_write(&sess->chann_lock);
-                       if (xa_is_err(old)) {
-                               kfree(chann);
-                               return xa_err(old);
-                       }
-               }
+               retval = register_session_channel(sess, conn, auth_key);
+               if (retval)
+                       goto out;
        }
 
        if (conn->ops->generate_signingkey) {
                retval = conn->ops->generate_signingkey(sess, conn);
                if (retval) {
                        ksmbd_debug(SMB, "SMB3 signing key generation failed\n");
-                       return -EINVAL;
+                       retval = -EINVAL;
+                       goto out;
                }
        }
 
        if (!ksmbd_conn_lookup_dialect(conn)) {
                pr_err("fail to verify the dialect\n");
-               return -ENOENT;
+               retval = -ENOENT;
+               goto out;
        }
-       return 0;
+       retval = 0;
+out:
+       if (binding)
+               memzero_explicit(channel_key, sizeof(channel_key));
+       return retval;
 }
 #else
 static int krb5_authenticate(struct ksmbd_work *work,
@@ -1975,12 +2013,35 @@ int smb2_sess_setup(struct ksmbd_work *work)
        } else if ((conn->dialect < SMB30_PROT_ID ||
                    server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
                   (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
-               sess = NULL;
+               sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
+               if (sess) {
+                       int sign_ret;
+
+                       work->sess = sess;
+                       if (sess->dialect >= SMB30_PROT_ID)
+                               sign_ret = smb3_check_sign_req(work);
+                       else
+                               sign_ret = smb2_check_sign_req(work);
+                       if (sess->state != SMB2_SESSION_VALID ||
+                           !(req->hdr.Flags & SMB2_FLAGS_SIGNED) ||
+                           !sign_ret) {
+                               ksmbd_user_session_put(sess);
+                               work->sess = NULL;
+                               sess = NULL;
+                       }
+               }
                rc = -EACCES;
                goto out_err;
        } else {
                sess = ksmbd_session_lookup(conn,
                                            le64_to_cpu(req->hdr.SessionId));
+               if (!sess) {
+                       sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
+                       if (sess && !lookup_chann_list(sess, conn)) {
+                               ksmbd_user_session_put(sess);
+                               sess = NULL;
+                       }
+               }
                if (!sess) {
                        rc = -ENOENT;
                        goto out_err;
@@ -2091,12 +2152,19 @@ out_err:
                rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED;
        else if (rc == -EFAULT)
                rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED;
-       else if (rc == -ENOMEM)
+       else if (rc == -ENOMEM || rc == -ENOSPC)
                rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
        else if (rc == -EOPNOTSUPP)
                rsp->hdr.Status = STATUS_NOT_SUPPORTED;
+       else if (rc == -EKEYREJECTED)
+               rsp->hdr.Status = STATUS_ACCESS_DENIED;
        else if (rc)
                rsp->hdr.Status = STATUS_LOGON_FAILURE;
+       if ((rsp->hdr.Status == STATUS_USER_SESSION_DELETED ||
+            (rsp->hdr.Status == STATUS_INVALID_PARAMETER &&
+             (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING))) &&
+           (req->hdr.Flags & SMB2_FLAGS_SIGNED))
+               rsp->hdr.Flags |= SMB2_FLAGS_SIGNED;
 
        if (conn->mechToken) {
                kfree(conn->mechToken);
@@ -2104,6 +2172,17 @@ out_err:
        }
 
        if (rc < 0) {
+               if (sess && conn->dialect == SMB311_PROT_ID &&
+                   (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+                       struct preauth_session *preauth_sess;
+
+                       preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id);
+                       if (preauth_sess) {
+                               list_del(&preauth_sess->preauth_entry);
+                               kfree(preauth_sess);
+                       }
+               }
+
                /*
                 * SecurityBufferOffset should be set to zero
                 * in session setup error response.
@@ -2130,8 +2209,16 @@ out_err:
                                sess->last_active = jiffies;
                                sess->state = SMB2_SESSION_EXPIRED;
                        }
-                       ksmbd_user_session_put(sess);
-                       work->sess = NULL;
+                       /*
+                        * Keep the binding session reference until the response is
+                        * signed and sent.  Error responses for a signed binding
+                        * request are signed with the existing session signing key.
+                        */
+                       if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) ||
+                           work->sess != sess) {
+                               ksmbd_user_session_put(sess);
+                               work->sess = NULL;
+                       }
                        if (try_delay) {
                                ksmbd_conn_set_need_reconnect(conn);
                                ssleep(5);
@@ -3943,6 +4030,7 @@ reconnected_fp:
        time = ksmbd_UnixTimeToNT(stat.atime);
        rsp->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
+       fp->open_mtime = time;
        rsp->LastWriteTime = cpu_to_le64(time);
        rsp->ChangeTime = cpu_to_le64(fp->change_time);
        /*
@@ -6409,6 +6497,9 @@ int smb2_close(struct ksmbd_work *work)
                time = ksmbd_UnixTimeToNT(stat.atime);
                rsp->LastAccessTime = cpu_to_le64(time);
                time = ksmbd_UnixTimeToNT(stat.mtime);
+               if (time > fp->open_mtime &&
+                   time - fp->open_mtime < KSMBD_WRITE_TIME_RESOLUTION)
+                       time = fp->open_mtime;
                rsp->LastWriteTime = cpu_to_le64(time);
                rsp->ChangeTime = cpu_to_le64(fp->change_time);
                ksmbd_fd_put(work, fp);
@@ -6506,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;
        }
 
@@ -6690,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;
@@ -6705,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) {
@@ -7080,6 +7183,8 @@ err_out:
                rsp->hdr.Status = STATUS_INVALID_PARAMETER;
        else if (rc == -EMSGSIZE)
                rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
+       else if (rc == -ENOSPC || rc == -EFBIG)
+               rsp->hdr.Status = STATUS_DISK_FULL;
        else if (rc == -ESHARE)
                rsp->hdr.Status = STATUS_SHARING_VIOLATION;
        else if (rc == -ENOENT)
@@ -7340,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;
@@ -9483,7 +9597,6 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command)
 
        if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) &&
            command != SMB2_NEGOTIATE_HE &&
-           command != SMB2_SESSION_SETUP_HE &&
            command != SMB2_OPLOCK_BREAK_HE)
                return true;
 
@@ -9595,9 +9708,13 @@ int smb3_check_sign_req(struct ksmbd_work *work)
        } else {
                chann = lookup_chann_list(work->sess, conn);
                if (!chann) {
-                       return 0;
+                       if (le16_to_cpu(hdr->Command) != SMB2_SESSION_SETUP_HE ||
+                           !(hdr->Flags & SMB2_FLAGS_SIGNED))
+                               return 0;
+                       signing_key = work->sess->smb3signingkey;
+               } else {
+                       signing_key = chann->smb3signingkey;
                }
-               signing_key = chann->smb3signingkey;
        }
 
        if (!signing_key) {
@@ -9632,13 +9749,14 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
        struct channel *chann;
        char signature[SMB2_CMACAES_SIZE];
        struct kvec *iov;
+       u16 command = conn->ops->get_cmd_val(work);
        int n_vec = 1;
        char *signing_key;
 
        hdr = ksmbd_resp_buf_curr(work);
 
-       if (conn->binding == false &&
-           le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
+       if (command == SMB2_SESSION_SETUP_HE &&
+           (!conn->binding || hdr->Status != STATUS_SUCCESS)) {
                signing_key = work->sess->smb3signingkey;
        } else {
                chann = lookup_chann_list(work->sess, work->conn);
@@ -9690,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);
        }
 }
 
index c2512dbcdec8569a890840dff23e434f7e3f4b68..aa06c8c905f1fc35cd6043e4e9074c65e3d91189 100644 (file)
@@ -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;
index 9c59c8f73b6675ef73c8f69daf0d279d22d61142..67b39b4d218cc93abd1de437bf152923bbd8c0ce 100644 (file)
@@ -258,6 +258,7 @@ static int sid_to_id(struct mnt_idmap *idmap,
                     struct smb_sid *psid, uint sidtype,
                     struct smb_fattr *fattr)
 {
+       const struct smb_sid *sid_prefix;
        int rc = -EINVAL;
 
        /*
@@ -279,6 +280,12 @@ static int sid_to_id(struct mnt_idmap *idmap,
                kuid_t uid;
                uid_t id;
 
+               /* Only the server domain RID has a local uid representation. */
+               sid_prefix = &server_conf.domain_sid;
+               if (psid->num_subauth != sid_prefix->num_subauth + 1 ||
+                   compare_sids(psid, sid_prefix))
+                       return -EINVAL;
+
                id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
                uid = KUIDT_INIT(id);
                uid = from_vfsuid(idmap, &init_user_ns, VFSUIDT_INIT(uid));
@@ -290,6 +297,12 @@ static int sid_to_id(struct mnt_idmap *idmap,
                kgid_t gid;
                gid_t id;
 
+               /* Local gids are represented by S-1-22-2-<gid>. */
+               sid_prefix = &sid_unix_groups;
+               if (psid->num_subauth != sid_prefix->num_subauth + 1 ||
+                   compare_sids(psid, sid_prefix))
+                       return -EINVAL;
+
                id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
                gid = KGIDT_INIT(id);
                gid = from_vfsgid(idmap, &init_user_ns, VFSGIDT_INIT(gid));
@@ -900,9 +913,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
 
                rc = sid_to_id(idmap, owner_sid_ptr, SIDOWNER, fattr);
                if (rc) {
-                       pr_err("%s: Error %d mapping Owner SID to uid\n",
-                              __func__, rc);
+                       ksmbd_debug(SMB, "Owner SID has no Unix uid mapping\n");
                        owner_sid_ptr = NULL;
+                       rc = 0;
                }
        }
 
@@ -918,9 +931,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
                }
                rc = sid_to_id(idmap, group_sid_ptr, SIDUNIX_GROUP, fattr);
                if (rc) {
-                       pr_err("%s: Error %d mapping Group SID to gid\n",
-                              __func__, rc);
+                       ksmbd_debug(SMB, "Group SID has no Unix gid mapping\n");
                        group_sid_ptr = NULL;
+                       rc = 0;
                }
        }
 
index 287f3e675cd30ab3aca328822eea92c03f4d12cc..b9e27307a26c1530b1d89c79b69deddcf2df33d2 100644 (file)
@@ -105,6 +105,7 @@ struct ksmbd_file {
        __u64                           change_time;
        __u64                           allocation_size;
        __u64                           itime;
+       __u64                           open_mtime;
 
        bool                            is_nt_open;
        bool                            attrib_only;
index c6909716b041518ad91437d89e7a7adb7a0194a1..89501e8bd2f8458c25b6c69b8474ba7eda12c0dc 100644 (file)
@@ -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);
 }
index 0075b6d5a1b5ff739a5a3e20453babc380d572f0..8dd9c0266e21694203c1199689f1293c44464765 100644 (file)
@@ -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.
@@ -230,6 +225,29 @@ xrep_cow_mark_missing_staging_rmap(
                        xfs_gbno_to_fsb(cur->bc_group, rec_bno), rec_len);
 }
 
+/*
+ * 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;
-}
+       if (got->br_startoff < badoff) {
+               const int64_t   delta = badoff - got->br_startoff;
 
-#define REPLACE_LEFT_SIDE      (1U << 0)
-#define REPLACE_RIGHT_SIDE     (1U << 1)
-
-/*
- * 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);
-
-       new.br_startoff = got->br_startoff;
-       new.br_startblock = repl->fsbno;
-       new.br_blockcount = repl->len;
-       xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
+       if (got->br_blockcount == 0)
+               goto bad;
+
+       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;
 }
 
index c6a210f7508fc2accda89cbea9818a7e10d12a8f..b2cf6e5439d915f87b91cb1583312275726e134e 100644 (file)
@@ -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,
index 10950e4bd4c3c090a6d6d78df48b826e82334afa..079dc4e691a01a1c16652ba55526b62f2aed8d49 100644 (file)
@@ -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;
 }
index 493dcf5cc6c1595692cb717d4244956bbf50171f..3ec41c19835116201112a0db2e8ed294c316f3fa 100644 (file)
@@ -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));
 }
 
index 482f899a518a8533264edc5afc0b63798ff5d90d..2bd2c0351b35f738b0a590a25e77e1d3318f6b29 100644 (file)
@@ -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 */
index de3f22f310f7ebfb7eeb63b8797d8cf1c9b46d30..52c24d3d4be6ce07d516278909fe69af42160abf 100644 (file)
@@ -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);
index 0d10ce2910c2cbb41c9448c2208989cfde305cd0..4e7c540c8d2307e4781258f812a82fa10bd6eca7 100644 (file)
@@ -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)
index 043be93c7148844402379e32d137714976a00ae2..564d19a97a2f8f6cd975be9e842d9f8a30a39b5e 100644 (file)
@@ -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);
index a3f1abc91390351e5cdb60847cec71062aec97a5..6d7d3523b71f25034804c55ac90f69419586aa4d 100644 (file)
@@ -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;
index 1b7d9e07a27d3db3d48a8d1188d046762dd3104b..d5d39d82749e5ca5daa479f851323eab8c087f32 100644 (file)
@@ -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),
index c7c4a71b6fa7c65595c035f376fe6ef8718584a8..2ce24bfe4c0fab63ab1c315181052f7751811869 100644 (file)
@@ -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;
index 3b9f262f8e9128c3e9e8ab3a229b066b7c0591bd..c88b9ade7389dd42f44388194cd28e00315bc77e 100644 (file)
@@ -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;
+       }
+}
index c477b33616304006b5400403c03ac4c64703baa3..eaaf094154b9f9e882dc3931b91462fa49e1b290 100644 (file)
@@ -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__ */
index edc368938f3054e3770c79f8e79be32b0e45d3b8..639f875a8fb25ab8327c97df1c51d82e22c1a1de 100644 (file)
@@ -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;
index 5f984bf5698a89ede88d62c0cd1a240c07572332..fdb011e6ef60011b2b90144f8c702a206e3f798d 100644 (file)
@@ -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))
index a5c188b78138270095b92badbf7fda5f039d9de8..4801361366359b15936b6f3f497714e37294c7ba 100644 (file)
@@ -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)
index 676777064c2d76dece120cf9f1f9275c6c477979..b6271218732412e42a7d50cebfff04f792daff37 100644 (file)
@@ -780,6 +780,23 @@ static const struct kobj_type xfs_zoned_ktype = {
        .default_groups = xfs_zoned_groups,
 };
 
+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];
index 1622fe80ad3ed4a9de73af54e8893384f025b180..25e5f8fae2f3aba4ed5dde6368f1614621f0a2cb 100644 (file)
@@ -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__ */
index d478693674f9526ad4dc3a13674ecc8d18416ad7..aeb89ac53bf190bc2c947465dfe03d505bf46afc 100644 (file)
@@ -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
index 08d8b34f467e72c1c876ec164dc3d8c4cc31e22c..7d13fa7ab30a7521ac2163814658fa61033f6ea6 100644 (file)
@@ -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);
 }
index 8725ba92ff9163a070085df7bd2e1dab0ff31a9b..cc2937185a9f769bb63b62fb84682284f85290eb 100644 (file)
@@ -101,17 +101,6 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index)
 #define drm_exec_for_each_locked_object_reverse(exec, obj)             \
        __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec))
 
-/*
- * Helper to drm_exec_until_all_locked(). Don't use directly.
- *
- * Since labels can't be defined local to the loop's body we use a jump pointer
- * to make sure that the retry is only used from within the loop's body.
- */
-#define __drm_exec_until_all_locked(exec, _label)                       \
-_label:                                                                         \
-       for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \
-            drm_exec_cleanup(exec);)
-
 /**
  * drm_exec_until_all_locked - loop until all GEM objects are locked
  * @exec: drm_exec object
@@ -119,9 +108,18 @@ _label:                                                                     \
  * Core functionality of the drm_exec object. Loops until all GEM objects are
  * locked and no more contention exists. At the beginning of the loop it is
  * guaranteed that no GEM object is locked.
+ *
+ * A global label name drm_exec_retry is used, if you need to use more than one
+ * instance of this macro in the same function the label needs to be made local
+ * to the block with the __label__ keyword.
  */
 #define drm_exec_until_all_locked(exec)                                        \
-       __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec))
+       for (bool const __maybe_unused __drm_exec_loop = false;         \
+            drm_exec_cleanup(exec);)                                   \
+               if (false) {                                            \
+drm_exec_retry: __maybe_unused;                                                \
+                       continue;                                       \
+               } else
 
 /**
  * drm_exec_retry_on_contention - restart the loop to grap all locks
@@ -129,12 +127,14 @@ _label:                                                                    \
  *
  * Control flow helper to continue when a contention was detected and we need to
  * clean up and re-start the loop to prepare all GEM objects.
+ * The __drm_exec_loop check exists to prevent usage outside of an
+ * drm_exec_until_all_locked() loop.
  */
 #define drm_exec_retry_on_contention(exec)                     \
        do {                                                    \
                if (unlikely(drm_exec_is_contended(exec)))      \
-                       goto *__drm_exec_retry_ptr;             \
-       } while (0)
+                       goto drm_exec_retry;                    \
+       } while (__drm_exec_loop)
 
 /**
  * drm_exec_is_contended - check for contention
@@ -154,12 +154,14 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec)
  *
  * Unconditionally retry the loop to lock all objects. For consistency,
  * the exec object needs to be newly initialized.
+ * The __drm_exec_loop check exists to prevent usage outside of an
+ * drm_exec_until_all_locked() loop.
  */
 #define drm_exec_retry(_exec)                                  \
        do {                                                    \
                WARN_ON((_exec)->contended != DRM_EXEC_DUMMY);  \
-               goto *__drm_exec_retry_ptr;                     \
-       } while (0)
+               goto drm_exec_retry;                            \
+       } while (__drm_exec_loop)
 
 /**
  * drm_exec_ticket - return the ww_acquire_ctx for this exec context
index 6a46f755daba0d22b1097033db1c332373b2715d..7e077484c5bbfc1c04a31d9f83bcab1bc1613d62 100644 (file)
@@ -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 *
index 17f9f015bf7df56ab49a025adf63b1c296a23f08..071221fe5c57d7c47bb20a168676f76c97035bc3 100644 (file)
@@ -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
index 04c4d0c6fd34bcc7df44b9ecdafe787d9551b089..c2505b9eb63e5d05bcd8bab9df970c60a3679849 100644 (file)
@@ -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
index 17eca3dfc59e257d64e2dd52eb66b42ecb9a095e..e71d83ee0aef3c5ed43466573020996f0a849a35 100644 (file)
@@ -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;
 }
index 240401d9b25b9ec50e12a00f9e2c26aa4ffa4af7..c09b7994de4eb5a8f716bd475688099794a5c707 100644 (file)
@@ -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);
index d3788a3d0942b2fd92a76af66e2e0780591c8b0a..056e0efa649fd3307b91113b44d40a723590729b 100644 (file)
@@ -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
index 6f7edb3590ef97e9e493842499d15f3e24f050b9..cfbbf8ba28f6322b50c1837988d7be61d819f1b0 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * DAMON api
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #ifndef _DAMON_H_
@@ -843,11 +841,13 @@ struct damon_attrs {
  * including damon_call() and damos_walk().
  *
  * @ops:       Set of monitoring operations for given use cases.
+ * @probes:    Head of probes (&damon_probe) list.
  * @addr_unit: Scale factor for core to ops address conversion.
  * @min_region_sz:     Minimum region size.
  * @pause:     Pause kdamond main loop.
  * @adaptive_targets:  Head of monitoring targets (&damon_target) list.
  * @schemes:           Head of schemes (&damos) list.
+ * @rnd_state: Per-ctx PRNG state for damon_rand().
  */
 struct damon_ctx {
        struct damon_attrs attrs;
@@ -905,7 +905,6 @@ struct damon_ctx {
        struct list_head adaptive_targets;
        struct list_head schemes;
 
-       /* Per-ctx PRNG state for damon_rand(); kdamond is the sole consumer. */
        struct rnd_state rnd_state;
 };
 
@@ -1065,9 +1064,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx)
 
 static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs)
 {
-       /* {aggr,sample}_interval are unsigned long, hence could overflow */
-       return min(attrs->aggr_interval / attrs->sample_interval,
+       unsigned long sample_interval;
+       unsigned long max_nr_accesses;
+
+       sample_interval = attrs->sample_interval ? : 1;
+       max_nr_accesses = min(attrs->aggr_interval / sample_interval,
                        (unsigned long)UINT_MAX);
+       return max_nr_accesses ? : 1;
 }
 
 
index d10897b3a1e35017d0286a822106f5fd4b0d7edd..50ce731a2b78f140b1990ee49b976a8d2af05f50 100644 (file)
@@ -2444,6 +2444,11 @@ static inline struct mnt_idmap *file_mnt_idmap(const struct file *file)
        return mnt_idmap(file->f_path.mnt);
 }
 
+static inline bool file_owner_or_capable(const struct file *file)
+{
+       return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file));
+}
+
 /**
  * is_idmapped_mnt - check whether a mount is mapped
  * @mnt: the mount to check
index 861327b33e41dccf9c95c75a67cf292f5fd6d52c..91595e750936e515ba0d829c53b31f89cd2fb943 100644 (file)
@@ -6,5 +6,6 @@
 #include <linux/compiler.h>    /* For __pure */
 
 bool __pure glob_match(char const *pat, char const *str);
+bool __pure glob_match_len(char const *pat, char const *str, size_t len);
 
 #endif /* _LINUX_GLOB_H */
index e71056553108508c9ac51172f989a2d54872085b..ab5cc8db3fbb3b81071fe72f9ae3467975fe06ab 100644 (file)
@@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info {
  * @attr_usage_id:     Usage Id of a field, e.g. X-axis for a gyro.
  * @raw_size:          Response size for a read request.
  * @raw_data:          Place holder for received response.
+ * @index:             Current write index into raw_data for multi-byte reads.
+ * @max_raw_size:      Total buffer size for multi-byte reads; 0 for single-value reads.
  */
 struct sensor_hub_pending {
        bool status;
@@ -51,6 +53,8 @@ struct sensor_hub_pending {
        u32 attr_usage_id;
        int raw_size;
        u8  *raw_data;
+       u32 index;
+       u32 max_raw_size;
 };
 
 /**
@@ -183,6 +187,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        bool is_signed
 );
 
+/**
+ * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request
+ * @hsdev:             Hub device instance.
+ * @usage_id:          Attribute usage id of parent physical device as per spec
+ * @attr_usage_id:     Attribute usage id as per spec
+ * @report_id:         Report id to look for
+ * @flag:              Synchronous or asynchronous read
+ * @buffer_size:       Size of the buffer in bytes
+ * @buffer:            Buffer to store the read data
+ *
+ * Issues a synchronous or asynchronous read request for an input attribute,
+ * accumulating data into the provided buffer until it is full.
+ * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a
+ * negative error code.
+ */
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+                                     u32 usage_id, u32 attr_usage_id,
+                                     u32 report_id,
+                                     enum sensor_hub_read_flags flag,
+                                     u32 buffer_size, u8 *buffer);
+
 /**
 * sensor_hub_set_feature() - Feature set request
 * @hsdev:      Hub device instance.
index 18f9c662cf4cbe2cc90542e83b7d6ebe7e7bb06f..c109722b1969a95a21f4a96f7cffecd987a14d33 100644 (file)
@@ -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;
 }
 
 /**
index dccbeb25f70141982160c776f3cc727296def2b7..6032eea2539a60d0476d85667bda3bfcad8f1425 100644 (file)
@@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev)
 #define __in_dev_put(idev)  refcount_dec(&(idev)->refcnt)
 #define in_dev_hold(idev)   refcount_inc(&(idev)->refcnt)
 
+static inline bool in_dev_hold_safe(struct in_device *idev)
+{
+       return refcount_inc_not_zero(&idev->refcnt);
+}
+
 #endif /* __KERNEL__ */
 
 static __inline__ __be32 inet_make_mask(int logmask)
index 61e876e255e89cf0fb974078a1bd3f753b2a94fe..f23d4b218da079729021e31cfd032019ea4f2497 100644 (file)
@@ -120,14 +120,18 @@ struct page_ext_iter {
  * page_ext_iter_begin() - Prepare for iterating through page extensions.
  * @iter: page extension iterator.
  * @pfn: PFN of the page we're interested in.
+ * @count: maximum number of page extensions to return.
  *
  * Must be called with RCU read lock taken.
  *
  * Return: NULL if no page_ext exists for this page.
  */
 static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter,
-                                               unsigned long pfn)
+               unsigned long pfn, unsigned long count)
 {
+       if (!count)
+               return NULL;
+
        iter->index = 0;
        iter->start_pfn = pfn;
        iter->page_ext = page_ext_lookup(pfn);
@@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter,
 /**
  * page_ext_iter_next() - Get next page extension
  * @iter: page extension iterator.
+ * @count: maximum number of page extensions to return.
  *
  * Must be called with RCU read lock taken.
  *
  * Return: NULL if no next page_ext exists.
  */
-static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter)
+static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter,
+               unsigned long count)
 {
        unsigned long pfn;
 
        if (WARN_ON_ONCE(!iter->page_ext))
                return NULL;
 
-       iter->index++;
+       if (++iter->index >= count)
+               return NULL;
        pfn = iter->start_pfn + iter->index;
 
        if (page_ext_iter_next_fast_possible(pfn))
@@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite
  * IMPORTANT: must be called with RCU read lock taken.
  */
 #define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \
-       for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\
-               __page_ext && __iter.index < __pgcount;          \
-               __page_ext = page_ext_iter_next(&__iter))
+       for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \
+               __page_ext; \
+               __page_ext = page_ext_iter_next(&__iter, __pgcount))
 
 #else /* !CONFIG_PAGE_EXTENSION */
 struct page_ext;
index 9d4ca5c218a0fb5bc9e07e202524ce7613a200c8..272b1274efdc1c18dc5117e69924e1181338b2af 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/mmzone.h>
 #include <linux/scatterlist.h>
 
-/* This value should always be a power of 2, see page_reporting_cycle() */
 #define PAGE_REPORTING_CAPACITY                32
 #define PAGE_REPORTING_ORDER_UNSPECIFIED       -1
 
@@ -22,6 +21,9 @@ struct page_reporting_dev_info {
 
        /* Minimal order of page reporting */
        unsigned int order;
+
+       /* Max pages per report batch; 0 (default) means PAGE_REPORTING_CAPACITY */
+       unsigned int capacity;
 };
 
 /* Tear-down and bring-up for page reporting devices */
index a8553401b1c93fd5685c798a41d1df856af3cfc7..d5e35f24738d5cb9cf4b365417f05e1c495f5f0f 100644 (file)
@@ -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)
 {
index f8b406b0a1af5d8bf7bcdfd0182c794ac09c3ab2..3c2b8c355ab3a85d4e4665dd073943466111b15f 100644 (file)
@@ -190,6 +190,7 @@ int         rpc_switch_client_transport(struct rpc_clnt *,
                                const struct rpc_timeout *);
 
 void           rpc_shutdown_client(struct rpc_clnt *);
+void           rpc_hold_client(struct rpc_clnt *);
 void           rpc_release_client(struct rpc_clnt *);
 void           rpc_task_release_transport(struct rpc_task *);
 void           rpc_task_release_client(struct rpc_task *);
index 4a0c36f40fe2c25ff6533e13df015c35a3eb722b..e0d838c9ce9382802d9a86b8581b175854dad8b9 100644 (file)
@@ -292,13 +292,18 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
        {                                                               \
        }                                                               \
        static inline bool                                              \
+       __trace_##name##_enabled(void)                                  \
+       {                                                               \
+               return static_branch_unlikely(&__tracepoint_##name.key);\
+       }                                                               \
+       static inline bool                                              \
        trace_##name##_enabled(void)                                    \
        {                                                               \
                if (IS_ENABLED(CONFIG_LOCKDEP)) {                       \
                        WARN_ONCE(!rcu_is_watching(),                   \
                                  "RCU not watching for tracepoint");   \
                }                                                       \
-               return static_branch_unlikely(&__tracepoint_##name.key);\
+               return __trace_##name##_enabled();                      \
        }
 
 #define __DECLARE_TRACE(name, proto, args, cond, data_proto)                   \
@@ -457,6 +462,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
        {                                                               \
        }                                                               \
        static inline bool                                              \
+       __trace_##name##_enabled(void)                                  \
+       {                                                               \
+               return false;                                           \
+       }                                                               \
+       static inline bool                                              \
        trace_##name##_enabled(void)                                    \
        {                                                               \
                return false;                                           \
index 539bbbe54b14e8108ff7304d7a08bc605655cc31..8ced27a8229b6e0580f934be2223676cc123307b 100644 (file)
@@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev)
        refcount_inc(&idev->refcnt);
 }
 
+static inline bool in6_dev_hold_safe(struct inet6_dev *idev)
+{
+       return refcount_inc_not_zero(&idev->refcnt);
+}
+
 /* called with rcu_read_lock held */
 static inline bool ip6_ignore_linkdown(const struct net_device *dev)
 {
index 38186a245f14dbd2a26cbc331e7f1b3e72f1a607..50f0eef71fb1986c11c9db30337d64c650adfc3b 100644 (file)
@@ -3413,8 +3413,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb)
 #define hci_iso_flags_pack(pb, ts)     ((pb & 0x03) | ((ts & 0x01) << 2))
 
 /* ISO data length and flags pack/unpack */
-#define hci_iso_data_len_pack(h, f)    ((__u16) ((h) | ((f) << 14)))
-#define hci_iso_data_len(h)            ((h) & 0x3fff)
+#define hci_iso_data_len_pack(h, f)    ((__u16) (((h) & 0x0fff) | \
+                                                 (((f) & 0x3) << 14)))
+#define hci_iso_data_len(h)            ((h) & 0x0fff)
 #define hci_iso_data_flags(h)          ((h) >> 14)
 
 /* codec transport types */
index 7e15da47fe3ac39f4be6abfe593396b19fb5e015..e7133ff87fbfa81b836fcff87cb64fae601fe0da 100644 (file)
@@ -985,6 +985,7 @@ enum {
        HCI_CONN_AUTH_FAILURE,
        HCI_CONN_PER_ADV,
        HCI_CONN_BIG_CREATED,
+       HCI_CONN_CREATE,
        HCI_CONN_CREATE_CIS,
        HCI_CONN_CREATE_BIG_SYNC,
        HCI_CONN_BIG_SYNC,
@@ -2429,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);
index 1640cc9bf83ac1e4be499dcd877870564ec7db10..ef6ce1c20a4f05e45d255f4c30a3c5b2ab49058b 100644 (file)
@@ -617,7 +617,8 @@ struct l2cap_chan {
 struct l2cap_ops {
        char                    *name;
 
-       struct l2cap_chan       *(*new_connection) (struct l2cap_chan *chan);
+       int                     (*new_connection)(struct l2cap_chan *chan,
+                                                 struct l2cap_chan *new_chan);
        int                     (*recv) (struct l2cap_chan * chan,
                                         struct sk_buff *skb);
        void                    (*teardown) (struct l2cap_chan *chan, int err);
@@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
        return (seq + 1) % (chan->tx_win_max + 1);
 }
 
-static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
+static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
+                                              struct l2cap_chan *new_chan)
 {
-       return NULL;
+       return -EOPNOTSUPP;
 }
 
 static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
 void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
 int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
-void l2cap_chan_set_defaults(struct l2cap_chan *chan);
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
 int l2cap_ertm_init(struct l2cap_chan *chan);
 void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
index 8188ad200de5393ed324d725016e3fef7160cd26..f5abf1db7558bcb9d7654e5c60f0bf02ae2380bc 100644 (file)
@@ -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;
 
index dfca298bec9ccd0ee99fb3826d2cc1da2028eb55..caefd6da86939b4c1d9702a84860fe2de2c46697 100644 (file)
@@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags)
 
 static inline size_t guehdr_priv_flags_len(__be32 flags)
 {
-       return 0;
+       return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0;
 }
 
 /* Validate standard and private flags. Returns non-zero (meaning invalid)
index 49297fec448a8209b5f8b993b1b8ad008a1549ea..417ff51f62fc8edb80475706832e9c6008854893 100644 (file)
@@ -752,7 +752,8 @@ struct ip_vs_protocol {
 
        void (*state_transition)(struct ip_vs_conn *cp, int direction,
                                 const struct sk_buff *skb,
-                                struct ip_vs_proto_data *pd);
+                                struct ip_vs_proto_data *pd,
+                                unsigned int iph_len);
 
        int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
 
index 13c87baf018ee199fa57e90a767e053f64c6435b..04acb6791dbd9720289243fc5141bb7b059618dd 100644 (file)
@@ -305,6 +305,14 @@ struct mana_recv_buf_oob {
 
        void *buf_va;
        bool from_pool; /* allocated from a page pool */
+       /* head page of the page_pool fragment; valid only when
+        * from_pool && frag_count > 1.
+        */
+       struct page *pp_page;
+       /* Fragment offset plus rxq->headroom, passed to
+        * page_pool_dma_sync_for_cpu().
+        */
+       u32 dma_sync_offset;
 
        /* SGL of the buffer going to be sent as part of the work request. */
        u32 num_sge;
index 7b23b245a5a86ae4a9fe874b1c65c0d5b7846cf5..ce414118962f5480a758f4c2686aff97dc2d0e93 100644 (file)
@@ -155,11 +155,12 @@ struct flow_offload_tuple {
                                        tun_num:2,
                                        in_vlan_ingress:2;
        u16                             mtu;
+       u32                             dst_cookie;
+       struct dst_entry                *dst_cache;
+
        union {
                struct {
-                       struct dst_entry *dst_cache;
                        u32             ifidx;
-                       u32             dst_cookie;
                };
                struct {
                        u32             ifidx;
@@ -357,6 +358,8 @@ static inline int nf_flow_register_bpf(void)
 
 void nf_flow_offload_add(struct nf_flowtable *flowtable,
                         struct flow_offload *flow);
+void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
+                            struct flow_offload *flow);
 void nf_flow_offload_del(struct nf_flowtable *flowtable,
                         struct flow_offload *flow);
 void nf_flow_offload_stats(struct nf_flowtable *flowtable,
index affee44bd38e31f71cd8da75e3dd983840b52209..cccc662561aac391999148ce8ae0e8df03f08d67 100644 (file)
@@ -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
index cb7b82f2cbc7fd8778a262edf4813a2734cb6127..97754ea0a8279d5ef1830f180dffcd5a86edf87e 100644 (file)
@@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a)
        return false;
 }
 
-static inline int tcf_pedit_nkeys(const struct tc_action *a)
+/* Must be called with act->tcfa_lock held to ensure consistency of parallel
+ * reads of the same action's pedit keys (e.g. flow_offload count vs fill).
+ * Note, this is only used for pedit offload.
+ */
+static inline int tcf_pedit_nkeys_locked(const struct tc_action *a)
 {
-       struct tcf_pedit_parms *parms;
-       int nkeys;
-
-       rcu_read_lock();
-       parms = to_pedit_parms(a);
-       nkeys = parms->tcfp_nkeys;
-       rcu_read_unlock();
-
-       return nkeys;
+       lockdep_assert_held(&a->tcfa_lock);
+       return rcu_dereference_protected(to_pedit(a)->parms,
+                                        lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys;
 }
 
 static inline u32 tcf_pedit_htype(const struct tc_action *a, int index)
index 519a0156a05cac56997e2864449d05b7ffbbca46..a6d69aaa6cd2d0e6752b7b3048caad7d0b406591 100644 (file)
@@ -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;
index 029f5115b2ea09306fe1b8006372891bc9e13e8a..8694eeadd753e586f42195fe867ce2c5c0e58617 100644 (file)
@@ -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 *,
index 7e2011830ba4bd48fe8a7df3c88285d6da6d0a71..f6b286fa59f214b98ea705b30d05ab08ce47e459 100644 (file)
@@ -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;
 
index 4bcbf19d75acec2df73fd545beaafb3f4d09014d..53f6d02889dbd1fc4c2316d1a070fa754e76d33d 100644 (file)
@@ -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)
index aa57cc8f896bedae1b5d32bb397a893faa3a4cf4..7a8ee5d1a44e4d0d9aa04a42cf5b13d5dd67c3b9 100644 (file)
@@ -1,6 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #undef TRACE_SYSTEM
-#define TRACE_SYSTEM memory_failure
+/*
+ * For historical versions, memory_failure_event is in ras subsystem,
+ * some user programs depend on it.
+ */
+#define TRACE_SYSTEM ras
 #define TRACE_INCLUDE_FILE memory-failure
 
 #if !defined(_TRACE_MEMORY_FAILURE_H) || defined(TRACE_HEADER_MULTI_READ)
index 9b576603b3f14cc3c0bff28ee7173c5152787938..0a13baf3d8d1446d5a51a9cfecd8087e8ce3d2da 100644 (file)
@@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 {
        __u64 buf_size;            /* in - size of buffer
                                            * out - on EOVERFLOW: needed size
                                            *       to store item */
-       __u64 buf[];                       /* out - found items */
+       __u8 buf[];                        /* out - found items */
 };
 
 /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */
index 5a50f0675fe58f74bb2521e3a4a4d413b2b644fc..cf2bd068e331b56ac34b935c59c846cc16b734ea 100644 (file)
@@ -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;
 
index d0580c754bf867070c6aa00acb3167ff210d58e3..26ea841a22e700a5429280a923680b9ea7e619f5 100644 (file)
@@ -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;
index 3cd29477fff2d3e35d7024b8a8905cc177fd36f0..de0129bceaba30a35d69ffa5abe84e78529bac2b 100644 (file)
@@ -287,8 +287,6 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
                iov = kmalloc_objs(struct iovec, nr_avail);
                if (unlikely(!iov))
                        return -ENOMEM;
-               if (arg->mode & KBUF_MODE_FREE)
-                       kfree(arg->iovs);
                arg->iovs = iov;
                nr_iovs = nr_avail;
        } else if (nr_avail < nr_iovs) {
@@ -330,6 +328,9 @@ 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->iovs != org_iovs && (arg->mode & KBUF_MODE_FREE))
+               kfree(org_iovs);
+
        if (head == tail)
                req->flags |= REQ_F_BL_EMPTY;
 
index da1f6c5d07f8a092ebbfbd5d2c716a497828557d..23e8a85111bca6ee71258b11c081bd5d7a16053d 100644 (file)
@@ -337,7 +337,7 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr,
 
        ptr = io_uring_validate_mmap_request(filp, pgoff);
        if (IS_ERR(ptr))
-               return -ENOMEM;
+               return PTR_ERR(ptr);
 
        /*
         * Some architectures have strong cache aliasing requirements.
index 3ff9098573dbf49eb8ac7df8ca74347c8baa5188..3067c93439919a0efc3608e53a56c15d40dd8f39 100644 (file)
@@ -93,19 +93,38 @@ static void io_msg_remote_post(struct io_ring_ctx *ctx, struct io_kiocb *req,
        io_req_task_work_add_remote(req, IOU_F_TWQ_LAZY_WAKE);
 }
 
+static int io_msg_ring_cqe_flags(struct io_ring_ctx *target_ctx,
+                                const struct io_msg *msg, u32 *flags)
+{
+       *flags = 0;
+
+       if (!(msg->flags & IORING_MSG_RING_FLAGS_PASS))
+               return 0;
+
+       *flags = msg->cqe_flags;
+       if ((*flags & IORING_CQE_F_32) &&
+           !(target_ctx->flags & (IORING_SETUP_CQE32 |
+                                  IORING_SETUP_CQE_MIXED)))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int io_msg_data_remote(struct io_ring_ctx *target_ctx,
                              struct io_msg *msg)
 {
        struct io_kiocb *target;
-       u32 flags = 0;
+       u32 flags;
+       int ret;
 
-       target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO)  ;
+       ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags);
+       if (ret)
+               return ret;
+
+       target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
        if (unlikely(!target))
                return -ENOMEM;
 
-       if (msg->flags & IORING_MSG_RING_FLAGS_PASS)
-               flags = msg->cqe_flags;
-
        io_msg_remote_post(target_ctx, target, msg->len, flags, msg->user_data);
        return 0;
 }
@@ -130,8 +149,9 @@ static int __io_msg_ring_data(struct io_ring_ctx *target_ctx,
        if (io_msg_need_remote(target_ctx))
                return io_msg_data_remote(target_ctx, msg);
 
-       if (msg->flags & IORING_MSG_RING_FLAGS_PASS)
-               flags = msg->cqe_flags;
+       ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags);
+       if (ret)
+               return ret;
 
        ret = -EOVERFLOW;
        if (target_ctx->flags & IORING_SETUP_IOPOLL) {
index a4c872870d81c6dddb64063f748a1ae89b1787ef..e6ee15571e85c41c5eb2bc1b4a004305ed0a449c 100644 (file)
@@ -139,11 +139,11 @@ void tctx_task_work(struct callback_head *cb)
  */
 static void io_ctx_mark_taskrun(struct io_ring_ctx *ctx)
 {
+       lockdep_assert_in_rcu_read_lock();
+
        if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) {
-               struct io_rings *rings;
+               struct io_rings *rings = rcu_dereference(ctx->rings_rcu);
 
-               guard(rcu)();
-               rings = rcu_dereference(ctx->rings_rcu);
                atomic_or(IORING_SQ_TASKRUN, &rings->sq_flags);
        }
 }
@@ -153,6 +153,9 @@ void io_req_local_work_add(struct io_kiocb *req, unsigned flags)
        struct io_ring_ctx *ctx = req->ctx;
        int nr_wait;
 
+       /* pairs with synchronize_rcu() in io_ring_exit_work() */
+       guard(rcu)();
+
        /*
         * We don't know how many requests there are in the link and whether
         * they can even be queued lazily, fall back to non-lazy.
index 7b25dcd9d05fc56491391626926b9a2d4d8b5598..c14c22cff49e37c05a4edeb21d374d3cf8cf277e 100644 (file)
@@ -90,7 +90,7 @@ static void io_uring_cmd_del_cancelable(struct io_uring_cmd *cmd,
 }
 
 /*
- * Mark this command as concelable, then io_uring_try_cancel_uring_cmd()
+ * Mark this command as cancelable, then io_uring_try_cancel_uring_cmd()
  * will try to cancel this issued command by sending ->uring_cmd() with
  * issue_flags of IO_URING_F_CANCEL.
  *
@@ -168,7 +168,7 @@ void __io_uring_cmd_done(struct io_uring_cmd *ioucmd, s32 ret, u64 res2,
        }
        io_req_uring_cleanup(req, issue_flags);
        if (req->flags & REQ_F_IOPOLL) {
-               /* order with io_iopoll_req_issued() checking ->iopoll_complete */
+               /* order with io_do_iopoll() checking ->iopoll_completed */
                smp_store_release(&req->iopoll_completed, 1);
        } else if (issue_flags & IO_URING_F_COMPLETE_DEFER) {
                if (WARN_ON_ONCE(issue_flags & IO_URING_F_UNLOCKED))
index dcc657d35776d8462760d1cd29e4ed679ae7839a..562476937fa793def6c7b3e8893a46dc8966a81e 100644 (file)
@@ -62,6 +62,7 @@
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <linux/sctp.h>
+#include <linux/overflow.h>
 
 #include "audit.h"
 
@@ -950,7 +951,7 @@ main_queue:
                 *       do the multicast send and rotate records from the
                 *       main queue to the retry/hold queues */
                wait_event_freezable(kauditd_wait,
-                                    (skb_queue_len(&audit_queue) ? 1 : 0));
+                               (skb_queue_len_lockless(&audit_queue) ? 1 : 0));
        }
 
        return 0;
@@ -1283,7 +1284,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
                s.rate_limit               = audit_rate_limit;
                s.backlog_limit            = audit_backlog_limit;
                s.lost                     = atomic_read(&audit_lost);
-               s.backlog                  = skb_queue_len(&audit_queue);
+               s.backlog                  = skb_queue_len_lockless(&audit_queue);
                s.feature_bitmap           = AUDIT_FEATURE_BITMAP_ALL;
                s.backlog_wait_time        = audit_backlog_wait_time;
                s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual);
@@ -1627,7 +1628,7 @@ static void audit_receive(struct sk_buff *skb)
 
        /* can't block with the ctrl lock, so penalize the sender now */
        if (audit_backlog_limit &&
-           (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+           (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
                DECLARE_WAITQUEUE(wait, current);
 
                /* wake kauditd to try and flush the queue */
@@ -1933,7 +1934,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
                long stime = audit_backlog_wait_time;
 
                while (audit_backlog_limit &&
-                      (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+                       (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
                        /* wake kauditd to try and flush the queue */
                        wake_up_interruptible(&kauditd_wait);
 
@@ -1953,7 +1954,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
                        } else {
                                if (audit_rate_check() && printk_ratelimit())
                                        pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n",
-                                               skb_queue_len(&audit_queue),
+                                               skb_queue_len_lockless(&audit_queue),
                                                audit_backlog_limit);
                                audit_log_lost("backlog limit exceeded");
                                return NULL;
@@ -2080,7 +2081,8 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
 void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
                size_t len)
 {
-       int i, avail, new_len;
+       int avail;
+       size_t i, new_len;
        unsigned char *ptr;
        struct sk_buff *skb;
 
@@ -2090,7 +2092,12 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
        BUG_ON(!ab->skb);
        skb = ab->skb;
        avail = skb_tailroom(skb);
-       new_len = len<<1;
+
+       if (check_shl_overflow(len, 1, &new_len)) {
+               audit_log_format(ab, "?");
+               return;
+       }
+
        if (new_len >= avail) {
                /* Round the buffer request up to the next multiple */
                new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
index 64572f85edc832298d57e008cd4871049f22ed7f..c4673a54c4baf25dfa3654780c7d7570d4d1e9cb 100644 (file)
@@ -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)
 {
index 6515d4d3c0032f174a166cd694a49f4f89c52d65..99444eae917e4821aa997df5f3b66eb0fc6cc600 100644 (file)
@@ -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)
index 591e3aa487fc185d84e0596e82551b4d7d482da2..45944b3e31ca4941f2bc6679241e0456220d2f00 100644 (file)
@@ -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
index d7f3e2c2ecb1eef59abdbf83e1502ba18f998c23..ba5bd6a78fe7ba878229cd51d0b5a21428257bc3 100644 (file)
@@ -7150,6 +7150,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma)
        int err = 0;
        unsigned long pagenum;
 
+       guard(mutex)(&rb->aux_mutex);
+
        /*
         * We map this as a VM_PFNMAP VMA.
         *
index 1056422bc1013e0179a0dab5e5c98e377cfe82e2..2c0b1c02920f467d4bdf32d425a11c8ad637c2b5 100644 (file)
@@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts
        __unhash_process(post, tsk, group_dead);
        write_sequnlock(&sig->stats_lock);
 
-       tsk->sighand = NULL;
+       /*
+        * Ensure that all preceeding state is visible. Pairs with
+        * the smp_acquire__after_ctrl_dep() in the sighand == NULL
+        * path of lock_task_sighand().
+        */
+       smp_store_release(&tsk->sighand, NULL);
        spin_unlock(&sighand->siglock);
 
        __cleanup_sighand(sighand);
index 691d53fe0f648898d5b40a8ff8b2fbe9a1ee17db..e3fa7b2fac9dfc66bee17b031b5d11d2b0775158 100644 (file)
@@ -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);
@@ -3647,6 +3682,13 @@ static void scx_disable_task(struct scx_sched *sch, struct task_struct *p)
                SCX_CALL_OP_TASK(sch, disable, rq, 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
@@ -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);
        }
index 145272cb4d8a711a8634bbbad7cd3cc34042dfb2..673059fa9d728b12333c96453e58ef450b902ed5 100644 (file)
@@ -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
  *
index 9c2b32c4d755320ffdc330e7a9e34565000c5ac5..bbc0fd4cc4d7c1e94e5ef73d550513996cc592db 100644 (file)
@@ -1362,8 +1362,16 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk,
        rcu_read_lock();
        for (;;) {
                sighand = rcu_dereference(tsk->sighand);
-               if (unlikely(sighand == NULL))
+               if (unlikely(sighand == NULL)) {
+                       /*
+                        * Pairs with the smp_store_release() in
+                        * __exit_signal().  It ensures that all state
+                        * modifications to the task preceeding the store are
+                        * visible to the callers of lock_task_sighand().
+                        */
+                       smp_acquire__after_ctrl_dep();
                        break;
+               }
 
                /*
                 * This sighand can be already freed and even reused, but
index 5e633d8750d1f6e91c23be8fcc0377f9dcb7ee90..a7d3e8229c4ba494b045da2c9fb34f7402bb9c10 100644 (file)
@@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
                trigger_base_recalc_expires(timer, p);
 }
 
+/*
+ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
+ *
+ * This can race with the reaping of the task:
+ *
+ * CPU0                                        CPU1
+ *
+ * // Finds task
+ * p = pid_task(pid, pid_type);                __exit_signal(p)
+ *                                       lock(p, sighand);
+ *                                       posix_cpu_timers*_exit();
+ * sighand = lock_task_sighand(p);       unhash_task(p);
+ *                                       p->sighand = NULL;
+ *                                       unlock(sighand);
+ *
+ * In this case sighand is NULL, which means the task and the associated timer
+ * queue cannot be longer accessed safely.
+ *
+ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
+ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
+ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
+ * themself are still accessible, but not longer connected to the task.
+ *
+ * exec() works slightly differently. The task which exec()'s terminates all
+ * other threads in the thread group and runs __exit_signal() on them. As the
+ * thread group is not dead they only clean up the per task timers via
+ * posix_cpu_timers_exit().
+ *
+ * As the TGID on exec() stays the same per process timers stay queued, if they
+ * are armed. This works without a problem when exec() is done by the thread
+ * group leader. If a non-leader thread exec()'s this can end up in the
+ * following scenario:
+ *
+ * CPU0                                        CPU1
+ * // Returns old leader
+ * p = pid_task(pid, pid_type);                de_thread()
+ *                                     switch_leader()
+ *                                     release_task(old leader)
+ *                                       __exit_signal()
+ *                                       old_leader->sighand = NULL;
+ * // Returns NULL
+ * sighand = lock_task_sighand(p)
+ *
+ * That's problematic for several functions:
+ *
+ *  - posix_cpu_timer_del(): If the timer is still enqueued on the task the
+ *    underlying k_itimer will be freed which results in a UAF in
+ *    run_posix_cpu_timers() or on timerqueue related add/delete operations.
+ *    If the timer is not enqueued, the failure is harmless
+ *
+ *  - posix_cpu_timer_set(): Independent of the enqueued state that results in a
+ *    transient failure which is user space visible (-ESRCH) for regular posix
+ *    timers. But for the use case in do_cpu_nanosleep() it's the same UAF
+ *    problem just that the timer is allocated on the stack.
+ *
+ *  - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
+ *    silently ignores the rearm request, which is a functional problem as the
+ *    timer wont expire anymore.
+ */
+static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
+{
+       enum pid_type type = clock_pid_type(timer->it_clock);
+       struct cpu_timer *ctmr = &timer->it.cpu;
+
+       guard(rcu)();
+
+       for (;;) {
+               struct task_struct *t = pid_task(timer->it.cpu.pid, type);
+
+               /* Fail if the task cannot be found. */
+               if (!t)
+                       break;
+
+               /* Try to lock the task's sighand */
+               if (lock_task_sighand(t, flags))
+                       return t;
+
+               /*
+                * The next PID lookup might either fail or return the new
+                * leader. This is correct for both exit() and exec().
+                */
+       }
+
+       /*
+        * If the timer is still enqueued, warn. There is nothing safe to do
+        * here as there might be two timers in there which are removed in
+        * parallel and that will cause more damage than good. This should never
+        * happen!
+        *
+        * Ensure that the stores to the timer and timerqueue are visible:
+        *
+        * __exit_signal()
+        *   posix_cpu_timers*_exit()
+        *   write_seqlock(seqlock)
+        *      smp_wmb(); <-------
+        *   __unhash_process()   |     !pid_task()
+        *                        ----> smp_rmb();
+        *                              WARN_ON_ONCE(...)
+        */
+       smp_rmb();
+       WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
+       return NULL;
+}
 
 /*
  * Clean up a CPU-clock timer that is about to be destroyed.
@@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
  */
 static int posix_cpu_timer_del(struct k_itimer *timer)
 {
-       struct cpu_timer *ctmr = &timer->it.cpu;
-       struct sighand_struct *sighand;
        struct task_struct *p;
        unsigned long flags;
        int ret = 0;
 
-       rcu_read_lock();
-       p = cpu_timer_task_rcu(timer);
-       if (!p)
-               goto out;
+       p = timer_lock_sighand(timer, &flags);
 
-       /*
-        * Protect against sighand release/switch in exit/exec and process/
-        * thread timer list entry concurrent read/writes.
-        */
-       sighand = lock_task_sighand(p, &flags);
-       if (unlikely(sighand == NULL)) {
-               /*
-                * This raced with the reaping of the task. The exit cleanup
-                * should have removed this timer from the timer queue.
-                */
-               WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
-       } else {
+       if (likely(p)) {
                if (timer->it.cpu.firing) {
                        /*
                         * Prevent signal delivery. The timer cannot be dequeued
@@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
                unlock_task_sighand(p, &flags);
        }
 
-out:
-       rcu_read_unlock();
-
        if (!ret) {
-               put_pid(ctmr->pid);
+               put_pid(timer->it.cpu.pid);
                timer->it_status = POSIX_TIMER_DISARMED;
        }
        return ret;
@@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
        clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
        struct cpu_timer *ctmr = &timer->it.cpu;
        u64 old_expires, new_expires, now;
-       struct sighand_struct *sighand;
        struct task_struct *p;
        unsigned long flags;
        int ret = 0;
 
-       rcu_read_lock();
-       p = cpu_timer_task_rcu(timer);
-       if (!p) {
-               /*
-                * If p has just been reaped, we can no
-                * longer get any information about it at all.
-                */
-               rcu_read_unlock();
+       p = timer_lock_sighand(timer, &flags);
+       /*
+        * If p has just been reaped, we can no longer get any information about
+        * it at all.
+        */
+       if (!p)
                return -ESRCH;
-       }
 
        /*
         * Use the to_ktime conversion because that clamps the maximum
@@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
         */
        new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
 
-       /*
-        * Protect against sighand release/switch in exit/exec and p->cpu_timers
-        * and p->signal->cpu_timers read/write in arm_timer()
-        */
-       sighand = lock_task_sighand(p, &flags);
-       /*
-        * If p has just been reaped, we can no
-        * longer get any information about it at all.
-        */
-       if (unlikely(sighand == NULL)) {
-               rcu_read_unlock();
-               return -ESRCH;
-       }
-
        /* Retrieve the current expiry time before disarming the timer */
        old_expires = cpu_timer_getexpires(ctmr);
 
@@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
        /* Retry if the timer expiry is running concurrently */
        if (unlikely(ret)) {
                unlock_task_sighand(p, &flags);
-               goto out;
+               return ret;
        }
 
        /* Convert relative expiry time to absolute */
@@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
         */
        if (!sigev_none && new_expires && now >= new_expires)
                cpu_timer_fire(timer);
-out:
-       rcu_read_unlock();
        return ret;
 }
 
@@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk,
 static bool posix_cpu_timer_rearm(struct k_itimer *timer)
 {
        clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
-       struct sighand_struct *sighand;
        struct task_struct *p;
        unsigned long flags;
        u64 now;
 
-       guard(rcu)();
-       p = cpu_timer_task_rcu(timer);
-       if (!p)
-               return true;
-
-       /* Protect timer list r/w in arm_timer() */
-       sighand = lock_task_sighand(p, &flags);
-       if (unlikely(sighand == NULL))
+       p = timer_lock_sighand(timer, &flags);
+       if (unlikely(!p))
                return true;
 
        /*
index 56a328e943955e64c43c3493a8813e9c3a3d73d9..804ccae694d2151810da6ed0d2c01e069de1e529 100644 (file)
@@ -270,7 +270,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
        if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
                return length;
        length -= RB_EVNT_HDR_SIZE;
-       if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]))
+       if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) ||
+           RB_FORCE_8BYTE_ALIGNMENT)
                 length -= sizeof(event->array[0]);
        return length;
 }
@@ -2329,10 +2330,7 @@ static struct ring_buffer_desc *ring_buffer_desc(struct trace_buffer_desc *trace
        size_t len;
        int i;
 
-       if (!trace_desc)
-               return NULL;
-
-       if (cpu >= trace_desc->nr_cpus)
+       if (!trace_desc || !trace_desc->nr_cpus)
                return NULL;
 
        end = (struct ring_buffer_desc *)((void *)trace_desc + trace_desc->struct_len);
@@ -7174,7 +7172,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
                        rpos = reader->read;
                        pos += event_size;
 
-                       if (rpos >= event_size)
+                       if (rpos >= size)
                                break;
 
                        event = rb_reader_event(cpu_buffer);
index 1146b83b711a2bfa724ba08def7e19f6c4425790..18710c190c924f3d9528c424b4fdb1a5cf6dbc5a 100644 (file)
@@ -87,7 +87,7 @@ void __init disable_tracing_selftest(const char *reason)
 
 /* Pipe tracepoints to printk */
 static struct trace_iterator *tracepoint_print_iter;
-int tracepoint_printk;
+static int tracepoint_printk;
 static bool tracepoint_printk_stop_on_boot __initdata;
 static bool traceoff_after_boot __initdata;
 static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key);
@@ -5015,7 +5015,6 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf)
                                                RING_BUFFER_ALL_CPUS);
                if (ret < 0)
                        return ret;
-               ret = 0;
        }
 
        list_for_each_entry(t, &tr->tracers, list) {
index 50518b07141441f6db2b1ab67cb561ba638e0977..bcd97cb24ac911c2ce2c1793522da5dbc86b2491 100644 (file)
@@ -172,7 +172,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event,
        if (!slash)
                return false;
 
-       if (strncmp(ep->event_system, argv[0], slash - argv[0]))
+       if (strncmp(ep->event_system, argv[0], slash - argv[0]) ||
+           ep->event_system[slash - argv[0]] != '\0')
                return false;
        if (strcmp(ep->event_name, slash + 1))
                return false;
index 609325f57942784d10b68fbb46b4573b529df662..6385cd662d8d16658c965fe3ddda25da8c842394 100644 (file)
@@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len)
        return 0;
 }
 
-static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused)
+static int regex_match_glob(char *str, struct regex *r, int len)
 {
-       if (glob_match(r->pattern, str))
-               return 1;
-       return 0;
+       return glob_match_len(r->pattern, str, len) ? 1 : 0;
 }
 
 /**
index e6871230bde96dd73db35ed75c0e093443f9d7fc..dc15658a887cb74d7ef4504dc869d652ef9d1c17 100644 (file)
@@ -839,8 +839,10 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
                        seq_buf_puts(&s, "__data_loc ");
                        seq_buf_puts(&s, field->type);
 
-                       if (WARN_ON_ONCE(!seq_buf_buffer_left(&s)))
+                       if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) {
+                               kfree(type);
                                goto free;
+                       }
                        s.buffer[s.len] = '\0';
 
                        kfree(field->type);
@@ -1446,13 +1448,13 @@ static int __create_synth_event(const char *name, const char *raw_fields)
                        if (cmd_version > 1 && n_fields_this_loop >= 1) {
                                synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str));
                                ret = -EINVAL;
-                               goto err_free_arg;
+                               goto err_free_field;
                        }
 
                        if (n_fields == SYNTH_FIELDS_MAX) {
                                synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0);
                                ret = -EINVAL;
-                               goto err_free_arg;
+                               goto err_free_field;
                        }
                        fields[n_fields++] = field;
 
@@ -1491,6 +1493,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
        kfree(saved_fields);
 
        return ret;
+ err_free_field:
+       free_synth_field(field);
  err_free_arg:
        argv_free(argv);
  err:
index c4ba484f7b38b3f1aa002ce8c0d24ec4ef5d2e0f..8c82ecb735f41545c49a8df6121c0576a5e783e0 100644 (file)
@@ -109,6 +109,9 @@ struct user_event_enabler {
 
        /* Track enable bit, flags, etc. Aligned for bitops. */
        unsigned long           values;
+
+       /* Defer the event put and enabler free past an RCU grace period. */
+       struct rcu_work         put_rwork;
 };
 
 /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
@@ -396,17 +399,39 @@ error:
        return NULL;
 };
 
-static void user_event_enabler_destroy(struct user_event_enabler *enabler,
-                                      bool locked)
+static void delayed_user_event_enabler_put(struct work_struct *work)
 {
-       list_del_rcu(&enabler->mm_enablers_link);
+       struct user_event_enabler *enabler = container_of(to_rcu_work(work),
+                       struct user_event_enabler, put_rwork);
 
        /* No longer tracking the event via the enabler */
-       user_event_put(enabler->event, locked);
+       user_event_put(enabler->event, false);
 
+       /* Run from queue_rcu_work(), the RCU grace period has elapsed */
        kfree(enabler);
 }
 
+static void user_event_enabler_destroy(struct user_event_enabler *enabler)
+{
+       list_del_rcu(&enabler->mm_enablers_link);
+
+       /*
+        * The enabler is removed from an RCU-traversed list
+        * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only),
+        * and readers there dereference enabler->event and take a new ref on
+        * it. Both the put of that event reference and the free of the enabler
+        * therefore have to wait for a grace period so no reader can be looking
+        * at the enabler or racing the last put of its event.
+        *
+        * The put itself must not run in RCU context: when it drops the last
+        * reference user_event_put() takes event_mutex, which cannot be taken
+        * from a softirq/RCU callback. Defer both to a work item scheduled
+        * after a grace period via queue_rcu_work().
+        */
+       INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put);
+       queue_rcu_work(system_percpu_wq, &enabler->put_rwork);
+}
+
 static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
                                  int attempt)
 {
@@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work)
 
        /* User asked for enabler to be removed during fault */
        if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
-               user_event_enabler_destroy(enabler, true);
+               user_event_enabler_destroy(enabler);
                goto out;
        }
 
@@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm)
        struct user_event_enabler *enabler, *next;
 
        list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
-               user_event_enabler_destroy(enabler, false);
+               user_event_enabler_destroy(enabler);
 
        mmdrop(mm->mm);
        kfree(mm);
@@ -2645,7 +2670,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
                        flags |= enabler->values & ENABLE_VAL_COMPAT_MASK;
 
                        if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
-                               user_event_enabler_destroy(enabler, true);
+                               user_event_enabler_destroy(enabler);
 
                        /* Removed at least one */
                        ret = 0;
index f283391a4dc80d17e57e7252d9afc5617b66a751..cd37f201375813715464a429fe6ef668d204d8fb 100644 (file)
@@ -458,12 +458,12 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
        ftrace_func_t func;
        u32 new_flags;
 
-       /* Do nothing if already set. */
-       if (!!set == !!(tr->current_trace_flags->val & bit))
+       /* We can change this flag only when current tracer is function. */
+       if (tr->current_trace != &function_trace)
                return 0;
 
-       /* We can change this flag only when not running. */
-       if (tr->current_trace != &function_trace)
+       /* Do nothing if already set. */
+       if (!!set == !!(tr->current_trace_flags->val & bit))
                return 0;
 
        new_flags = (tr->current_trace_flags->val & ~bit) | (set ? bit : 0);
index 5e83c4f6f2b429c432ddf6ff387ac7426c615b83..0e1265acd1ccc07ed776d59fb16c98a15377dd8d 100644 (file)
@@ -179,7 +179,9 @@ static void osnoise_unregister_instance(struct trace_array *tr)
        if (!found)
                return;
 
-       kvfree_rcu_mightsleep(inst);
+       /* Do a full sync to ensure that tr remains valid, not just inst */
+       synchronize_rcu();
+       kvfree(inst);
 }
 
 /*
index 0c42b15c380047b44efdd4b2c6395d6b77b9ccf7..b63e3558948f7a8d764e95d1f4468a55cda62d6f 100644 (file)
@@ -30,7 +30,7 @@
 #else
 #define trace(point, args)                                     \
        do {                                                    \
-               if (trace_##point##_enabled()) {                \
+               if (__trace_##point##_enabled()) {              \
                        bool exit_rcu = false;                  \
                        if (in_nmi())                           \
                                break;                          \
index d17cfee77d9cea337539ff44e3ec6a278f8aa5bf..506e6037e16375fb3de1bc4ea28bbdf803e9d1ef 100644 (file)
@@ -188,7 +188,7 @@ void __trace_probe_log_err(int offset, int err_type)
 
        lockdep_assert_held(&dyn_event_ops_mutex);
 
-       if (!trace_probe_log.argv)
+       if (!trace_probe_log.argv || !trace_probe_log.argc)
                return;
 
        /* Recalculate the length and allocate buffer */
@@ -2013,7 +2013,7 @@ int traceprobe_update_arg(struct probe_arg *arg)
 }
 
 /* When len=0, we just calculate the needed length */
-#define LEN_OR_ZERO (len ? len - pos : 0)
+#define LEN_OR_ZERO (len > pos ? len - pos : 0)
 static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
                           enum probe_print_type ptype)
 {
@@ -2338,16 +2338,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b)
 bool trace_probe_match_command_args(struct trace_probe *tp,
                                    int argc, const char **argv)
 {
-       char buf[MAX_ARGSTR_LEN + 1];
        int i;
 
        if (tp->nr_args < argc)
                return false;
 
        for (i = 0; i < argc; i++) {
-               snprintf(buf, sizeof(buf), "%s=%s",
-                        tp->args[i].name, tp->args[i].comm);
-               if (strcmp(buf, argv[i]))
+               int len = strlen(tp->args[i].name);
+
+               if (strncmp(argv[i], tp->args[i].name, len) ||
+                   argv[i][len] != '=' ||
+                   strcmp(argv[i] + len + 1, tp->args[i].comm))
                        return false;
        }
        return true;
index 2a6cc000ec98dcaefea2db1a37f9b7d71d45d175..0f6ef5c36d84e8a5e50bbde6a05e9d46fdab5b8b 100644 (file)
@@ -979,33 +979,30 @@ EXPORT_SYMBOL_GPL(trace_remote_free_buffer);
 int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size,
                              const struct cpumask *cpumask)
 {
+       size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask));
        unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1;
-       void *desc_end = desc + desc_size;
        struct ring_buffer_desc *rb_desc;
        int cpu, ret = -ENOMEM;
 
-       if (desc_size < struct_size(desc, __data, 0))
+       if (desc_size < min_desc_size)
                return -EINVAL;
 
        desc->nr_cpus = 0;
-       desc->struct_len = struct_size(desc, __data, 0);
+       desc->struct_len = min_desc_size;
 
-       rb_desc = (struct ring_buffer_desc *)&desc->__data[0];
+       rb_desc = __first_ring_buffer_desc(desc);
 
        for_each_cpu(cpu, cpumask) {
                unsigned int id;
 
-               if ((void *)rb_desc + struct_size(rb_desc, page_va, nr_pages) > desc_end) {
-                       ret = -EINVAL;
-                       goto err;
-               }
-
                rb_desc->cpu = cpu;
                rb_desc->nr_page_va = 0;
                rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL);
                if (!rb_desc->meta_va)
                        goto err;
 
+               desc->nr_cpus++;
+
                for (id = 0; id < nr_pages; id++) {
                        rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL);
                        if (!rb_desc->page_va[id])
@@ -1013,9 +1010,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
 
                        rb_desc->nr_page_va++;
                }
-               desc->nr_cpus++;
-               desc->struct_len += offsetof(struct ring_buffer_desc, page_va);
-               desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va);
                rb_desc = __next_ring_buffer_desc(rb_desc);
        }
 
index 292420f45811a59a86dfcddd2b1c53656522c879..7c1c2c27f58e75f532e7be3f66548467f4caeb16 100644 (file)
--- 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();
 
index 591c1c2a7fb31775304f2d4a07e93ad0138b27f6..83d4c95e079ef0c6ae5cd9d316b0305ac3d69dfa 100644 (file)
@@ -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
index 6bf130cfbbf98c80e239595917f748d38cd66d1b..3d2b017a0525ae5838826014a1e449f6faf28c6c 100644 (file)
@@ -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");
index 7aca76c25bcb2210d84cf556b576ebf0a85e7c96..c80d9dd736b4f928cc6a6646a616eb7d4844673e 100644 (file)
@@ -11,6 +11,9 @@
 MODULE_DESCRIPTION("glob(7) matching");
 MODULE_LICENSE("Dual MIT/GPL");
 
+static bool __pure glob_match_str(char const *pat, char const *str,
+                                 char const *str_end);
+
 /**
  * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
  * @pat: Shell-style pattern to match, e.g. "*.[ch]".
@@ -40,6 +43,29 @@ MODULE_LICENSE("Dual MIT/GPL");
  * An opening bracket without a matching close is matched literally.
  */
 bool __pure glob_match(char const *pat, char const *str)
+{
+       return glob_match_str(pat, str, NULL);
+}
+EXPORT_SYMBOL(glob_match);
+
+/**
+ * glob_match_len - glob match against a length-bounded string
+ * @pat: Shell-style pattern to match.
+ * @str: String to match.  Need not be NUL-terminated.
+ * @len: Number of bytes of @str that may be read.
+ *
+ * Like glob_match(), but @str is only read up to @len bytes, so it can be
+ * used on buffers that are not NUL-terminated (e.g. trace event fields).
+ * A NUL byte within @len still terminates the string.
+ */
+bool __pure glob_match_len(char const *pat, char const *str, size_t len)
+{
+       return glob_match_str(pat, str, str + len);
+}
+EXPORT_SYMBOL(glob_match_len);
+
+static bool __pure glob_match_str(char const *pat, char const *str,
+                                 char const *str_end)
 {
        /*
         * Backtrack to previous * on mismatch and retry starting one
@@ -55,9 +81,11 @@ bool __pure glob_match(char const *pat, char const *str)
         * on mismatch, or true after matching the trailing nul bytes.
         */
        for (;;) {
-               unsigned char c = *str++;
+               unsigned char c = (str_end && str >= str_end) ? '\0' : *str;
                unsigned char d = *pat++;
 
+               str++;
+
                switch (d) {
                case '?':       /* Wildcard: anything but nul */
                        if (c == '\0')
@@ -125,4 +153,3 @@ backtrack:
                }
        }
 }
-EXPORT_SYMBOL(glob_match);
index 40cfb38ac919d2991a78d7c417e823dd4767d6b7..d459bef245f4a7dc5b2977020f6d54b58627acc0 100644 (file)
@@ -878,6 +878,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter)
                iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht);
                iter->slot = 0;
                iter->skip = 0;
+               iter->p = NULL;
                return -EAGAIN;
        }
 
index b776f35ad02007059c9e34d9066496f5c512fb79..f08765ade014ce39a642566c264ad7c323f63227 100644 (file)
@@ -1875,15 +1875,14 @@ static void compaction_free(struct folio *dst, unsigned long data)
        int order = folio_order(dst);
        struct page *page = &dst->page;
 
-       if (folio_put_testzero(dst)) {
-               free_pages_prepare(page, order);
+       if (folio_put_testzero(dst) && free_pages_prepare(page, order)) {
                list_add(&dst->lru, &cc->freepages[order]);
                cc->nr_freepages += 1 << order;
        }
        cc->nr_migratepages += 1 << order;
        /*
-        * someone else has referenced the page, we cannot take it back to our
-        * free list.
+        * someone else has referenced the page or free_pages_prepare() fails,
+        * we cannot take it back to our free list.
         */
 }
 
index 7e4b9affc5b060ea323d44684334e33c81d16ce4..cff932b3317d0897b1db5e3d3b2aa644bd033cc6 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Data Access Monitor
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon: " fmt
@@ -356,8 +354,21 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,
 {
        struct damon_region *r, *next;
        unsigned int i;
+       unsigned long last_end;
        int err;
 
+       for (i = 0; i < nr_ranges; i++) {
+               unsigned long start, end;
+
+               start = ALIGN_DOWN(ranges[i].start, min_region_sz);
+               end = ALIGN(ranges[i].end, min_region_sz);
+               if (start >= end)
+                       return -EINVAL;
+               if (i > 0 && last_end > start)
+                       return -EINVAL;
+               last_end = end;
+       }
+
        /* Remove regions which are not in the new ranges */
        damon_for_each_region_safe(r, next, t) {
                for (i = 0; i < nr_ranges; i++) {
index 8298c6001fd097c9e54a6fad901139201cb2df66..32f41491b726b2a2b1016adb7f3485beecf28703 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON-based LRU-lists Sorting
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon-lru-sort: " fmt
index 86d58f8c4f63585d528f29ebd1d5afddb97ecf14..f87fa46a95a0880469359ee35548fb16326c3637 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Common Code for DAMON Modules
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/damon.h>
index f103ad55636872b2bf61e1f526aad454ba9b02e7..6fd45490e45b6311aae4adc64db00976799021e9 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Common Code for DAMON Modules
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/moduleparam.h>
index 5c93ef2bb8a97f24267c830578561bc70e99ab7f..6bdd1cfd3863a38e166d7bbf61dba47a01faa020 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Common Code for Data Access Monitoring
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/migrate.h>
@@ -143,6 +141,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
         * Transform it to fit in [0, DAMOS_MAX_SCORE]
         */
        hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE;
+       hotness = max(min(hotness, DAMOS_MAX_SCORE), 0);
 
        return hotness;
 }
index 5efa5b5970def780e9cce2fb1db069fd5eead107..38d295488fa181f09369218557fcdc3f599f450a 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Common Code for Data Access Monitoring
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/damon.h>
index d0598f5f268822db98f57d1425b4e1dbeac55bff..5c2da45f988cf9cc982d08da34844c35b4c4df3d 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON Code for The Physical Address Space
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon-pa: " fmt
index ce4499cf4b8b0509c041a3febc9fa18a0de5c041..11b70d0a9a6f0a364e3d2557d7bd5ad7fd26ccec 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON-based page reclamation
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon-reclaim: " fmt
index bdc6ae2639e4f9369597ae3795b7c81697a6c363..c59d7bf7a73ae0bfa4d2c93acf460027c632ef64 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Common Code for DAMON Sysfs Interface
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/slab.h>
index 3079306966a910081ddea7a9eea4c60399fdcff7..733764716e8dd9ec75fea4de9cad02718a362a49 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Common Code for DAMON Sysfs Interface
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/damon.h>
index 329cfd0bbe9f326e6d814523cf932f045b1137e7..3cbeccd436e40f93d7b36de887ea9c39141905d7 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON sysfs Interface
- *
- * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/slab.h>
@@ -1993,22 +1991,19 @@ static int damon_sysfs_access_pattern_add_dirs(
        err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
                        &access_pattern->sz, "sz");
        if (err)
-               goto put_sz_out;
+               return err;
 
        err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
                        &access_pattern->nr_accesses, "nr_accesses");
        if (err)
-               goto put_nr_accesses_sz_out;
+               goto put_sz_out;
 
        err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
                        &access_pattern->age, "age");
        if (err)
-               goto put_age_nr_accesses_sz_out;
+               goto put_nr_accesses_sz_out;
        return 0;
 
-put_age_nr_accesses_sz_out:
-       kobject_put(&access_pattern->age->kobj);
-       access_pattern->age = NULL;
 put_nr_accesses_sz_out:
        kobject_put(&access_pattern->nr_accesses->kobj);
        access_pattern->nr_accesses = NULL;
@@ -2516,12 +2511,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
                goto put_filters_watermarks_quotas_access_pattern_out;
        err = damon_sysfs_scheme_set_tried_regions(scheme);
        if (err)
-               goto put_tried_regions_out;
+               goto put_stats_out;
        return 0;
 
-put_tried_regions_out:
-       kobject_put(&scheme->tried_regions->kobj);
-       scheme->tried_regions = NULL;
+put_stats_out:
+       kobject_put(&scheme->stats->kobj);
+       scheme->stats = NULL;
 put_filters_watermarks_quotas_access_pattern_out:
        kobject_put(&scheme->ops_filters->kobj);
        scheme->ops_filters = NULL;
index 2e95e3bac774dc4b1034a3b5506caa15df96133c..a9e187158067d43b1329771d86a4583ccf5111cb 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON sysfs Interface
- *
- * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
  */
 
 #include <linux/pid.h>
index 1cfb8c176b873e8419f581855854b8709139206c..fcf7c7fadb5fe52e38c05e64f7e32f36c9ec581c 100644 (file)
@@ -1,10 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Data Access Monitor Unit Tests
- *
- * Copyright 2019 Amazon.com, Inc. or its affiliates.  All rights reserved.
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #ifdef CONFIG_DAMON_KUNIT_TEST
index f9ec5e795b34b6f7c1f42a927fdc1c57c24f20a0..138a4b8d14e73429619bac2640b63f57ca7495c6 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Data Access Monitor Unit Tests
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #ifdef CONFIG_DAMON_SYSFS_KUNIT_TEST
index 563fbc7e3f44835ef7707fe697feca95a750f77e..61f844336ffb5086538b65d913a7a0269612fa22 100644 (file)
@@ -1,10 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Data Access Monitor Unit Tests
- *
- * Copyright 2019 Amazon.com, Inc. or its affiliates.  All rights reserved.
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #ifdef CONFIG_DAMON_VADDR_KUNIT_TEST
index d271476035641bba84a01a471c6dc52d6070877a..e73ec1ce016e5b7f6456713873b8960dec1b21ae 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * DAMON Code for Virtual Address Spaces
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon-va: " fmt
index 5af62e6abca58b04f038466d3736cf91d8dbca81..58eb9d24064348020b3b706b555729587adbc805 100644 (file)
@@ -4704,7 +4704,7 @@ static inline bool can_do_cachestat(struct file *f)
 {
        if (f->f_mode & FMODE_WRITE)
                return true;
-       if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f)))
+       if (file_owner_or_capable(f))
                return true;
        return file_permission(f, MAY_WRITE) == 0;
 }
index 2bccb0a53a0a602a9cc3473decab4f0a61ae4ec8..b5d1e9d4463d00fea4a00db561338c4264405bd8 100644 (file)
@@ -3587,10 +3587,6 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
                                 (1L << PG_dropbehind) |
                                 LRU_GEN_MASK | LRU_REFS_MASK));
 
-               if (handle_hwpoison &&
-                   page_range_has_hwpoisoned(new_head, new_nr_pages))
-                       folio_set_has_hwpoisoned(new_folio);
-
                new_folio->mapping = folio->mapping;
                new_folio->index = folio->index + i;
 
@@ -3612,6 +3608,14 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
                        folio_set_large_rmappable(new_folio);
                }
 
+               /*
+                * PG_has_hwpoisoned is on the 2nd page, so set it after
+                * the compound head is prepped.
+                */
+               if (handle_hwpoison &&
+                   page_range_has_hwpoisoned(new_head, new_nr_pages))
+                       folio_set_has_hwpoisoned(new_folio);
+
                if (folio_test_young(folio))
                        folio_set_young(new_folio);
                if (folio_test_idle(folio))
index 7c7ba17ce7af00331380eb91b57dd00e88ae1183..e196f53f9b46205374868874f835a93031586fb8 100644 (file)
@@ -687,7 +687,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp)
        atomic_set(&object->use_count, 1);
        object->excess_ref = 0;
        object->count = 0;                      /* white color initially */
-       object->checksum = 0;
+       object->checksum = ~0;
        object->del_state = 0;
 
        /* task information */
@@ -981,7 +981,7 @@ static void reset_checksum(unsigned long ptr)
        }
 
        raw_spin_lock_irqsave(&object->lock, flags);
-       object->checksum = 0;
+       object->checksum = ~0;
        raw_spin_unlock_irqrestore(&object->lock, flags);
        put_object(object);
 }
@@ -1410,7 +1410,8 @@ static bool update_checksum(struct kmemleak_object *object)
                for_each_possible_cpu(cpu) {
                        void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu);
 
-                       object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size);
+                       object->checksum = crc32(object->checksum,
+                                                kasan_reset_tag((void *)ptr), object->size);
                }
        } else {
                object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size);
index cd9bb077072ccb1b90242a3211a9c6ef48929f50..77552b03d318d9ab93902cd42670dd23f26d83a7 100644 (file)
@@ -336,8 +336,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma)
         * otherwise we'd be including shared non-exclusive mappings, which
         * opens a side channel.
         */
-       return inode_owner_or_capable(&nop_mnt_idmap,
-                                     file_inode(vma->vm_file)) ||
+       return file_owner_or_capable(vma->vm_file) ||
               file_permission(vma->vm_file, MAY_WRITE) == 0;
 }
 
index 296f2e3922b5f48eb66a2c54ca0e69cc2e91c1a1..c8757c5085bf90040aa135433385ecea9f97a234 100644 (file)
@@ -227,8 +227,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma)
         * for writing; otherwise we'd be including shared non-exclusive
         * mappings, which opens a side channel.
         */
-       return inode_owner_or_capable(&nop_mnt_idmap,
-                                     file_inode(vma->vm_file)) ||
+       return file_owner_or_capable(vma->vm_file) ||
               file_permission(vma->vm_file, MAY_WRITE) == 0;
 }
 
index 7418f2e500bb481dd6c64466cc140f4a0283ac70..942e84b6908af75ff75ed400126c76b2c3962109 100644 (file)
@@ -173,11 +173,8 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
         * any pages that may have already been present from the previous
         * list processed. This should result in us reporting all pages on
         * an idle system in about 30 seconds.
-        *
-        * The division here should be cheap since PAGE_REPORTING_CAPACITY
-        * should always be a power of 2.
         */
-       budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16);
+       budget = DIV_ROUND_UP(area->nr_free, prdev->capacity * 16);
 
        /* loop through free list adding unreported pages to sg list */
        list_for_each_entry_safe(page, next, list, lru) {
@@ -222,10 +219,10 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
                spin_unlock_irq(&zone->lock);
 
                /* begin processing pages in local list */
-               err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY);
+               err = prdev->report(prdev, sgl, prdev->capacity);
 
                /* reset offset since the full list was reported */
-               *offset = PAGE_REPORTING_CAPACITY;
+               *offset = prdev->capacity;
 
                /* update budget to reflect call to report function */
                budget--;
@@ -234,7 +231,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
                spin_lock_irq(&zone->lock);
 
                /* flush reported pages from the sg list */
-               page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err);
+               page_reporting_drain(prdev, sgl, prdev->capacity, !err);
 
                /*
                 * Reset next to first entry, the old next isn't valid
@@ -260,13 +257,13 @@ static int
 page_reporting_process_zone(struct page_reporting_dev_info *prdev,
                            struct scatterlist *sgl, struct zone *zone)
 {
-       unsigned int order, mt, leftover, offset = PAGE_REPORTING_CAPACITY;
+       unsigned int order, mt, leftover, offset = prdev->capacity;
        unsigned long watermark;
        int err = 0;
 
        /* Generate minimum watermark to be able to guarantee progress */
        watermark = low_wmark_pages(zone) +
-                   (PAGE_REPORTING_CAPACITY << page_reporting_order);
+                   (prdev->capacity << page_reporting_order);
 
        /*
         * Cancel request if insufficient free memory or if we failed
@@ -290,7 +287,7 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev,
        }
 
        /* report the leftover pages before going idle */
-       leftover = PAGE_REPORTING_CAPACITY - offset;
+       leftover = prdev->capacity - offset;
        if (leftover) {
                sgl = &sgl[offset];
                err = prdev->report(prdev, sgl, leftover);
@@ -322,11 +319,11 @@ static void page_reporting_process(struct work_struct *work)
        atomic_set(&prdev->state, state);
 
        /* allocate scatterlist to store pages being reported on */
-       sgl = kmalloc_objs(*sgl, PAGE_REPORTING_CAPACITY);
+       sgl = kmalloc_objs(*sgl, prdev->capacity);
        if (!sgl)
                goto err_out;
 
-       sg_init_table(sgl, PAGE_REPORTING_CAPACITY);
+       sg_init_table(sgl, prdev->capacity);
 
        for_each_zone(zone) {
                err = page_reporting_process_zone(prdev, sgl, zone);
@@ -377,6 +374,9 @@ int page_reporting_register(struct page_reporting_dev_info *prdev)
                        page_reporting_order = pageblock_order;
        }
 
+       if (!prdev->capacity || prdev->capacity > PAGE_REPORTING_CAPACITY)
+               prdev->capacity = PAGE_REPORTING_CAPACITY;
+
        /* initialize state and work structures */
        atomic_set(&prdev->state, PAGE_REPORTING_IDLE);
        INIT_DELAYED_WORK(&prdev->work, &page_reporting_process);
index 2ccbabfb2cc17a6ab9c14f2df0753cbbbfbae199..bac2eb5de63d6149fb5e073ba1ad24f07736a1ac 100644 (file)
@@ -243,21 +243,31 @@ restart:
                 */
                pmde = pmdp_get_lockless(pvmw->pmd);
 
-               if (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde)) {
+               if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+                   (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde) ||
+                   pmd_is_device_private_entry(pmde))) {
                        pvmw->ptl = pmd_lock(mm, pvmw->pmd);
                        pmde = *pvmw->pmd;
-                       if (!pmd_present(pmde)) {
+                       if (pmd_is_migration_entry(pmde)) {
                                softleaf_t entry;
 
-                               if (!thp_migration_supported() ||
-                                   !(pvmw->flags & PVMW_MIGRATION))
+                               if (!(pvmw->flags & PVMW_MIGRATION))
                                        return not_found(pvmw);
                                entry = softleaf_from_pmd(pmde);
+                               if (!check_pmd(softleaf_to_pfn(entry), pvmw))
+                                       return not_found(pvmw);
+                               return true;
+                       } else if (pmd_is_device_private_entry(pmde)) {
+                               softleaf_t entry;
 
-                               if (!softleaf_is_migration(entry) ||
-                                   !check_pmd(softleaf_to_pfn(entry), pvmw))
+                               if (pvmw->flags & PVMW_MIGRATION)
+                                       return not_found(pvmw);
+                               entry = softleaf_from_pmd(pmde);
+                               if (!check_pmd(softleaf_to_pfn(entry), pvmw))
                                        return not_found(pvmw);
                                return true;
+                       } else if (!pmd_present(pmde)) {
+                               return not_found(pvmw);
                        }
                        if (likely(pmd_trans_huge(pmde))) {
                                if (pvmw->flags & PVMW_MIGRATION)
@@ -266,17 +276,10 @@ restart:
                                        return not_found(pvmw);
                                return true;
                        }
-                       /* THP pmd was split under us: handle on pte level */
+                       /* THP/device-private pmd was split under us: handle on pte level */
                        spin_unlock(pvmw->ptl);
                        pvmw->ptl = NULL;
                } else if (!pmd_present(pmde)) {
-                       const softleaf_t entry = softleaf_from_pmd(pmde);
-
-                       if (softleaf_is_device_private(entry)) {
-                               pvmw->ptl = pmd_lock(mm, pvmw->pmd);
-                               return true;
-                       }
-
                        if ((pvmw->flags & PVMW_SYNC) &&
                            thp_vma_suitable_order(vma, pvmw->address,
                                                   PMD_ORDER) &&
index 7082d01c8c9d0d1b8a71be6157f15dff1ef26b7a..a70aab124a0e7faddce5efd3d6e81339c4b55672 100644 (file)
@@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new,
        return 0;
 }
 
-void free_shrinker_info(struct mem_cgroup *memcg)
+static void __free_shrinker_info(struct mem_cgroup *memcg)
 {
        struct mem_cgroup_per_node *pn;
        struct shrinker_info *info;
        int nid;
 
+       lockdep_assert_held(&shrinker_mutex);
+
        for_each_node(nid) {
                pn = memcg->nodeinfo[nid];
                info = rcu_dereference_protected(pn->shrinker_info, true);
@@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg)
        }
 }
 
+void free_shrinker_info(struct mem_cgroup *memcg)
+{
+       mutex_lock(&shrinker_mutex);
+       __free_shrinker_info(memcg);
+       mutex_unlock(&shrinker_mutex);
+}
+
 int alloc_shrinker_info(struct mem_cgroup *memcg)
 {
        int nid, ret = 0;
@@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg)
        return ret;
 
 err:
+       __free_shrinker_info(memcg);
        mutex_unlock(&shrinker_mutex);
-       free_shrinker_info(memcg);
        return -ENOMEM;
 }
 
index cda4e86428c8f3b4b3f286f55fda24a6f9bb8b33..cafb566301326d0df5f61f756f90797bf72e8290 100644 (file)
@@ -183,10 +183,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
        }
        shrinker->debugfs_entry = entry;
 
-       debugfs_create_file("count", 0440, entry, shrinker,
-                           &shrinker_debugfs_count_fops);
-       debugfs_create_file("scan", 0220, entry, shrinker,
-                           &shrinker_debugfs_scan_fops);
+       if (shrinker->count_objects)
+               debugfs_create_file("count", 0440, entry, shrinker,
+                                   &shrinker_debugfs_count_fops);
+       if (shrinker->scan_objects)
+               debugfs_create_file("scan", 0220, entry, shrinker,
+                                   &shrinker_debugfs_scan_fops);
        return 0;
 }
 
index 99e2be39671b63da971f36f07edb3af1df51d3d2..ebd3ac997f645b8ea3d5ad5088887a93343a28d8 100644 (file)
@@ -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);
 }
 
index 246af12bf80142291a8fb509e7dbde7139bc976b..c3adedaaf7d5476925c7bb144dfa604f6379b6bf 100644 (file)
@@ -2111,7 +2111,10 @@ static bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags,
 {
        const struct vm_uffd_ops *ops = vma_uffd_ops(vma);
 
-       if (vma->vm_flags & VM_DROPPABLE)
+       if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK))
+               return false;
+
+       if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL))
                return false;
 
        vm_flags &= __VM_UFFD_FLAGS;
index c40c9e02391befb5e9a01a2bea16d6d263578bda..a6fe4820f65b9883b1d6622e15d2b1d1f809e612 100644 (file)
@@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
         * the one with the lowest address
         */
        if (tmp_max == max && max_orig_node &&
-           batadv_compare_eth(candidate->orig, max_orig_node->orig))
+           memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0)
                goto out;
 
        ret = true;
index 8a006a0473a87d77a79a38b11d0d06c364aebc3c..2e20a2cb64cbf0ffb0f004e0412c4b0a0453f48c 100644 (file)
@@ -518,8 +518,10 @@ int batadv_frag_send_packet(struct sk_buff *skb,
        mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
        max_fragment_size = mtu - header_size;
 
-       if (skb->len == 0 || max_fragment_size == 0)
-               return -EINVAL;
+       if (skb->len == 0 || max_fragment_size == 0) {
+               ret = -EINVAL;
+               goto free_skb;
+       }
 
        num_fragments = (skb->len - 1) / max_fragment_size + 1;
        max_fragment_size = (skb->len - 1) / num_fragments + 1;
@@ -545,7 +547,7 @@ int batadv_frag_send_packet(struct sk_buff *skb,
         */
        if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
                ret = -ENOMEM;
-               goto free_skb;
+               goto put_primary_if;
        }
 
        /* Create one header to be copied to all fragments */
index 4d3807a645b78b639c4f3d0a9cdc193cd936d050..67bed3ee77e7e72e67ce0361960620661ddbb798 100644 (file)
@@ -259,6 +259,7 @@ err_orig:
 void batadv_mesh_free(struct net_device *mesh_iface)
 {
        struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
+       struct batadv_meshif_vlan *vlan;
 
        WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
 
@@ -273,6 +274,13 @@ void batadv_mesh_free(struct net_device *mesh_iface)
 
        batadv_mcast_free(bat_priv);
 
+       /* destroy the "untagged" VLAN */
+       vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+       if (vlan) {
+               batadv_meshif_destroy_vlan(bat_priv, vlan);
+               batadv_meshif_vlan_put(vlan);
+       }
+
        /* Free the TT and the originator tables only after having terminated
         * all the other depending components which may use these structures for
         * their purposes.
@@ -368,7 +376,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset)
 
        switch (ethhdr->h_proto) {
        case htons(ETH_P_8021Q):
-               vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr),
+               vhdr = skb_header_pointer(skb, offset,
                                          sizeof(*vhdr), &vhdr_tmp);
                if (!vhdr)
                        return;
index 511f70e0706a7c689e18a737ddb81adce4e6bd36..fbfd99268de47e1d83357fb06c2c3ab12666ecfc 100644 (file)
@@ -195,6 +195,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
        if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
                goto dropped;
 
+       if (!pskb_may_pull(skb, ETH_HLEN))
+               goto dropped;
+
        /* reset control block to avoid left overs from previous users */
        memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
 
@@ -592,8 +595,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
  * @bat_priv: the bat priv with all the mesh interface information
  * @vlan: the object to remove
  */
-static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
-                                      struct batadv_meshif_vlan *vlan)
+void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
+                               struct batadv_meshif_vlan *vlan)
 {
        /* explicitly remove the associated TT local entry because it is marked
         * with the NOPURGE flag
@@ -1088,22 +1091,13 @@ static int batadv_meshif_newlink(struct net_device *dev,
 static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface,
                                          struct list_head *head)
 {
-       struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
        struct batadv_hard_iface *hard_iface;
-       struct batadv_meshif_vlan *vlan;
 
        while (!list_empty(&mesh_iface->adj_list.lower)) {
                hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next);
                batadv_hardif_disable_interface(hard_iface);
        }
 
-       /* destroy the "untagged" VLAN */
-       vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
-       if (vlan) {
-               batadv_meshif_destroy_vlan(bat_priv, vlan);
-               batadv_meshif_vlan_put(vlan);
-       }
-
        unregister_netdevice_queue(mesh_iface, head);
 }
 
index 53756c5a45e0471b82a21d3bbc4fd7d6b68924fb..5e1e83e04ffbc6dbb2765b26433084d57f688edc 100644 (file)
@@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface,
 bool batadv_meshif_is_valid(const struct net_device *net_dev);
 extern struct rtnl_link_ops batadv_link_ops;
 int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
+void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
+                               struct batadv_meshif_vlan *vlan);
 void batadv_meshif_vlan_release(struct kref *ref);
 struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv,
                                                  unsigned short vid);
index b8668a80b94a1e6b7fd06d98531e34b505fabf38..1404a3b7adfb1f8c371c6808c7df5a48702647a3 100644 (file)
@@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
 {
        struct batadv_tvlv_mcast_tracker *mcast_tracker;
        struct batadv_neigh_node *neigh_node;
-       unsigned long offset, num_dests_off;
        struct sk_buff *nexthop_skb;
        unsigned char *skb_net_hdr;
        bool local_recv = false;
        unsigned int tvlv_len;
+       unsigned long offset;
        bool xmitted = false;
        u8 *dest, *next_dest;
        u16 num_dests;
@@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
        /* (at least) TVLV part needs to be linearized */
        SKB_LINEAR_ASSERT(skb);
 
-       /* check if num_dests is within skb length */
-       num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests);
-       if (num_dests_off > skb_network_header_len(skb))
+       /* check if batadv_tvlv_mcast_tracker header is within skb length */
+       if (sizeof(*mcast_tracker) > skb_network_header_len(skb))
                return -EINVAL;
 
        skb_net_hdr = skb_network_header(skb);
index 4bfad36a4b7043b1a1e24ec4118c0e65edf8c2b8..dae5e1d8c03859f8149a2ec534e06cafeeed0860 100644 (file)
@@ -2971,7 +2971,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv,
 out:
        batadv_hardif_put(primary_if);
 
-       if (ret && tt_req_node) {
+       if (!ret && tt_req_node) {
                spin_lock_bh(&bat_priv->tt.req_list_lock);
                if (!hlist_unhashed(&tt_req_node->list)) {
                        hlist_del_init(&tt_req_node->list);
@@ -4033,7 +4033,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
                                             u16 tvlv_value_len)
 {
        struct batadv_tvlv_tt_data *tt_data;
-       u16 tt_vlan_len, tt_num_entries;
+       u16 tt_num_entries;
+       size_t tt_vlan_len;
        char tt_flag;
        bool ret;
 
index cb1e329d66fd4e5161e274a6c3d32c37dbf6fb81..d504a363a30f3698ba6f4c67a07fb1e4d621bc7a 100644 (file)
@@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)
        if (!chan)
                return NULL;
 
-       l2cap_chan_set_defaults(chan);
+       l2cap_chan_set_defaults(chan, NULL);
 
        chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
        chan->mode = L2CAP_MODE_LE_FLOWCTL;
@@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
        ifup(dev->netdev);
 }
 
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
-{
-       struct l2cap_chan *chan;
-
-       chan = chan_create();
-       if (!chan)
-               return NULL;
-
-       chan->ops = pchan->ops;
-
-       BT_DBG("chan %p pchan %p", chan, pchan);
-
-       return chan;
-}
-
 static void unregister_dev(struct lowpan_btle_dev *dev)
 {
        struct hci_dev *hdev = READ_ONCE(dev->hdev);
@@ -797,20 +782,10 @@ static void chan_close_cb(struct l2cap_chan *chan)
        struct lowpan_btle_dev *dev = NULL;
        struct lowpan_peer *peer;
        int err = -ENOENT;
-       bool last = false, remove = true;
+       bool last = false;
 
        BT_DBG("chan %p conn %p", chan, chan->conn);
 
-       if (chan->conn && chan->conn->hcon) {
-               if (!is_bt_6lowpan(chan->conn->hcon))
-                       return;
-
-               /* If conn is set, then the netdev is also there and we should
-                * not remove it.
-                */
-               remove = false;
-       }
-
        spin_lock(&devices_lock);
 
        list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
@@ -837,10 +812,8 @@ static void chan_close_cb(struct l2cap_chan *chan)
 
                ifdown(dev->netdev);
 
-               if (remove) {
-                       INIT_WORK(&entry->delete_netdev, delete_netdev);
-                       schedule_work(&entry->delete_netdev);
-               }
+               INIT_WORK(&entry->delete_netdev, delete_netdev);
+               schedule_work(&entry->delete_netdev);
        } else {
                spin_unlock(&devices_lock);
        }
@@ -901,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
 
 static const struct l2cap_ops bt_6lowpan_chan_ops = {
        .name                   = "L2CAP 6LoWPAN channel",
-       .new_connection         = chan_new_conn_cb,
        .recv                   = chan_recv_cb,
        .close                  = chan_close_cb,
        .state_change           = chan_state_change_cb,
@@ -1029,16 +1001,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
 
        hci_dev_lock(hdev);
        hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
-       hci_dev_unlock(hdev);
-       hci_dev_put(hdev);
-
-       if (!hcon)
+       if (!hcon) {
+               hci_dev_unlock(hdev);
+               hci_dev_put(hdev);
                return -ENOENT;
+       }
 
-       *conn = (struct l2cap_conn *)hcon->l2cap_data;
+       *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
 
        BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
 
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+
        return 0;
 }
 
@@ -1093,23 +1068,15 @@ done:
        } while (nchans);
 }
 
-struct set_enable {
-       struct work_struct work;
-       bool flag;
-};
-
-static void do_enable_set(struct work_struct *work)
+static void do_enable_set(bool flag)
 {
-       struct set_enable *set_enable = container_of(work,
-                                                    struct set_enable, work);
-
-       if (!set_enable->flag || enable_6lowpan != set_enable->flag)
+       if (!flag || enable_6lowpan != flag)
                /* Disconnect existing connections if 6lowpan is
                 * disabled
                 */
                disconnect_all_peers();
 
-       enable_6lowpan = set_enable->flag;
+       enable_6lowpan = flag;
 
        mutex_lock(&set_lock);
        if (listen_chan) {
@@ -1121,22 +1088,11 @@ static void do_enable_set(struct work_struct *work)
 
        listen_chan = bt_6lowpan_listen();
        mutex_unlock(&set_lock);
-
-       kfree(set_enable);
 }
 
 static int lowpan_enable_set(void *data, u64 val)
 {
-       struct set_enable *set_enable;
-
-       set_enable = kzalloc_obj(*set_enable);
-       if (!set_enable)
-               return -ENOMEM;
-
-       set_enable->flag = !!val;
-       INIT_WORK(&set_enable->work, do_enable_set);
-
-       schedule_work(&set_enable->work);
+       do_enable_set(!!val);
 
        return 0;
 }
@@ -1185,18 +1141,22 @@ static ssize_t lowpan_control_write(struct file *fp,
                if (conn) {
                        struct lowpan_peer *peer;
 
-                       if (!is_bt_6lowpan(conn->hcon))
+                       if (!is_bt_6lowpan(conn->hcon)) {
+                               l2cap_conn_put(conn);
                                return -EINVAL;
+                       }
 
                        peer = lookup_peer(conn);
                        if (peer) {
                                BT_DBG("6LoWPAN connection already exists");
+                               l2cap_conn_put(conn);
                                return -EALREADY;
                        }
 
                        BT_DBG("conn %p dst %pMR type %d user %u", conn,
                               &conn->hcon->dst, conn->hcon->dst_type,
                               addr_type);
+                       l2cap_conn_put(conn);
                }
 
                ret = bt_6lowpan_connect(&addr, addr_type);
@@ -1212,6 +1172,8 @@ static ssize_t lowpan_control_write(struct file *fp,
                        return ret;
 
                ret = bt_6lowpan_disconnect(conn, addr_type);
+               if (conn)
+                       l2cap_conn_put(conn);
                if (ret < 0)
                        return ret;
 
index bcbc11c9cb156b609d769736152b9123eb96f4af..a2290ffdc2c1a3b7e68b288cfd92519bac417bf8 100644 (file)
@@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
 
 restart:
        for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
-               /* Prevent early freeing of sk due to unlink and sock_kill */
+               /* The reference from bt_accept_get() keeps sk alive. */
                lock_sock(sk);
 
                /* Check sk has not already been unlinked via
@@ -321,13 +321,11 @@ restart:
 
                next = bt_accept_get(parent, sk);
 
-               /* sk is safely in the parent list so reduce reference count */
-               sock_put(sk);
-
                /* FIXME: Is this check still needed */
                if (sk->sk_state == BT_CLOSED) {
                        bt_accept_unlink(sk);
                        release_sock(sk);
+                       sock_put(sk);
                        continue;
                }
 
@@ -337,16 +335,6 @@ restart:
                        if (newsock)
                                sock_graft(sk, newsock);
 
-                       /* Hand the caller a reference taken while sk is
-                        * still locked.  bt_accept_unlink() just dropped
-                        * the accept-queue reference; without this hold a
-                        * concurrent teardown (e.g. l2cap_conn_del() ->
-                        * l2cap_sock_kill()) could free sk between
-                        * release_sock() and the caller using it.  Every
-                        * caller drops this with sock_put() when done.
-                        */
-                       sock_hold(sk);
-
                        release_sock(sk);
                        if (next)
                                sock_put(next);
@@ -354,6 +342,7 @@ restart:
                }
 
                release_sock(sk);
+               sock_put(sk);
        }
 
        return NULL;
index add9a8f7535d482381cced94bda6a6bd2a3c4e05..f7d88c33e23e4fea5d27726cd3eb3be44e5317c8 100644 (file)
@@ -559,14 +559,18 @@ static int bnep_session(void *arg)
        return 0;
 }
 
-static struct device *bnep_get_device(struct bnep_session *session)
+static struct l2cap_conn *bnep_get_conn(struct bnep_session *session)
 {
-       struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
+       struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan;
+       struct l2cap_conn *conn;
 
-       if (!conn || !conn->hcon)
-               return NULL;
+       l2cap_chan_lock(chan);
+       conn = chan->conn;
+       if (conn)
+               l2cap_conn_get(conn);
+       l2cap_chan_unlock(chan);
 
-       return &conn->hcon->dev;
+       return conn;
 }
 
 static const struct device_type bnep_type = {
@@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
        struct net_device *dev;
        struct bnep_session *s, *ss;
+       struct l2cap_conn *conn = NULL;
        u8 dst[ETH_ALEN], src[ETH_ALEN];
        int err;
 
@@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        bnep_set_default_proto_filter(s);
 #endif
 
-       SET_NETDEV_DEV(dev, bnep_get_device(s));
+       conn = bnep_get_conn(s);
+       if (!conn) {
+               err = -ENOTCONN;
+               goto failed;
+       }
+
+       SET_NETDEV_DEV(dev, &conn->hcon->dev);
        SET_NETDEV_DEVTYPE(dev, &bnep_type);
 
        err = register_netdev(dev);
+       l2cap_conn_put(conn);
+       conn = NULL;
        if (err)
                goto failed;
 
@@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        return 0;
 
 failed:
+       if (conn)
+               l2cap_conn_put(conn);
        up_write(&bnep_session_sem);
        free_netdev(dev);
        return err;
index c335372e406211fe03a285ce6123a874c85fd66a..1966cd153d9730dda12c1ce64cdd930bb50dcee0 100644 (file)
@@ -3178,26 +3178,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
 
        conn->abort_reason = reason;
 
-       /* If the connection is pending check the command opcode since that
-        * might be blocking on hci_cmd_sync_work while waiting its respective
-        * event so we need to hci_cmd_sync_cancel to cancel it.
-        *
-        * hci_connect_le serializes the connection attempts so only one
-        * connection can be in BT_CONNECT at time.
+       /* Cancel the connect attempt. A return of 0 means the create command
+        * was still queued and got dequeued, so there is nothing to disconnect.
         */
-       if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
-               switch (hci_skb_event(hdev->sent_cmd)) {
-               case HCI_EV_CONN_COMPLETE:
-               case HCI_EV_LE_CONN_COMPLETE:
-               case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
-               case HCI_EVT_LE_CIS_ESTABLISHED:
-                       hci_cmd_sync_cancel(hdev, ECANCELED);
-                       break;
-               }
-       /* Cancel connect attempt if still queued/pending */
-       } else if (!hci_cancel_connect_sync(hdev, conn)) {
+       if (!hci_cancel_connect_sync(hdev, conn))
                return 0;
-       }
 
        /* Run immediately if on cmd_sync_work since this may be called
         * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
index b6d963ce26d0e2f6675a7268452eecd6b0771d6a..741d658e9630368e4b573d295032d7b2139257a2 100644 (file)
@@ -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)
 {
index 3be8c3581c6caadeeea56e14e09d4123deb4ec03..532534bc601c5a3b19d36c15f97f9ffb38516166 100644 (file)
@@ -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;
 
@@ -6633,6 +6669,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
 
        bt_dev_dbg(hdev, "conn %p", conn);
 
+       /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+        * clear_bit() at done.
+        */
+       hci_conn_get(conn);
+
        clear_bit(HCI_CONN_SCANNING, &conn->flags);
        conn->state = BT_CONNECT;
 
@@ -6645,6 +6686,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
                    hdev->le_scan_type == LE_SCAN_ACTIVE &&
                    !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
                        hci_conn_del(conn);
+                       hci_conn_put(conn);
                        return -EBUSY;
                }
 
@@ -6659,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;
@@ -6672,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
@@ -6690,6 +6736,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
                                             &own_addr_type);
        if (err)
                goto done;
+
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+       set_bit(HCI_CONN_CREATE, &conn->flags);
+
        /* Send command LE Extended Create Connection if supported */
        if (use_ext_conn(hdev)) {
                err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6725,11 +6777,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
                                       conn->conn_timeout, NULL);
 
 done:
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+
        if (err == -ETIMEDOUT)
                hci_le_connect_cancel_sync(hdev, conn, 0x00);
 
        /* Re-enable advertising after the connection attempt is finished. */
        hci_resume_advertising_sync(hdev);
+       hci_conn_put(conn);
        return err;
 }
 
@@ -7004,10 +7059,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
        else
                cp.role_switch = 0x00;
 
-       return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
-                                       sizeof(cp), &cp,
-                                       HCI_EV_CONN_COMPLETE,
-                                       conn->conn_timeout, NULL);
+       /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+        * clear_bit() below.
+        */
+       hci_conn_get(conn);
+
+       /* Mark create connection in flight so hci_cancel_connect_sync() can
+        * cancel it while blocking on the connection complete event.
+        */
+       set_bit(HCI_CONN_CREATE, &conn->flags);
+
+       err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
+                                      sizeof(cp), &cp,
+                                      HCI_EV_CONN_COMPLETE,
+                                      conn->conn_timeout, NULL);
+
+       clear_bit(HCI_CONN_CREATE, &conn->flags);
+       hci_conn_put(conn);
+
+       return err;
 }
 
 int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7059,22 +7129,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
        return (err == -EEXIST) ? 0 : err;
 }
 
-int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
+static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
+                                          struct hci_conn *conn)
 {
-       if (conn->state != BT_OPEN)
-               return -EINVAL;
+       struct hci_cmd_sync_work_entry *entry;
+       int err = -EBUSY;
 
+       /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel
+        * atomic against the worker, which takes this lock to dequeue every
+        * entry: while it is held no other command can become pending, so
+        * hci_cmd_sync_cancel() cannot cancel an unrelated command.
+        */
+       mutex_lock(&hdev->cmd_sync_work_lock);
+
+       /* In flight: this connection owns the pending request, cancel it. */
+       if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+               hci_cmd_sync_cancel(hdev, ECANCELED);
+               goto unlock;
+       }
+
+       /* Still queued: a successful dequeue means it never started, so there
+        * is nothing to disconnect.
+        */
+       entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn,
+                                          NULL);
+       if (entry) {
+               _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
+               err = 0;
+       }
+
+unlock:
+       mutex_unlock(&hdev->cmd_sync_work_lock);
+       return err;
+}
+
+static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
+                                         struct hci_conn *conn)
+{
+       struct hci_cmd_sync_work_entry *entry;
+       int err = -EBUSY;
+
+       /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel
+        * atomic against the cmd_sync worker.
+        */
+       mutex_lock(&hdev->cmd_sync_work_lock);
+
+       if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+               hci_cmd_sync_cancel(hdev, ECANCELED);
+               goto unlock;
+       }
+
+       entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn,
+                                          create_le_conn_complete);
+       if (entry) {
+               _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
+               err = 0;
+       }
+
+unlock:
+       mutex_unlock(&hdev->cmd_sync_work_lock);
+       return err;
+}
+
+static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
+                                          struct hci_conn *conn)
+{
+       /* LE Create CIS is shared by the whole CIG and cannot be dequeued
+        * per-connection, so only an in-flight command can be cancelled.
+        * cmd_sync_work_lock keeps the test and the cancel atomic against the
+        * cmd_sync worker.
+        */
+       mutex_lock(&hdev->cmd_sync_work_lock);
+
+       if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
+               hci_cmd_sync_cancel(hdev, ECANCELED);
+
+       mutex_unlock(&hdev->cmd_sync_work_lock);
+       return -EBUSY;
+}
+
+int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
+{
        switch (conn->type) {
        case ACL_LINK:
-               return !hci_cmd_sync_dequeue_once(hdev,
-                                                 hci_acl_create_conn_sync,
-                                                 conn, NULL);
+               return hci_acl_cancel_create_conn_sync(hdev, conn);
        case LE_LINK:
-               return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
-                                                 conn, create_le_conn_complete);
+               return hci_le_cancel_create_conn_sync(hdev, conn);
+       case CIS_LINK:
+               return hci_cis_cancel_create_conn_sync(hdev, conn);
+       default:
+               return -ENOENT;
        }
-
-       return -ENOENT;
 }
 
 int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
@@ -7130,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.
@@ -7201,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
index 793a481d7066ad2f8bd8e4592e49df64f722609c..2e95a153912c5de477d747fe0188b39f05f7a538 100644 (file)
@@ -1590,6 +1590,7 @@ static void iso_conn_big_sync(struct sock *sk)
 {
        int err;
        struct hci_dev *hdev;
+       struct iso_conn *conn;
        bdaddr_t src, dst;
        u8 src_type;
 
@@ -1612,8 +1613,17 @@ static void iso_conn_big_sync(struct sock *sk)
        hci_dev_lock(hdev);
        lock_sock(sk);
 
+       /* The socket lock was dropped for hci_get_route(), so the connection
+        * may have been torn down meanwhile: iso_chan_del() clears conn and
+        * the broadcast teardown path can clear conn->hcon on its own. Check
+        * both before dereferencing conn->hcon.
+        */
+       conn = iso_pi(sk)->conn;
+       if (!conn || !conn->hcon)
+               goto unlock;
+
        if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
-               err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
+               err = hci_conn_big_create_sync(hdev, conn->hcon,
                                               &iso_pi(sk)->qos,
                                               iso_pi(sk)->sync_handle,
                                               iso_pi(sk)->bc_num_bis,
@@ -1622,6 +1632,7 @@ static void iso_conn_big_sync(struct sock *sk)
                        bt_dev_err(hdev, "hci_big_create_sync: %d", err);
        }
 
+unlock:
        release_sock(sk);
        hci_dev_unlock(hdev);
        hci_dev_put(hdev);
@@ -2529,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
        switch (pb) {
        case ISO_START:
        case ISO_SINGLE:
-               if (conn->rx_len) {
+               if (conn->rx_skb || conn->rx_len) {
                        BT_ERR("Unexpected start frame (len %d)", skb->len);
                        kfree_skb(conn->rx_skb);
                        conn->rx_skb = NULL;
@@ -2610,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                break;
 
        case ISO_CONT:
-               BT_DBG("Cont: frag len %d (expecting %d)", skb->len,
+       case ISO_END:
+               BT_DBG("%s: frag len %d (expecting %d)",
+                      (pb == ISO_END) ? "End" : "Cont", skb->len,
                       conn->rx_len);
 
-               if (!conn->rx_len) {
-                       BT_ERR("Unexpected continuation frame (len %d)",
-                              skb->len);
+               if (!conn->rx_skb) {
+                       BT_ERR("Unexpected ISO %s frame (len %d)",
+                              (pb == ISO_END) ? "End" : "Cont", skb->len);
                        goto drop;
                }
 
@@ -2631,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
                                          skb->len);
                conn->rx_len -= skb->len;
-               break;
-
-       case ISO_END:
-               if (!conn->rx_len) {
-                       BT_ERR("Unexpected end frame (len %d)", skb->len);
-                       goto drop;
-               }
 
-               skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
-                                         skb->len);
-               conn->rx_len -= skb->len;
+               if (pb == ISO_CONT)
+                       break;
 
                if (!conn->rx_len) {
                        struct sk_buff *rx_skb = conn->rx_skb;
@@ -2652,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                         */
                        conn->rx_skb = NULL;
                        iso_recv_frame(conn, rx_skb);
+               } else {
+                       BT_ERR("ISO fragment incomplete (len %d, expected %d)",
+                              skb->len, conn->rx_len);
+                       kfree_skb(conn->rx_skb);
+                       conn->rx_skb = NULL;
+                       conn->rx_len = 0;
+                       goto drop;
                }
                break;
        }
index 62133eef9d2fea14e7bf8d203fc951f4ed4499b4..538ae9aa34794312e5b8cd95f4e8ef5661b2e98a 100644 (file)
@@ -522,7 +522,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
 }
 EXPORT_SYMBOL_GPL(l2cap_chan_put);
 
-void l2cap_chan_set_defaults(struct l2cap_chan *chan)
+/* Initialise @chan with default values, inheriting from the parent channel
+ * @pchan when it is given.
+ */
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
 {
        chan->fcs  = L2CAP_FCS_CRC16;
        chan->max_tx = L2CAP_DEFAULT_MAX_TX;
@@ -536,6 +539,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
        chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
        chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
 
+       if (pchan) {
+               BT_DBG("chan %p pchan %p", chan, pchan);
+
+               chan->chan_type = pchan->chan_type;
+               chan->imtu = pchan->imtu;
+               chan->omtu = pchan->omtu;
+               chan->mode = pchan->mode;
+               chan->fcs = pchan->fcs;
+               chan->max_tx = pchan->max_tx;
+               chan->tx_win = pchan->tx_win;
+               chan->tx_win_max = pchan->tx_win_max;
+               chan->sec_level = pchan->sec_level;
+               chan->conf_state = pchan->conf_state;
+               chan->flags = pchan->flags;
+               chan->tx_credits = pchan->tx_credits;
+               chan->rx_credits = pchan->rx_credits;
+
+               if (chan->chan_type == L2CAP_CHAN_FIXED) {
+                       chan->scid = pchan->scid;
+                       chan->dcid = pchan->scid;
+               }
+
+               return;
+       }
+
        chan->conf_state = 0;
        set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
 
@@ -1775,19 +1803,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
        disable_delayed_work_sync(&conn->info_timer);
        disable_delayed_work_sync(&conn->id_addr_timer);
 
+       cancel_work_sync(&conn->pending_rx_work);
+
        mutex_lock(&conn->lock);
 
        kfree_skb(conn->rx_skb);
 
        skb_queue_purge(&conn->pending_rx);
-
-       /* We can not call flush_work(&conn->pending_rx_work) here since we
-        * might block if we are running on a worker from the same workqueue
-        * pending_rx_work is waiting on.
-        */
-       if (work_pending(&conn->pending_rx_work))
-               cancel_work_sync(&conn->pending_rx_work);
-
        ida_destroy(&conn->tx_ida);
 
        l2cap_unregister_all_users(conn);
@@ -3051,13 +3073,24 @@ fail:
        return NULL;
 }
 
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
-                                    unsigned long *val)
+static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type,
+                                    int *olen, unsigned long *val)
 {
        struct l2cap_conf_opt *opt = *ptr;
        int len;
 
+       /* opt->len is attacker-controlled. Validate that the full option
+        * (header + value) actually fits in the buffer before touching
+        * opt->val, otherwise the switch below reads past the end of the
+        * caller's buffer.
+        */
+       if (end - *ptr < L2CAP_CONF_OPT_SIZE)
+               return -EINVAL;
+
        len = L2CAP_CONF_OPT_SIZE + opt->len;
+       if (end - *ptr < len)
+               return -EINVAL;
+
        *ptr += len;
 
        *type = opt->type;
@@ -3429,6 +3462,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
        void *ptr = rsp->data;
        void *endptr = data + data_size;
        void *req = chan->conf_req;
+       void *req_end = req + chan->conf_len;
        int len = chan->conf_len;
        int type, hint, olen;
        unsigned long val;
@@ -3442,9 +3476,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
        BT_DBG("chan %p", chan);
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
-               len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
-               if (len < 0)
+               int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val);
+
+               if (ret < 0)
                        break;
+               len -= ret;
 
                hint  = type & L2CAP_CONF_HINT;
                type &= L2CAP_CONF_MASK;
@@ -3672,6 +3708,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
        struct l2cap_conf_req *req = data;
        void *ptr = req->data;
        void *endptr = data + size;
+       void *rsp_end = rsp + len;
        int type, olen;
        unsigned long val;
        struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
@@ -3680,9 +3717,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
        BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
-               len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
-               if (len < 0)
+               int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+               if (ret < 0)
                        break;
+               len -= ret;
 
                switch (type) {
                case L2CAP_CONF_MTU:
@@ -3933,6 +3972,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
 {
        int type, olen;
        unsigned long val;
+       void *rsp_end = rsp + len;
        /* Use sane default values in case a misbehaving remote device
         * did not send an RFC or extended window size option.
         */
@@ -3951,9 +3991,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
                return;
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
-               len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
-               if (len < 0)
+               int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+               if (ret < 0)
                        break;
+               len -= ret;
 
                switch (type) {
                case L2CAP_CONF_RFC:
@@ -4010,6 +4052,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
        return 0;
 }
 
+/* Allocate and initialise a channel for an incoming connection.
+ *
+ * The channel inherits its configuration from @pchan and is linked into @conn
+ * before ->new_connection() runs, so the conn list reference keeps it alive if
+ * the callback exposes it (e.g. via the socket accept queue) before this
+ * returns. The l2cap_chan_create() reference is taken over by the subsystem on
+ * success and dropped here on failure.
+ */
+static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
+                                              struct l2cap_chan *pchan)
+{
+       struct l2cap_chan *chan;
+
+       chan = l2cap_chan_create();
+       if (!chan)
+               return NULL;
+
+       l2cap_chan_set_defaults(chan, pchan);
+       chan->ops = pchan->ops;
+
+       __l2cap_chan_add(conn, chan);
+
+       if (pchan->ops->new_connection &&
+           pchan->ops->new_connection(pchan, chan) < 0) {
+               l2cap_chan_del(chan, 0);
+               l2cap_chan_put(chan);
+               return NULL;
+       }
+
+       return chan;
+}
+
 static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
                          u8 *data, u8 rsp_code)
 {
@@ -4056,7 +4130,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
                goto response;
        }
 
-       chan = pchan->ops->new_connection(pchan);
+       chan = l2cap_new_connection(conn, pchan);
        if (!chan)
                goto response;
 
@@ -4074,8 +4148,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
        chan->psm  = psm;
        chan->dcid = scid;
 
-       __l2cap_chan_add(conn, chan);
-
        dcid = chan->scid;
 
        __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4807,6 +4879,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id)
        case L2CAP_ECHO_RSP:
        case L2CAP_INFO_RSP:
        case L2CAP_CONN_PARAM_UPDATE_RSP:
+       case L2CAP_LE_CONN_RSP:
        case L2CAP_ECRED_CONN_RSP:
        case L2CAP_ECRED_RECONF_RSP:
                /* First do a lookup since the remote may send bogus ids that
@@ -4958,7 +5031,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
                goto response_unlock;
        }
 
-       chan = pchan->ops->new_connection(pchan);
+       chan = l2cap_new_connection(conn, pchan);
        if (!chan) {
                result = L2CAP_CR_LE_NO_MEM;
                goto response_unlock;
@@ -4973,8 +5046,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
        chan->omtu = mtu;
        chan->remote_mps = mps;
 
-       __l2cap_chan_add(conn, chan);
-
        l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
 
        dcid = chan->scid;
@@ -5182,7 +5253,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
                        continue;
                }
 
-               chan = pchan->ops->new_connection(pchan);
+               chan = l2cap_new_connection(conn, pchan);
                if (!chan) {
                        result = L2CAP_CR_LE_NO_MEM;
                        continue;
@@ -5197,8 +5268,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
                chan->omtu = mtu;
                chan->remote_mps = mps;
 
-               __l2cap_chan_add(conn, chan);
-
                l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
 
                /* Init response */
@@ -6704,6 +6773,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
        struct l2cap_conn *conn = chan->conn;
        struct l2cap_le_credits pkt;
        u16 return_credits = l2cap_le_rx_credits(chan);
+       int ident;
 
        if (chan->mode != L2CAP_MODE_LE_FLOWCTL &&
            chan->mode != L2CAP_MODE_EXT_FLOWCTL)
@@ -6721,9 +6791,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
        pkt.cid     = cpu_to_le16(chan->scid);
        pkt.credits = cpu_to_le16(return_credits);
 
-       chan->ident = l2cap_get_ident(conn);
+       ident = l2cap_get_ident(conn);
+
+       l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
 
-       l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
+       /* L2CAP_LE_CREDITS has no response so the ident is never released by
+        * l2cap_put_ident() - release it right away, otherwise the tx_ida
+        * range is exhausted after 254 packets and from then on credits are
+        * sent with the invalid ident 0, which some remote stacks ignore,
+        * stalling the channel.
+        */
+       if (ident > 0)
+               ida_free(&conn->tx_ida, ident);
 }
 
 void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail)
@@ -7478,14 +7557,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
                        goto next;
 
                l2cap_chan_lock(pchan);
-               chan = pchan->ops->new_connection(pchan);
+               chan = l2cap_new_connection(conn, pchan);
                if (chan) {
                        bacpy(&chan->src, &hcon->src);
                        bacpy(&chan->dst, &hcon->dst);
                        chan->src_type = bdaddr_src_type(hcon);
                        chan->dst_type = dst_type;
-
-                       __l2cap_chan_add(conn, chan);
                }
 
                l2cap_chan_unlock(pchan);
@@ -7702,6 +7779,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
 
        return c;
 }
+EXPORT_SYMBOL(l2cap_conn_hold_unless_zero);
 
 int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle,
                       struct sk_buff *skb, u16 flags)
index 4853f1b33449f0cfa235e3d308fa213809603bf0..4058ff50cc27a8c657fa7d9e5ac71878820d0fc5 100644 (file)
@@ -43,7 +43,8 @@ static struct bt_sock_list l2cap_sk_list = {
 static const struct proto_ops l2cap_sock_ops;
 static void l2cap_sock_init(struct sock *sk, struct sock *parent);
 static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
-                                    int proto, gfp_t prio, int kern);
+                                    int proto, gfp_t prio, int kern,
+                                    struct l2cap_chan *chan);
 static void l2cap_sock_cleanup_listen(struct sock *parent);
 
 bool l2cap_is_socket(struct socket *sock)
@@ -1284,6 +1285,23 @@ done:
        return err;
 }
 
+/* Release the sock's ref on chan and clear the pointer so that the ref is
+ * dropped exactly once even if both l2cap_sock_kill() and
+ * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
+ * task from dereferencing the now-dead sock pointer.
+ */
+static void l2cap_sock_put_chan(struct sock *sk)
+{
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+       if (!chan)
+               return;
+
+       chan->data = NULL;
+       l2cap_pi(sk)->chan = NULL;
+       l2cap_chan_put(chan);
+}
+
 /* Kill socket (only if zapped and orphan)
  * Must be called on unlocked socket, with l2cap channel lock.
  */
@@ -1294,13 +1312,9 @@ static void l2cap_sock_kill(struct sock *sk)
 
        BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
 
-       /* Sock is dead, so set chan data to NULL, avoid other task use invalid
-        * sock pointer.
-        */
-       l2cap_pi(sk)->chan->data = NULL;
-       /* Kill poor orphan */
+       l2cap_sock_put_chan(sk);
 
-       l2cap_chan_put(l2cap_pi(sk)->chan);
+       /* Kill poor orphan */
        sock_set_flag(sk, SOCK_DEAD);
        sock_put(sk);
 }
@@ -1492,8 +1506,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
 
        /* Close not yet accepted channels.
         *
-        * bt_accept_dequeue() now returns sk with an extra reference held
-        * (taken while sk was still locked) so a concurrent l2cap_conn_del()
+        * bt_accept_dequeue() returns sk with its temporary queue-walk
+        * reference held, so a concurrent l2cap_conn_del()
         * -> l2cap_sock_kill() cannot free sk under us.
         *
         * cleanup_listen() runs under the parent sk lock, so unlike
@@ -1543,12 +1557,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
        }
 }
 
-static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
+static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
+                                       struct l2cap_chan *new_chan)
 {
        struct sock *sk, *parent = chan->data;
 
        if (!parent)
-               return NULL;
+               return -EINVAL;
 
        lock_sock(parent);
 
@@ -1556,25 +1571,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
                release_sock(parent);
-               return NULL;
+               return -ENOBUFS;
        }
 
        sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
-                             GFP_ATOMIC, 0);
+                             GFP_ATOMIC, 0, new_chan);
        if (!sk) {
                release_sock(parent);
-               return NULL;
-        }
+               return -ENOMEM;
+       }
 
        bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
 
        l2cap_sock_init(sk, parent);
 
+       /* The conn list reference taken by l2cap_new_connection() keeps new_chan
+        * alive once release_sock() lets another task free this socket.
+        */
        bt_accept_enqueue(parent, sk, false);
 
        release_sock(parent);
 
-       return l2cap_pi(sk)->chan;
+       return 0;
 }
 
 static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1871,10 +1889,7 @@ static void l2cap_sock_destruct(struct sock *sk)
 
        BT_DBG("sk %p", sk);
 
-       if (l2cap_pi(sk)->chan) {
-               l2cap_pi(sk)->chan->data = NULL;
-               l2cap_chan_put(l2cap_pi(sk)->chan);
-       }
+       l2cap_sock_put_chan(sk);
 
        list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
                kfree_skb(rx_busy->skb);
@@ -1907,30 +1922,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
        BT_DBG("sk %p", sk);
 
        if (parent) {
-               struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
-
                sk->sk_type = parent->sk_type;
                bt_sk(sk)->flags = bt_sk(parent)->flags;
 
-               chan->chan_type = pchan->chan_type;
-               chan->imtu = pchan->imtu;
-               chan->omtu = pchan->omtu;
-               chan->conf_state = pchan->conf_state;
-               chan->mode = pchan->mode;
-               chan->fcs  = pchan->fcs;
-               chan->max_tx = pchan->max_tx;
-               chan->tx_win = pchan->tx_win;
-               chan->tx_win_max = pchan->tx_win_max;
-               chan->sec_level = pchan->sec_level;
-               chan->flags = pchan->flags;
-               chan->tx_credits = pchan->tx_credits;
-               chan->rx_credits = pchan->rx_credits;
-
-               if (chan->chan_type == L2CAP_CHAN_FIXED) {
-                       chan->scid = pchan->scid;
-                       chan->dcid = pchan->scid;
-               }
-
+               /* Channel configuration is inherited from the parent by
+                * l2cap_new_connection().
+                */
                security_sk_clone(parent, sk);
        } else {
                switch (sk->sk_type) {
@@ -1956,7 +1953,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                        chan->mode = L2CAP_MODE_BASIC;
                }
 
-               l2cap_chan_set_defaults(chan);
+               l2cap_chan_set_defaults(chan, NULL);
        }
 
        /* Default config options */
@@ -1975,10 +1972,10 @@ static struct proto l2cap_proto = {
 };
 
 static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
-                                    int proto, gfp_t prio, int kern)
+                                    int proto, gfp_t prio, int kern,
+                                    struct l2cap_chan *chan)
 {
        struct sock *sk;
-       struct l2cap_chan *chan;
 
        sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
        if (!sk)
@@ -1989,16 +1986,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
        INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
 
-       chan = l2cap_chan_create();
-       if (!chan) {
-               sk_free(sk);
-               if (sock)
-                       sock->sk = NULL;
-               return NULL;
-       }
-
-       l2cap_chan_hold(chan);
-
+       /* The sock takes ownership of the caller's reference on chan. */
        l2cap_pi(sk)->chan = chan;
 
        return sk;
@@ -2008,6 +1996,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
                             int kern)
 {
        struct sock *sk;
+       struct l2cap_chan *chan;
 
        BT_DBG("sock %p", sock);
 
@@ -2022,10 +2011,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
 
        sock->ops = &l2cap_sock_ops;
 
-       sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
-       if (!sk)
+       chan = l2cap_chan_create();
+       if (!chan)
                return -ENOMEM;
 
+       sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
+       if (!sk) {
+               l2cap_chan_put(chan);
+               return -ENOMEM;
+       }
+
        l2cap_sock_init(sk, NULL);
        bt_sock_link(&l2cap_sk_list, sk);
        return 0;
index d23ca1dd089365fe4606600c68475dceb561e5ee..1db10e0f617f92cdde51cf8cd5f618843a21e724 100644 (file)
@@ -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;
 }
@@ -5375,6 +5391,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
                if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED)
                        monitor->state = ADV_MONITOR_STATE_REGISTERED;
                hci_update_passive_scan(hdev);
+       } else {
+               hci_free_adv_monitor(hdev, monitor);
        }
 
        mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
@@ -7386,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));
 
@@ -7400,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,
@@ -7407,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);
 
@@ -7546,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));
 
@@ -7562,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);
 }
 
@@ -7658,6 +7694,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
        if (!err) {
                struct hci_conn_params *params;
 
+               hci_dev_lock(hdev);
+
                params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
                                                le_addr_type(cp->addr.type));
 
@@ -7666,6 +7704,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
                device_flags_changed(NULL, hdev, &cp->addr.bdaddr,
                                     cp->addr.type, hdev->conn_flags,
                                     params ? params->flags : 0);
+               hci_dev_unlock(hdev);
        }
 
        mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE,
@@ -7932,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;
 
-       return hci_le_conn_update_sync(hdev, conn, params);
+       hci_dev_unlock(hdev);
+
+       return hci_le_conn_update_sync(hdev, conn, &local);
+
+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,
@@ -8049,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);
+                       }
                }
        }
 
@@ -9843,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)
@@ -9904,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;
        }
 
index 2f008167cbaa2b4d50fb645025862459384a58c2..d7badce8746ca3c1f3fa929e3c000b04af0c3f6e 100644 (file)
@@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
        monitor->state = ADV_MONITOR_STATE_OFFLOADED;
 
 unlock:
-       if (status)
+       if (status && msft->resuming)
                hci_free_adv_monitor(hdev, monitor);
 
        hci_dev_unlock(hdev);
index fcc597be5bbd58d7ab3a952316cb24c15d90db51..c05f79b7aa31208891649634f08fb682c7f2f73e 100644 (file)
@@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk)
 /* Must be called on unlocked socket. */
 static void sco_sock_close(struct sock *sk)
 {
+       struct sco_conn *conn;
+
+       lock_sock(sk);
+       conn = sco_pi(sk)->conn;
+       if (conn)
+               sco_conn_hold(conn);
+       release_sock(sk);
+
+       if (conn)
+               disable_delayed_work_sync(&conn->timeout_work);
+
        lock_sock(sk);
-       sco_sock_clear_timer(sk);
        __sco_sock_close(sk);
        release_sock(sk);
+
+       if (conn)
+               sco_conn_put(conn);
 }
 
 static void sco_sock_init(struct sock *sk, struct sock *parent)
index 031d3022cb1e547433fd2035303fec5a5bdfb9c8..c4470958b0d5716c7a617f5d01157824f12e3d33 100644 (file)
@@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = {
        .get_sndtimeo           = l2cap_chan_no_get_sndtimeo,
 };
 
-static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+static inline int smp_new_conn_cb(struct l2cap_chan *chan,
+                                 struct l2cap_chan *new_chan)
 {
-       struct l2cap_chan *chan;
-
-       BT_DBG("pchan %p", pchan);
-
-       chan = l2cap_chan_create();
-       if (!chan)
-               return NULL;
-
-       chan->chan_type = pchan->chan_type;
-       chan->ops       = &smp_chan_ops;
-       chan->scid      = pchan->scid;
-       chan->dcid      = chan->scid;
-       chan->imtu      = pchan->imtu;
-       chan->omtu      = pchan->omtu;
-       chan->mode      = pchan->mode;
+       new_chan->ops = &smp_chan_ops;
 
        /* Other L2CAP channels may request SMP routines in order to
         * change the security level. This means that the SMP channel
         * lock must be considered in its own category to avoid lockdep
         * warnings.
         */
-       atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
-
-       BT_DBG("created chan %p", chan);
+       atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
 
-       return chan;
+       return 0;
 }
 
 static const struct l2cap_ops smp_root_chan_ops = {
@@ -3288,7 +3273,7 @@ create_chan:
 
        l2cap_add_scid(chan, cid);
 
-       l2cap_chan_set_defaults(chan);
+       l2cap_chan_set_defaults(chan, NULL);
 
        if (cid == L2CAP_CID_SMP) {
                u8 bdaddr_type;
index f20c039e44c8d719c6e6b77ee7c72508a6fbde30..96c9a8f57c87029cfdcec7cdc7cf219232533b7e 100644 (file)
@@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
            left - sizeof(struct ebt_entry_match) < m->match_size)
                return -EINVAL;
 
+       if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+               return -EINVAL;
+
        match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision);
        if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) {
                if (!IS_ERR(match))
@@ -921,8 +924,7 @@ static int translate_table(struct net *net, const char *name,
                 * if an error occurs
                 */
                newinfo->chainstack =
-                       vmalloc_array(nr_cpu_ids,
-                                     sizeof(*(newinfo->chainstack)));
+                       vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
                if (!newinfo->chainstack)
                        return -ENOMEM;
                for_each_possible_cpu(i) {
@@ -1434,6 +1436,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len)
        if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
                return -EFAULT;
 
+       hlp.name[sizeof(hlp.name) - 1] = '\0';
+
        if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
                return -EINVAL;
 
@@ -2273,6 +2277,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
 
        memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
 
+       repl->name[sizeof(repl->name) - 1] = '\0';
+
        /* starting with hook_entry, 32 vs. 64 bit structures are different */
        for (i = 0; i < NF_BR_NUMHOOKS; i++)
                repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]);
@@ -2395,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg,
        if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
                return -EFAULT;
 
+       hlp.name[sizeof(hlp.name) - 1] = '\0';
+
        /* try real handler in case userland supplied needed padding */
        if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
                return update_counters(net, arg, len);
index a4bef2c48a559cf7b0fa1d00d1fb1187e31359d8..3d637a1e0ac1a9e056cd31545985a625ef8fcdc7 100644 (file)
@@ -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 (op->kt_ival1 && (op->count > 0)) {
-               bcm_can_tx(op);
-               if (!op->count && (op->flags & TX_COUNTEVT)) {
+       if (tx_ival1) {
+               u32 flags, count;
+               struct bcm_timeval ival1, ival2;
 
+               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);
index c48b4a818297e2a1348a2b64016d0f4ff613e683..54becaf6898f134a1d44a94eb935d6bcc8d064dc 100644 (file)
@@ -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);
-
-                               can_rx_unregister(net, dev, so->txid,
-                                                 SINGLE_MASK(so->txid),
-                                                 isotp_rcv_echo, sk);
-                               dev_put(dev);
-                               synchronize_rcu();
-                       }
-               }
+       /* 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, 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);
index df93d57907da7e5cb9dc004d551a209fdb5aa05f..8a31cb23bc76d0abf6451e3958119d1237626a88 100644 (file)
@@ -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 ... */
index a26942e78e688721c9306f4e3fcbf25cd395a127..82d9c0499c95facbfde2d5593df650b2e5fe93a4 100644 (file)
@@ -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)
 {
index ecd659f79fd4a0ba6d268cfe4e1a6f68f2ff3177..1d295a8769fad5de6e204b5ee49452a524ac5b8d 100644 (file)
@@ -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);
 
index 4b3d5cfdf6e00fe76ab50f10d2c60b80d65ac74f..5933c5dab09ee1f8fdde3f79d0da87531193bdcd 100644 (file)
@@ -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);
 
index 8a59bfaa8096e002afc63fb7d2fbb01f4dd1533f..498a57f34f5b54c52697f7a76c9529eb59d4abe8 100644 (file)
@@ -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)) {
index c60ba6d292f9ae9f1526880ee933601cdf4aabe3..9efbd8ca7db832e31fc64ae2e9f04a7b9179d28a 100644 (file)
@@ -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) &&
index 4ca2eca2e94b1458720a715953690f1bbfbfd342..3e969a070f9fe4aa2f56579d0ed708c80db6d826 100644 (file)
@@ -114,6 +114,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr,
        *mod = true;
 }
 
+/**
+ * ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute
+ * @dst:  value to update
+ * @attr: netlink attribute with new value or null
+ * @mod:  pointer to bool for modification tracking
+ *
+ * Some attributes are NLA_U32 on the wire but are stored in a u8. Read the
+ * full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into
+ * the u8 pointed to by @dst; do nothing if @attr is null.
+ * Bool pointed to by @mod is set to true if this function changed the value
+ * of *dst, otherwise it is left as is.
+ */
+static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr,
+                                      bool *mod)
+{
+       u32 val;
+
+       if (!attr)
+               return;
+       val = nla_get_u32(attr);
+       DEBUG_NET_WARN_ON_ONCE(val > U8_MAX);
+       if (*dst == val)
+               return;
+
+       *dst = val;
+       *mod = true;
+}
+
 /**
  * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute
  * @dst:  value to update
index d8adc78e3775a0394543f5881f592e6a6ee95b4e..d4a1a4724b67537576776e014cc4e6e5fff6fb97 100644 (file)
@@ -570,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = {
 const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
        [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
        [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
-       [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
+       [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
        [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
        [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -851,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
        indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
 
        rxfh.hfunc = data.hfunc;
-       ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+       ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
        if (rxfh.hfunc == data.hfunc)
                rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
 
@@ -860,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
                goto exit_free_indir;
 
        rxfh.input_xfrm = data.input_xfrm;
-       ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+       ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+                           &mod);
        xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
        if (rxfh.input_xfrm == data.input_xfrm)
                rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
@@ -934,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = {
 const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = {
        [ETHTOOL_A_RSS_HEADER]  = NLA_POLICY_NESTED(ethnl_header_policy),
        [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
-       [ETHTOOL_A_RSS_HFUNC]   = NLA_POLICY_MIN(NLA_U32, 1),
+       [ETHTOOL_A_RSS_HFUNC]   = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
        [ETHTOOL_A_RSS_INDIR]   = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_HKEY]    = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -1048,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
                goto exit_clean_data;
        indir_user_size = ret;
 
-       ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+       ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
 
        ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
        if (ret)
                goto exit_free_indir;
 
        rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
-       ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+       ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+                           &mod);
 
        ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size);
        if (!ctx) {
index e11dc86ceda0be33046b16512a0127100b04fbe8..6badad29593b718e21ab16fc86357be0efa6b30f 100644 (file)
@@ -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:
index b6337a47c1418589bf8ef31e00fd98b6187f5271..bb2d4441a49294f26e55dd0b56dfda1fd55ad376 100644 (file)
@@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf)
 
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
+       bool put = false;
+
        spin_lock_bh(&im->lock);
        if (timer_delete(&im->timer))
-               refcount_dec(&im->refcnt);
+               put = true;
        WRITE_ONCE(im->tm_running, 0);
        WRITE_ONCE(im->reporter, 0);
        im->unsolicit_count = 0;
        spin_unlock_bh(&im->lock);
+
+       if (put)
+               ip_ma_put(im);
 }
 
 /* It must be called with locked im->lock */
@@ -248,20 +253,26 @@ static void igmp_gq_start_timer(struct in_device *in_dev)
                return;
 
        in_dev->mr_gq_running = 1;
-       if (!mod_timer(&in_dev->mr_gq_timer, exp))
-               in_dev_hold(in_dev);
+       if (in_dev_hold_safe(in_dev)) {
+               if (mod_timer(&in_dev->mr_gq_timer, exp))
+                       in_dev_put(in_dev);
+       }
 }
 
 static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
 {
-       int tv = get_random_u32_below(delay);
+       if (in_dev_hold_safe(in_dev)) {
+               int tv = get_random_u32_below(delay);
 
-       if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
-               in_dev_hold(in_dev);
+               if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2))
+                       in_dev_put(in_dev);
+       }
 }
 
 static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
 {
+       bool put = false;
+
        spin_lock_bh(&im->lock);
        im->unsolicit_count = 0;
        if (timer_delete(&im->timer)) {
@@ -271,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
                        spin_unlock_bh(&im->lock);
                        return;
                }
-               refcount_dec(&im->refcnt);
+               put = true;
        }
        igmp_start_timer(im, max_delay);
        spin_unlock_bh(&im->lock);
+
+       if (put)
+               ip_ma_put(im);
 }
 
 
@@ -1922,6 +1936,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
 #endif
 
        while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) {
+               ip_mc_hash_remove(in_dev, i);
                in_dev->mc_list = i->next_rcu;
                WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1);
                ip_mc_clear_src(i);
index cc0bd73f36b6d63d253d122be9bb2ac33f34d602..8e905b50deadbe7e529985c5c1d2b44aff952d76 100644 (file)
@@ -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;
index 209ef7522508fcc3974ae71d35dd66cba96b73d0..4a46da375043be26bcbb282a0b0c3ba85bfc5186 100644 (file)
@@ -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;
index 04b811b3be978e36b0fd4ecd8312313d73ed2ed9..4d2b9377ba2de5ad2b6d503bb4b285bfd1307a55 100644 (file)
@@ -1083,8 +1083,10 @@ static void mld_gq_start_work(struct inet6_dev *idev)
        mc_assert_locked(idev);
 
        idev->mc_gq_running = 1;
-       if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
-               in6_dev_hold(idev);
+       if (in6_dev_hold_safe(idev)) {
+               if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
+                       in6_dev_put(idev);
+       }
 }
 
 static void mld_gq_stop_work(struct inet6_dev *idev)
@@ -1102,8 +1104,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)
 
        mc_assert_locked(idev);
 
-       if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
-               in6_dev_hold(idev);
+       if (in6_dev_hold_safe(idev)) {
+               if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
+                       in6_dev_put(idev);
+       }
 }
 
 static void mld_ifc_stop_work(struct inet6_dev *idev)
@@ -1121,8 +1125,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)
 
        mc_assert_locked(idev);
 
-       if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
-               in6_dev_hold(idev);
+       if (in6_dev_hold_safe(idev)) {
+               if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
+                       in6_dev_put(idev);
+       }
 }
 
 static void mld_dad_stop_work(struct inet6_dev *idev)
@@ -1395,18 +1401,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
 void igmp6_event_query(struct sk_buff *skb)
 {
        struct inet6_dev *idev = __in6_dev_get(skb->dev);
+       bool put = false;
 
        if (!idev || idev->dead)
                goto out;
 
        spin_lock_bh(&idev->mc_query_lock);
-       if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) {
+       if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS &&
+           in6_dev_hold_safe(idev)) {
                __skb_queue_tail(&idev->mc_query_queue, skb);
-               if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
-                       in6_dev_hold(idev);
+               if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
+                       put = true;
                skb = NULL;
        }
        spin_unlock_bh(&idev->mc_query_lock);
+
+       if (put)
+               in6_dev_put(idev);
 out:
        kfree_skb(skb);
 }
@@ -1570,18 +1581,23 @@ static void mld_query_work(struct work_struct *work)
 void igmp6_event_report(struct sk_buff *skb)
 {
        struct inet6_dev *idev = __in6_dev_get(skb->dev);
+       bool put = false;
 
        if (!idev || idev->dead)
                goto out;
 
        spin_lock_bh(&idev->mc_report_lock);
-       if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) {
+       if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS &&
+           in6_dev_hold_safe(idev)) {
                __skb_queue_tail(&idev->mc_report_queue, skb);
-               if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
-                       in6_dev_hold(idev);
+               if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
+                       put = true;
                skb = NULL;
        }
        spin_unlock_bh(&idev->mc_report_lock);
+
+       if (put)
+               in6_dev_put(idev);
 out:
        kfree_skb(skb);
 }
index 6d80f85e55fa7fb662eb251ac79677ca9582b5d0..a7025ec870359b22888c5904e971447c6a88314a 100644 (file)
@@ -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);
index 70da2f2ce064ca1424ea5f5381c4863b42b8bc51..1258783ed87629e248c6173a01634132595f2a40 100644 (file)
@@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par)
        }
 
        hdrlen = ipv6_authlen(ah);
+       if (skb->len - ptr < hdrlen) {
+               /* Packet smaller than its length field */
+               par->hotdrop = true;
+               return false;
+       }
 
        pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
        pr_debug("RES %04X ", ah->reserved);
index 450dd53846a2f77b81cb863fc62ad0236065f9c4..6d1a5d2026a678c69930497b8596056a73658795 100644 (file)
@@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
        hdrlen = ipv6_optlen(oh);
        if (skb->len - ptr < hdrlen) {
                /* Packet smaller than it's length field */
+               par->hotdrop = true;
                return false;
        }
 
index 5561bd9cea818572f8eed84bbb018ecff7e1fc47..278b52752f36456e48a0b856889fb6021e09fda0 100644 (file)
@@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par)
 
        hdrlen = ipv6_optlen(rh);
        if (skb->len - ptr < hdrlen) {
-               /* Pcket smaller than its length field */
+               /* Packet smaller than its length field */
+               par->hotdrop = true;
                return false;
        }
 
index 64ab23ff559bb9e97b989d19802480c703cdbcbe..599c49bf0a0afd5054e926a5f2b1f0677bcd8b4d 100644 (file)
@@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
        skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
        memmove(skb->head + sizeof(struct frag_hdr), skb->head,
                (skb->data - skb->head) - sizeof(struct frag_hdr));
-       skb->mac_header += sizeof(struct frag_hdr);
+       if (skb_mac_header_was_set(skb))
+               skb->mac_header += sizeof(struct frag_hdr);
        skb->network_header += sizeof(struct frag_hdr);
 
        skb_reset_transport_header(skb);
@@ -418,7 +419,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
                        return -1;
                }
                if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
-                       BUG();
+                       return -1;
                if (nexthdr == NEXTHDR_AUTH)
                        hdrlen = ipv6_authlen(&hdr);
                else
index ebe161d72fbd07a13d92812b45b5a3ce2464a015..522ba45ce9b759a6a76fd8603400097bc99c60c6 100644 (file)
@@ -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;
index 125ea9a5b8a082052380b7fd7ed7123f5247d7cc..3b749475f6ed6573eec7337b502d9188ee199bee 100644 (file)
@@ -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;
        }
 
index fed240b453bd9b517a571d65640f939fa3bbfd0d..b85fb9767dec211918f7e8d6a16e88c0b20455b8 100644 (file)
@@ -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;
 }
 
index 8ed1be1ecccc5927eb9d76446999b4c63cff238d..b0447c33dbf096b40d0a79fee4bf34f9e8b44e15 100644 (file)
@@ -312,6 +312,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
        /* assign new connection to its SAP */
        llc_sap_add_socket(sap, sk);
        sock_reset_flag(sk, SOCK_ZAPPED);
+       llc_sap_put(sap);
        rc = 0;
 out:
        dev_put(dev);
index e8f427375c6891e1fdf98fc9f7f7e3bca05a726d..260460d50f54c4bdb0de72d766f589e3443a33c2 100644 (file)
@@ -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;
 }
index 3b58af59f7e4295cad37fc61a0b14d63f9f712eb..b00191e02a63d7b659f6c93e1f15cfda3c58922b 100644 (file)
@@ -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;
 }
index d0fd6054f1820074f30f014205891894468376bb..882f91abbb6624b2d779ec4dbccf4229de765388 100644 (file)
@@ -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);
index 086272c3ec08c1555e4c37ed5a892d701ad529a9..43460a705a6bd04a6dcb82b48061b8d7dd2771bf 100644 (file)
@@ -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)
index 90d295cc364fb3d5839753b5e2c1112970de0330..eb1eaaf34612e1d7405c80fe8f312fc69a4e41bd 100644 (file)
@@ -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);
index 9e92337bb6f9e5f3b17ef5f57f801c55ca817223..fa773f3b0541ace523162a65579c9cd284d4025d 100644 (file)
@@ -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
index 1800bb96dd294fd0df210ad868ab7bfe183b087a..19e08661be43ab06d8ba157d768c4e02fd879331 100644 (file)
@@ -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);
 
index fb9a3574afe99d888cc706696f3ac3138550d61b..d9ea19be075dc686c4a34884340b37a716e07ce7 100644 (file)
@@ -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));
        }
index 02b587ff850461eab8c8e51b9a0ef200eb691dc2..22eba0e6e54c5b1c7fe67f4ce27feecd9d19d8b2 100644 (file)
@@ -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);
 }
index c13b209fad47aaac20840db486f82979610b5c75..91b14112e24f086f92fba813228f8e5ac269d9f8 100644 (file)
@@ -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);
 }
index f6d4ae4127c8738dff653e27f85df2ec4d7caab9..59f73dabe6e0ac16288fc3f2ff72819d35714a7c 100644 (file)
@@ -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;
        }
 
index 000be60d9580343e40f33ff872ec0aff7daa41d0..b823720630e726619da962494a80cade41c0118d 100644 (file)
@@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local)
 
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
-               list_del(&sdata->list);
+               list_del_rcu(&sdata->list);
 
                unregister_netdevice(sdata->dev);
        }
index ca504d9626cff77da1cb08eb4d37fc7043950bbb..318cb7e2ac5f7ca1592217988ebb14fe35626811 100644 (file)
@@ -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)
index dedf59b661ddf37d3a93ef5a049ad75b7413f7d8..8231317b0f1f4bf145923756c287faccb839aebf 100644 (file)
@@ -75,15 +75,17 @@ struct hbucket {
 struct htable_gc {
        struct delayed_work dwork;
        struct ip_set *set;     /* Set the gc belongs to */
+       spinlock_t lock;        /* Lock to exclude gc and resize */
        u32 region;             /* Last gc run position */
 };
 
 /* The hash table: the table size stored here in order to make resizing easy */
 struct htable {
-       atomic_t ref;           /* References for resizing */
+       bool resizing;          /* Mark ongoing resize */
        atomic_t uref;          /* References for dumping and gc */
        u8 htable_bits;         /* size of hash table == 2^htable_bits */
        u32 maxelem;            /* Maxelem per region */
+       struct list_head ad;    /* Resize add|del backlist */
        struct ip_set_region *hregion;  /* Region locks and ext sizes */
        struct hbucket __rcu *bucket[]; /* hashtable buckets */
 };
@@ -301,11 +303,13 @@ struct htype {
        u8 netmask;             /* netmask value for subnets to store */
        union nf_inet_addr bitmask;     /* stores bitmask */
 #endif
-       struct list_head ad;    /* Resize add|del backlist */
-       struct mtype_elem next; /* temporary storage for uadd */
 #ifdef IP_SET_HASH_WITH_NETS
        struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */
 #endif
+       /* Because 'next' is IPv4/IPv6 dependent, no elements of this
+        * structure and referred in create() may come after 'next'.
+        */
+       struct mtype_elem next; /* temporary storage for uadd */
 };
 
 /* ADD|DEL entries saved during resize */
@@ -451,13 +455,14 @@ static void
 mtype_destroy(struct ip_set *set)
 {
        struct htype *h = set->data;
+       struct htable *t = (__force struct htable *)h->table;
        struct list_head *l, *lt;
 
-       mtype_ahash_destroy(set, (__force struct htable *)h->table, true);
-       list_for_each_safe(l, lt, &h->ad) {
+       list_for_each_safe(l, lt, &t->ad) {
                list_del(l);
                kfree(l);
        }
+       mtype_ahash_destroy(set, t, true);
        kfree(h);
 
        set->data = NULL;
@@ -569,9 +574,10 @@ mtype_gc(struct work_struct *work)
        set = gc->set;
        h = set->data;
 
-       spin_lock_bh(&set->lock);
-       t = ipset_dereference_set(h->table, set);
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
        atomic_inc(&t->uref);
+       rcu_read_unlock_bh();
        numof_locks = ahash_numof_locks(t->htable_bits);
        r = gc->region++;
        if (r >= numof_locks) {
@@ -580,11 +586,13 @@ mtype_gc(struct work_struct *work)
        next_run = (IPSET_GC_PERIOD(set->timeout) * HZ) / numof_locks;
        if (next_run < HZ/10)
                next_run = HZ/10;
-       spin_unlock_bh(&set->lock);
 
-       mtype_gc_do(set, h, t, r);
+       spin_lock_bh(&gc->lock);
+       if (!t->resizing)
+               mtype_gc_do(set, h, t, r);
+       spin_unlock_bh(&gc->lock);
 
-       if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
+       if (atomic_dec_and_test(&t->uref) && t->resizing) {
                pr_debug("Table destroy after resize by expire: %p\n", t);
                mtype_ahash_destroy(set, t, false);
        }
@@ -668,15 +676,18 @@ retry:
        }
        t->htable_bits = htable_bits;
        t->maxelem = h->maxelem / ahash_numof_locks(htable_bits);
+       INIT_LIST_HEAD(&t->ad);
        for (i = 0; i < ahash_numof_locks(htable_bits); i++)
                spin_lock_init(&t->hregion[i].lock);
 
        /* There can't be another parallel resizing,
-        * but dumping, gc, kernel side add/del are possible
+        * but dumping and kernel side add/del are possible
         */
        orig = ipset_dereference_bh_nfnl(h->table);
-       atomic_set(&orig->ref, 1);
        atomic_inc(&orig->uref);
+       spin_lock_bh(&h->gc.lock);
+       orig->resizing = true;
+       spin_unlock_bh(&h->gc.lock);
        pr_debug("attempt to resize set %s from %u to %u, t %p\n",
                 set->name, orig->htable_bits, htable_bits, orig);
        for (r = 0; r < ahash_numof_locks(orig->htable_bits); r++) {
@@ -768,7 +779,7 @@ retry:
         * Kernel-side add cannot trigger a resize and userspace actions
         * are serialized by the mutex.
         */
-       list_for_each_safe(l, lt, &h->ad) {
+       list_for_each_safe(l, lt, &orig->ad) {
                x = list_entry(l, struct mtype_resize_ad, list);
                if (x->ad == IPSET_ADD) {
                        mtype_add(set, &x->d, &x->ext, &x->mext, x->flags);
@@ -792,11 +803,24 @@ out:
 
 cleanup:
        rcu_read_unlock_bh();
-       atomic_set(&orig->ref, 0);
+       spin_lock_bh(&h->gc.lock);
+       orig->resizing = false;
+       spin_unlock_bh(&h->gc.lock);
+       /* Make sure parallel readers see that orig->resizing is false
+        * before we decrement uref */
+       synchronize_rcu();
        atomic_dec(&orig->uref);
        mtype_ahash_destroy(set, t, false);
        if (ret == -EAGAIN)
                goto retry;
+
+       /* Cleanup the backlog of ADD/DEL elements */
+       spin_lock_bh(&set->lock);
+       list_for_each_safe(l, lt, &orig->ad) {
+               list_del(l);
+               kfree(l);
+       }
+       spin_unlock_bh(&set->lock);
        goto out;
 
 hbwarn:
@@ -860,15 +884,13 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        key = HKEY(value, h->initval, t->htable_bits);
        r = ahash_region(key);
        atomic_inc(&t->uref);
+       rcu_read_unlock_bh();
        elements = t->hregion[r].elements;
        maxelem = t->maxelem;
        if (elements >= maxelem) {
                u32 e;
-               if (SET_WITH_TIMEOUT(set)) {
-                       rcu_read_unlock_bh();
+               if (SET_WITH_TIMEOUT(set))
                        mtype_gc_do(set, h, t, r);
-                       rcu_read_lock_bh();
-               }
                maxelem = h->maxelem;
                elements = 0;
                for (e = 0; e < ahash_numof_locks(t->htable_bits); e++)
@@ -876,7 +898,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                if (elements >= maxelem && SET_WITH_FORCEADD(set))
                        forceadd = true;
        }
-       rcu_read_unlock_bh();
 
        spin_lock_bh(&t->hregion[r].lock);
        n = rcu_dereference_bh(hbucket(t, key));
@@ -1003,7 +1024,7 @@ overwrite_extensions:
        ret = 0;
 resize:
        spin_unlock_bh(&t->hregion[r].lock);
-       if (atomic_read(&t->ref) && ext->target) {
+       if (t->resizing && ext && ext->target) {
                /* Resize is in process and kernel side add, save values */
                struct mtype_resize_ad *x;
 
@@ -1017,7 +1038,7 @@ resize:
                memcpy(&x->mext, mext, sizeof(struct ip_set_ext));
                x->flags = flags;
                spin_lock_bh(&set->lock);
-               list_add_tail(&x->list, &h->ad);
+               list_add_tail(&x->list, &t->ad);
                spin_unlock_bh(&set->lock);
        }
        goto out;
@@ -1030,7 +1051,7 @@ set_full:
 unlock:
        spin_unlock_bh(&t->hregion[r].lock);
 out:
-       if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
+       if (atomic_dec_and_test(&t->uref) && t->resizing) {
                pr_debug("Table destroy after resize by add: %p\n", t);
                mtype_ahash_destroy(set, t, false);
        }
@@ -1093,7 +1114,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 #endif
                ip_set_ext_destroy(set, data);
 
-               if (atomic_read(&t->ref) && ext->target) {
+               if (t->resizing && ext && ext->target) {
                        /* Resize is in process and kernel side del,
                         * save values
                         */
@@ -1141,10 +1162,10 @@ out:
        spin_unlock_bh(&t->hregion[r].lock);
        if (x) {
                spin_lock_bh(&set->lock);
-               list_add(&x->list, &h->ad);
+               list_add(&x->list, &t->ad);
                spin_unlock_bh(&set->lock);
        }
-       if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
+       if (atomic_dec_and_test(&t->uref) && t->resizing) {
                pr_debug("Table destroy after resize by del: %p\n", t);
                mtype_ahash_destroy(set, t, false);
        }
@@ -1353,7 +1374,7 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start)
                rcu_read_unlock_bh();
        } else if (cb->args[IPSET_CB_PRIVATE]) {
                t = (struct htable *)cb->args[IPSET_CB_PRIVATE];
-               if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
+               if (atomic_dec_and_test(&t->uref) && t->resizing) {
                        pr_debug("Table destroy after resize "
                                 " by dump: %p\n", t);
                        mtype_ahash_destroy(set, t, false);
@@ -1566,7 +1587,13 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
        if (tb[IPSET_ATTR_MAXELEM])
                maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
 
-       hsize = sizeof(*h);
+#ifdef IP_SET_PROTO_UNDEF
+       hsize = sizeof(struct htype);
+#else
+       hsize = set->family == NFPROTO_IPV6 ?
+               sizeof(struct IPSET_TOKEN(HTYPE, 6)) :
+               sizeof(struct IPSET_TOKEN(HTYPE, 4));
+#endif
        h = kzalloc(hsize, GFP_KERNEL);
        if (!h)
                return -ENOMEM;
@@ -1593,6 +1620,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
                return -ENOMEM;
        }
        h->gc.set = set;
+       spin_lock_init(&h->gc.lock);
        for (i = 0; i < ahash_numof_locks(hbits); i++)
                spin_lock_init(&t->hregion[i].lock);
        h->maxelem = maxelem;
@@ -1619,9 +1647,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
        }
        t->htable_bits = hbits;
        t->maxelem = h->maxelem / ahash_numof_locks(hbits);
+       INIT_LIST_HEAD(&t->ad);
        RCU_INIT_POINTER(h->table, t);
-
-       INIT_LIST_HEAD(&h->ad);
        set->data = h;
 #ifndef IP_SET_PROTO_UNDEF
        if (set->family == NFPROTO_IPV4) {
index d54d7da583346b81c71aeb380d13b57f85c8b384..b0e00be85cb12ce0257da0c6b2eb3cf2b922430f 100644 (file)
@@ -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
index cb36641f8d1cc103fab31e48c12a35367642d88b..6ed2622363f0d696c53c8317bba6b2ef0fb4c768 100644 (file)
@@ -1420,8 +1420,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
        cp->app = NULL;
        cp->app_data = NULL;
        /* reset struct ip_vs_seq */
-       cp->in_seq.delta = 0;
-       cp->out_seq.delta = 0;
+       memset(&cp->in_seq, 0, sizeof(cp->in_seq));
+       memset(&cp->out_seq, 0, sizeof(cp->out_seq));
 
        if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) {
                int af_id = ip_vs_af_index(cp->af);
index d40b404c1bf64647f95e657c857f615e83b20161..bafab93451d037c7f9b3316cd4517cfb3c2ea9c9 100644 (file)
@@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
 static inline void
 ip_vs_set_state(struct ip_vs_conn *cp, int direction,
                const struct sk_buff *skb,
-               struct ip_vs_proto_data *pd)
+               struct ip_vs_proto_data *pd, unsigned int iph_len)
 {
        if (likely(pd->pp->state_transition))
-               pd->pp->state_transition(cp, direction, skb, pd);
+               pd->pp->state_transition(cp, direction, skb, pd, iph_len);
 }
 
 static inline int
@@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
                ip_vs_in_stats(cp, skb);
 
                /* set state */
-               ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+               ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len);
 
                /* transmit the first SYN packet */
                ret = cp->packet_xmit(skb, cp, pd->pp, iph);
@@ -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
 
@@ -1484,7 +1483,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
 
 after_nat:
        ip_vs_out_stats(cp, skb);
-       ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
+       ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len);
        skb->ipvs_property = 1;
        if (!(cp->flags & IP_VS_CONN_F_NFCT))
                ip_vs_notrack(skb);
@@ -1767,6 +1766,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
        bool tunnel, new_cp = false;
        union nf_inet_addr *raddr;
        char *outer_proto = "IPIP";
+       unsigned int hlen_ipip;
+       int ulen = 0;
 
        *related = 1;
 
@@ -1803,9 +1804,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
        /* Now find the contained IP header */
        offset += sizeof(_icmph);
        cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
-       if (cih == NULL)
+       if (!(cih && cih->version == 4 && cih->ihl >= 5))
                return NF_ACCEPT; /* The packet looks wrong, ignore */
        raddr = (union nf_inet_addr *)&cih->daddr;
+       hlen_ipip = cih->ihl * 4;
 
        /* Special case for errors for IPIP/UDP/GRE tunnel packets */
        tunnel = false;
@@ -1821,9 +1823,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
                /* Only for known tunnel */
                if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
                        return NF_ACCEPT;
-               offset += cih->ihl * 4;
+               offset += hlen_ipip;
                cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
-               if (cih == NULL)
+               if (!(cih && cih->version == 4 && cih->ihl >= 5))
                        return NF_ACCEPT; /* The packet looks wrong, ignore */
                tunnel = true;
        } else if ((cih->protocol == IPPROTO_UDP ||     /* Can be UDP encap */
@@ -1831,12 +1833,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
                   /* Error for our tunnel must arrive at LOCAL_IN */
                   (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) {
                __u8 iproto;
-               int ulen;
 
                /* Non-first fragment has no UDP/GRE header */
                if (unlikely(cih->frag_off & htons(IP_OFFSET)))
                        return NF_ACCEPT;
-               offset2 = offset + cih->ihl * 4;
+               offset2 = offset + hlen_ipip;
                if (cih->protocol == IPPROTO_UDP) {
                        ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
                                              raddr, &iproto);
@@ -1905,6 +1906,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
        }
 
        if (tunnel) {
+               unsigned int hlen_orig = cih->ihl * 4;
                __be32 info = ic->un.gateway;
                __u8 type = ic->type;
                __u8 code = ic->code;
@@ -1921,6 +1923,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
                                goto ignore_tunnel;
                        offset2 -= ihl + sizeof(_icmph);
                        skb_reset_network_header(skb);
+                       /* Ensure the IP header is present in headroom */
+                       if (!pskb_may_pull(skb, hlen_ipip))
+                               goto ignore_tunnel;
                        IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
                                  outer_proto, &ip_hdr(skb)->saddr,
                                  &ip_hdr(skb)->daddr, mtu);
@@ -1936,8 +1941,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
                                if (dest_dst)
                                        mtu = dst_mtu(dest_dst->dst_cache);
                        }
-                       if (mtu > 68 + sizeof(struct iphdr))
-                               mtu -= sizeof(struct iphdr);
+                       if (mtu > 68 + hlen_ipip + ulen)
+                               mtu -= hlen_ipip + ulen;
                        info = htonl(mtu);
                }
                /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
@@ -1946,6 +1951,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
                if (pskb_pull(skb, offset2) == NULL)
                        goto ignore_tunnel;
                skb_reset_network_header(skb);
+               /* Ensure the IP header is present in headroom */
+               if (!pskb_may_pull(skb, hlen_orig))
+                       goto ignore_tunnel;
                IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
                        &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
                        type, code, ntohl(info));
@@ -2233,7 +2241,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
        IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
 
        ip_vs_in_stats(cp, skb);
-       ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+       ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len);
        if (cp->packet_xmit)
                ret = cp->packet_xmit(skb, cp, pp, &iph);
                /* do not touch skb anymore */
index 63c78a1f3918a79690616227c217a4c8edac05f5..c67317be17dfaf682449a6bfefeb4601609e199e 100644 (file)
@@ -372,20 +372,15 @@ static const char *sctp_state_name(int state)
 
 static inline void
 set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
-               int direction, const struct sk_buff *skb)
+               int direction, const struct sk_buff *skb,
+               unsigned int iph_len)
 {
        struct sctp_chunkhdr _sctpch, *sch;
        unsigned char chunk_type;
        int event, next_state;
-       int ihl, cofs;
+       int cofs;
 
-#ifdef CONFIG_IP_VS_IPV6
-       ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
-       ihl = ip_hdrlen(skb);
-#endif
-
-       cofs = ihl + sizeof(struct sctphdr);
+       cofs = iph_len + sizeof(struct sctphdr);
        sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
        if (sch == NULL)
                return;
@@ -468,10 +463,11 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
 
 static void
 sctp_state_transition(struct ip_vs_conn *cp, int direction,
-               const struct sk_buff *skb, struct ip_vs_proto_data *pd)
+               const struct sk_buff *skb, struct ip_vs_proto_data *pd,
+               unsigned int iph_len)
 {
        spin_lock_bh(&cp->lock);
-       set_sctp_state(pd, cp, direction, skb);
+       set_sctp_state(pd, cp, direction, skb, iph_len);
        spin_unlock_bh(&cp->lock);
 }
 
index 8cc0a8ce62411261e5b3e39df764817fb788695c..f86b763efcc4dc12e1a294e35ca3986be5fad229 100644 (file)
@@ -579,17 +579,12 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
 static void
 tcp_state_transition(struct ip_vs_conn *cp, int direction,
                     const struct sk_buff *skb,
-                    struct ip_vs_proto_data *pd)
+                    struct ip_vs_proto_data *pd,
+                    unsigned int iph_len)
 {
        struct tcphdr _tcph, *th;
 
-#ifdef CONFIG_IP_VS_IPV6
-       int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
-       int ihl = ip_hdrlen(skb);
-#endif
-
-       th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
+       th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph);
        if (th == NULL)
                return;
 
index f9de632e38cdd6b93e4baa3817ab566fdbd2df5c..58f9e255927e2eb6442308457faf4692a55957e0 100644 (file)
@@ -444,7 +444,8 @@ static const char * udp_state_name(int state)
 static void
 udp_state_transition(struct ip_vs_conn *cp, int direction,
                     const struct sk_buff *skb,
-                    struct ip_vs_proto_data *pd)
+                    struct ip_vs_proto_data *pd,
+                    unsigned int iph_len)
 {
        if (unlikely(!pd)) {
                pr_err("UDP no ns data\n");
index ce542ed4b013c82849e9fe508250a2c601d68617..9fef4335da13fd3a134a8d0807aedd0a6d143d19 100644 (file)
@@ -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;
index 91582069f6d2ee83b032bf945e89d6a1a69ca9e6..e9ea6d9466e74c190315be99f814987dfbfbb0cd 100644 (file)
@@ -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".
index 9df159448b89798eeafa1fdfb67881f0e88b30f4..cc8d8e85169fb37f47200449f0dbb9f58879078a 100644 (file)
@@ -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;
                }
index 99c5b9d671a0cad88e4f002a2673f9f2bc79bc39..b66e6543934117763bef14c550f8f09621d09ea1 100644 (file)
@@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow,
 
        switch (route->tuple[dir].xmit_type) {
        case FLOW_OFFLOAD_XMIT_DIRECT:
+               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;
-               dst_release(dst);
                break;
        case FLOW_OFFLOAD_XMIT_XFRM:
        case FLOW_OFFLOAD_XMIT_NEIGH:
@@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
 static void nft_flow_dst_release(struct flow_offload *flow,
                                 enum flow_offload_tuple_dir dir)
 {
-       if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
-           flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
-               dst_release(flow->tuplehash[dir].tuple.dst_cache);
+       dst_release(flow->tuplehash[dir].tuple.dst_cache);
 }
 
 void flow_offload_route_init(struct flow_offload *flow,
@@ -345,10 +349,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
 
        nf_ct_refresh(flow->ct, NF_CT_DAY);
 
-       if (nf_flowtable_hw_offload(flow_table)) {
-               __set_bit(NF_FLOW_HW, &flow->flags);
+       if (nf_flowtable_hw_offload(flow_table))
                nf_flow_offload_add(flow_table, flow);
-       }
 
        return 0;
 }
@@ -369,7 +371,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table,
            test_bit(NF_FLOW_CLOSING, &flow->flags))
                return;
 
-       nf_flow_offload_add(flow_table, flow);
+       if (test_bit(NF_FLOW_HW, &flow->flags))
+               nf_flow_offload_refresh(flow_table, flow);
 }
 EXPORT_SYMBOL_GPL(flow_offload_refresh);
 
index 29e93ac1e2e4000bab8c532a7fbe7c9e441bb32c..0b78decce8a9bdc3ed404f9913384335408b00d3 100644 (file)
@@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 
 static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
 {
-       if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH &&
-           tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM)
+       if (!tuple->dst_cache)
                return true;
 
        return dst_check(tuple->dst_cache, tuple->dst_cookie);
@@ -590,10 +589,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
 
 static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
                                    struct flow_offload_tuple *tuple,
-                                   __be32 *ip_daddr)
+                                   struct dst_entry *dst, __be32 *ip_daddr)
 {
        struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
-       struct rtable *rt = dst_rtable(tuple->dst_cache);
+       struct rtable *rt = dst_rtable(dst);
        u8 tos = iph->tos, ttl = iph->ttl;
        __be16 frag_off = iph->frag_off;
        u32 headroom = sizeof(*iph);
@@ -636,21 +635,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
 
 static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
                                  struct flow_offload_tuple *tuple,
-                                 __be32 *ip_daddr)
+                                 struct dst_entry *dst,  __be32 *ip_daddr)
 {
        if (tuple->tun_num)
-               return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
+               return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);
 
        return 0;
 }
 
 static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
                                      struct flow_offload_tuple *tuple,
+                                     struct dst_entry *dst,
                                      struct in6_addr **ip6_daddr)
 {
        struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
-       struct rtable *rt = dst_rtable(tuple->dst_cache);
        __u8 dsfield = ipv6_get_dsfield(ip6h);
+       struct rtable *rt = dst_rtable(dst);
        struct flowi6 fl6 = {
                .daddr = tuple->tun.src_v6,
                .saddr = tuple->tun.dst_v6,
@@ -696,10 +696,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
 
 static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
                                  struct flow_offload_tuple *tuple,
+                                 struct dst_entry *dst,
                                  struct in6_addr **ip6_daddr)
 {
        if (tuple->tun_num)
-               return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
+               return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);
 
        return 0;
 }
@@ -842,7 +843,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
        other_tuple = &flow->tuplehash[!dir].tuple;
        ip_daddr = other_tuple->src_v4.s_addr;
 
-       if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
+       if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple,
+                                  tuplehash->tuple.dst_cache, &ip_daddr) < 0)
                return NF_DROP;
 
        switch (tuplehash->tuple.xmit_type) {
@@ -1158,6 +1160,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
        ip6_daddr = &other_tuple->src_v6;
 
        if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
+                                  tuplehash->tuple.dst_cache,
                                   &ip6_daddr) < 0)
                return NF_DROP;
 
index 002ec15d988bdbf44f0439fcf21d0b5e1cd06161..801a3dd9ceea3d87907c256b8bac4f2c4128fbb6 100644 (file)
@@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
        return offload;
 }
 
+static bool nf_flow_offload_unsupported(struct flow_offload *flow)
+{
+       if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num ||
+           flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num)
+               return true;
 
-void nf_flow_offload_add(struct nf_flowtable *flowtable,
-                        struct flow_offload *flow)
+       return false;
+}
+
+void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
+                            struct flow_offload *flow)
 {
        struct flow_offload_work *offload;
 
@@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable,
        flow_offload_queue_work(offload);
 }
 
+void nf_flow_offload_add(struct nf_flowtable *flowtable,
+                        struct flow_offload *flow)
+{
+       if (nf_flow_offload_unsupported(flow))
+               return;
+
+       set_bit(NF_FLOW_HW, &flow->flags);
+       nf_flow_offload_refresh(flowtable, flow);
+}
+
 void nf_flow_offload_del(struct nf_flowtable *flowtable,
                         struct flow_offload *flow)
 {
index 67c04d8143ab91c533ab4855fb0d1a6be3eabb52..aea02f6aff092aa7200772f9e2b8dbbffb259da8 100644 (file)
@@ -289,13 +289,24 @@ next:
 
        /* Mangle destination port for Cisco phones, then fix up checksums */
        if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
+               int doff = *dptr - (const char *)skb->data;
                struct udphdr *uh;
 
+               if (doff <= 0) {
+                       DEBUG_NET_WARN_ON_ONCE(1);
+                       return NF_DROP;
+               }
+
+               /* ct_sip_info->forced_dport only expected with UDP */
+               if (nf_ct_protonum(ct) != IPPROTO_UDP)
+                       return NF_DROP;
+
                if (skb_ensure_writable(skb, skb->len)) {
                        nf_ct_helper_log(skb, ct, "cannot mangle packet");
                        return NF_DROP;
                }
 
+               *dptr = skb->data + doff;
                uh = (void *)skb->data + protoff;
                uh->dest = ct_sip_info->forced_dport;
 
index 4884f7f7aaeee032de4edea314e35e95f987d7f2..a9eaf9455c7783efa1804bf0f724f42765cce1c4 100644 (file)
@@ -6563,6 +6563,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set,
        if (err < 0)
                return err;
 
+       if (!elem.priv)
+               return 0;
+
        err = -ENOMEM;
        skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (skb == NULL)
index 2cbcca9110dbf50448d6bb205e1fc224521f853c..f062ac21034329c824edfb4eb2d60568752d2cd0 100644 (file)
@@ -316,6 +316,8 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
 
        new_policy->max_expected =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
+       if (!new_policy->max_expected)
+               new_policy->max_expected = NF_CT_EXPECT_MAX_CNT;
        if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
                return -EINVAL;
 
index fa36575998616f63798b819cd84961493010aa48..5fee61b3813cbc146ce98e9cc0bfa24b115768e5 100644 (file)
@@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log,
                        goto nla_put_failure;
 
                if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
-                       BUG();
+                       goto nla_put_failure;
        }
 
        nlh->nlmsg_len = inst->skb->tail - old_tail;
@@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = {
        },
 };
 
+static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li,
+                                       const struct sk_buff *skb,
+                                       unsigned int copy_len)
+{
+       unsigned int len = skb->len;
+
+       if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
+           li->u.ulog.copy_len < copy_len)
+               copy_len = li->u.ulog.copy_len;
+       if (!skb_frags_readable(skb))
+               len = skb_headlen(skb);
+
+       return min(len, copy_len);
+}
+
 /* log handler for internal netfilter logging api */
 static void
 nfulnl_log_packet(struct net *net,
@@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net,
                break;
 
        case NFULNL_COPY_PACKET:
-               data_len = inst->copy_range;
-               if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
-                   (li->u.ulog.copy_len < data_len))
-                       data_len = li->u.ulog.copy_len;
-
-               if (data_len > skb->len)
-                       data_len = skb->len;
-
+               data_len = nfulnl_get_copy_len(li, skb, inst->copy_range);
                size += nla_total_size(data_len);
                break;
 
index 35d4c6c628ffc19cee9e44584bd37514a82a0835..b8aaf39cb4d8eafb27c0200b51fb4472aad1c272 100644 (file)
@@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr,
 }
 #endif
 
+static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb,
+                                      unsigned int copy_range)
+{
+       unsigned int data_len = entskb->len;
+
+       if (!skb_frags_readable(entskb))
+               data_len = skb_headlen(entskb);
+
+       return min(data_len, copy_range);
+}
+
 static struct sk_buff *
 nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                           struct nf_queue_entry *entry,
@@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                    nf_queue_checksum_help(entskb))
                        return NULL;
 
-               data_len = READ_ONCE(queue->copy_range);
-               if (data_len > entskb->len)
-                       data_len = entskb->len;
-
+               data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range));
                hlen = skb_zerocopy_headlen(entskb);
                hlen = min_t(unsigned int, hlen, data_len);
                size += sizeof(struct nlattr) + hlen;
index ba512e94b4023fba26116ef1ce6b09e1b40d51de..19887439847d11bc38342ee8887749a472c90134 100644 (file)
@@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr,
        bool found;
 
        ext = nft_set_do_lookup(net, set, &regs->data[priv->sreg]);
+       if (!ext)
+               ext = nft_set_catchall_lookup(net, set);
+
        found = !!ext ^ priv->invert;
        if (!found) {
-               ext = nft_set_catchall_lookup(net, set);
-               if (!ext) {
-                       regs->verdict.code = NFT_BREAK;
-                       return;
-               }
+               regs->verdict.code = NFT_BREAK;
+               return;
        }
 
        if (ext) {
index 018bbb6df4ce43aa43b146caabc7d4dbdb123228..6222e9bb57bc901970f1085f11f8af586bffd622 100644 (file)
@@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,
        if (!interval || nft_set_elem_expired(interval->from))
                return ERR_PTR(-ENOENT);
 
-       if (flags & NFT_SET_ELEM_INTERVAL_END)
+       if (flags & NFT_SET_ELEM_INTERVAL_END) {
+               if (!interval->to)
+                       return NULL;
+
                rbe = container_of(interval->to, struct nft_rbtree_elem, ext);
-       else
+       } else {
                rbe = container_of(interval->from, struct nft_rbtree_elem, ext);
+       }
 
        return &rbe->priv;
 }
index 4277084de2e70c995f49cadd411cab70473b3ad9..2cf27f7d59b95fc8beb0c1b75c7f3e67ea74d6e7 100644 (file)
@@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par)
        return ret;
 }
 
+static int connmark_tg_check_v2(const struct xt_tgchk_param *par)
+{
+       const struct xt_connmark_tginfo2 *info = par->targinfo;
+
+       if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32)
+               return -EINVAL;
+
+       return connmark_tg_check(par);
+}
+
 static void connmark_tg_destroy(const struct xt_tgdtor_param *par)
 {
        nf_ct_netns_put(par->net, par->family);
@@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = {
                .name           = "CONNMARK",
                .revision       = 2,
                .family         = NFPROTO_IPV4,
-               .checkentry     = connmark_tg_check,
+               .checkentry     = connmark_tg_check_v2,
                .target         = connmark_tg_v2,
                .targetsize     = sizeof(struct xt_connmark_tginfo2),
                .destroy        = connmark_tg_destroy,
@@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = {
                .name           = "CONNMARK",
                .revision       = 2,
                .family         = NFPROTO_IPV6,
-               .checkentry     = connmark_tg_check,
+               .checkentry     = connmark_tg_check_v2,
                .target         = connmark_tg_v2,
                .targetsize     = sizeof(struct xt_connmark_tginfo2),
                .destroy        = connmark_tg_destroy,
index b4f7bbc3f3caf9e3bfcf841da5ff94b01e9ded16..51c7f7ce88d9acd9d715caf1ef3ac263eef273b9 100644 (file)
@@ -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);
 }
 
index dd98f758176c2ed13c3aae4f6b3095a96463f034..a388881c68d42bd15171afce25a2d1669f028048 100644 (file)
@@ -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) {
index b1d736c15fcbe5e22ae9077feb8e1f11c8afa8bb..7c05b63425784e9933243fa19365abcd89b1c3a5 100644 (file)
@@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
        const struct xt_rateest_match_info *info = par->matchinfo;
        struct gnet_stats_rate_est64 sample = {0};
-       u_int32_t bps1, bps2, pps1, pps2;
+       u64 bps1, bps2, pps1, pps2;
        bool ret = true;
 
        gen_estimator_read(&info->est1->rate_est, &sample);
index 117d4615d6684c53b3a13225d2222a0993df0002..dabbaa742874cb91737b2713bbba4ead69f31c4d 100644 (file)
@@ -14,8 +14,8 @@
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_u32.h>
 
-static bool u32_match_it(const struct xt_u32 *data,
-                        const struct sk_buff *skb)
+static int u32_match_it(const struct xt_u32 *data,
+                       const struct sk_buff *skb)
 {
        const struct xt_u32_test *ct;
        unsigned int testind;
@@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data,
                        return false;
 
                if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0)
-                       BUG();
+                       return -1;
+
                val   = ntohl(n);
                nnums = ct->nnums;
 
@@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data,
 
                                if (skb_copy_bits(skb, at + pos, &n,
                                                    sizeof(n)) < 0)
-                                       BUG();
+                                       return -1;
                                val = ntohl(n);
                                break;
                        }
@@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data,
 static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
        const struct xt_u32 *data = par->matchinfo;
-       bool ret;
+       int ret;
 
        ret = u32_match_it(data, skb);
+       if (ret < 0) {
+               par->hotdrop = true;
+               return false;
+       }
+
        return ret ^ data->invert;
 }
 
@@ -100,7 +106,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par)
 {
        const struct xt_u32 *data = par->matchinfo;
        const struct xt_u32_test *ct;
-       unsigned int i;
+       unsigned int i, j;
 
        if (data->ntests > ARRAY_SIZE(data->tests))
                return -EINVAL;
@@ -111,6 +117,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par)
                if (ct->nnums > ARRAY_SIZE(ct->location) ||
                    ct->nvalues > ARRAY_SIZE(ct->value))
                        return -EINVAL;
+
+               for (j = 1; j < ct->nnums; ++j) {
+                       switch (ct->location[j].nextop) {
+                       case XT_U32_LEFTSH:
+                       case XT_U32_RIGHTSH:
+                               if (ct->location[j].number >= 32)
+                                       return -EINVAL;
+                               break;
+                       }
+               }
        }
 
        return 0;
index 13052408a132f489954973d910d8430fca05e2ac..d8079dee700edc3881a501355084705bfd4cb42c 100644 (file)
@@ -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)
index b68be143a067f9b7c5a1ab36195d05150d1ec234..f141634df21489495e933efbb80a18b7a5687d42 100644 (file)
@@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act,
 
 static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act)
 {
-       if (is_tcf_pedit(act))
-               return tcf_pedit_nkeys(act);
-       else
-               return 1;
+       unsigned int count;
+
+       if (is_tcf_pedit(act)) {
+               spin_lock_bh(&act->tcfa_lock);
+               count = tcf_pedit_nkeys_locked(act);
+               spin_unlock_bh(&act->tcfa_lock);
+               return count;
+       }
+       return 1;
 }
 
 static bool tc_act_skip_hw(u32 flags)
index 0d652dea4a691fce18f467bc7f256718d62f5981..d4d47a9921f45777eb11afac9ab2e6b9c25cf770 100644 (file)
@@ -567,9 +567,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
 {
        if (bind) {
                struct flow_action_entry *entry = entry_data;
+               int nkeys = tcf_pedit_nkeys_locked(act);
                int k;
 
-               for (k = 0; k < tcf_pedit_nkeys(act); k++) {
+               /* If the required keys exceed the remaining capacity return
+                * -ENOSPC to abort the offload and fallback to software.
+                */
+               if (nkeys > *index_inc) {
+                       NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys");
+                       return -ENOSPC;
+               }
+
+               for (k = 0; k < nkeys; k++) {
                        switch (tcf_pedit_cmd(act, k)) {
                        case TCA_PEDIT_KEY_EX_CMD_SET:
                                entry->id = FLOW_ACTION_MANGLE;
@@ -606,7 +615,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
                        return -EOPNOTSUPP;
                }
 
-               for (k = 1; k < tcf_pedit_nkeys(act); k++) {
+               for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) {
                        if (cmd != tcf_pedit_cmd(act, k)) {
                                NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
                                return -EOPNOTSUPP;
index 876b30c5709e1f7ac75def6b238f6a097136b218..b14807761d829ec174d70efd131b28d12f15ddd8 100644 (file)
@@ -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,
index 3e67600a4a1a10a8d63c7c4216f09fd4223fefe3..ffeea6db833747b5befee4d6c0ce2f889771a7e1 100644 (file)
@@ -3886,12 +3886,21 @@ int tc_setup_action(struct flow_action *flow_action,
 
                entry = &flow_action->entries[j];
                spin_lock_bh(&act->tcfa_lock);
+
+               /* Abort the offload if we have exhausted the allocated capacity */
+               if (j >= flow_action->num_entries) {
+                       NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow");
+                       err = -ENOSPC;
+                       goto err_out_locked;
+               }
+
                err = tcf_act_get_user_cookie(entry, act);
                if (err)
                        goto err_out_locked;
 
-               index = 0;
-               err = tc_setup_offload_act(act, entry, &index, extack);
+               index = flow_action->num_entries - j;
+               err = tc_setup_offload_act(act, entry, &index,
+                                          extack);
                if (err)
                        goto err_out_locked;
 
@@ -3945,10 +3954,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
        int i;
 
        tcf_exts_for_each_action(i, act, exts) {
-               if (is_tcf_pedit(act))
-                       num_acts += tcf_pedit_nkeys(act);
-               else
+               if (is_tcf_pedit(act)) {
+                       spin_lock_bh(&act->tcfa_lock);
+                       num_acts += tcf_pedit_nkeys_locked(act);
+                       spin_unlock_bh(&act->tcfa_lock);
+               } else {
                        num_acts++;
+               }
        }
        return num_acts;
 }
index a3c185505afce405d1a1e5911d22cfc325d69bb2..f78f8e9507766703306da1e025f7fa21a44346d3 100644 (file)
@@ -1389,10 +1389,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off)
        if (qd->min_netlen > len)
                WRITE_ONCE(qd->min_netlen, len);
 
-       len += q->rate_overhead;
-
-       if (len < q->rate_mpu)
-               len = q->rate_mpu;
+       len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu);
 
        if (q->atm_mode == CAKE_ATM_ATM) {
                len += 47;
index 24ba31f8c82855d9f7bb8c1a5f1e94b91f7f6371..5c42a29a981cb623de6500493a488b6450200b1b 100644 (file)
@@ -311,14 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
        int subq = skb_get_queue_mapping(skb);
        struct sk_buff *skb_res = NULL;
 
-       rcu_read_lock_bh();
-
-       start = rcu_dereference_bh(master->slaves);
-
 restart:
        nores = 0;
        busy = 0;
 
+       rcu_read_lock();
+
+       start = rcu_dereference(master->slaves);
+
        q = start;
        if (!q)
                goto drop;
@@ -345,17 +345,17 @@ restart:
                                    netdev_start_xmit(skb, slave, slave_txq, false) ==
                                    NETDEV_TX_OK) {
                                        __netif_tx_unlock(slave_txq);
-                                       spin_lock_bh(&master->slaves_lock);
+                                       spin_lock(&master->slaves_lock);
                                        if (rcu_dereference_protected(master->slaves,
                                                                      lockdep_is_held(&master->slaves_lock)) == q)
                                                rcu_assign_pointer(master->slaves,
                                                                   rcu_dereference_protected(NEXT_SLAVE(q),
                                                                                             lockdep_is_held(&master->slaves_lock)));
-                                       spin_unlock_bh(&master->slaves_lock);
+                                       spin_unlock(&master->slaves_lock);
                                        netif_wake_queue(dev);
                                        master->tx_packets++;
                                        master->tx_bytes += length;
-                                       rcu_read_unlock_bh();
+                                       rcu_read_unlock();
                                        return NETDEV_TX_OK;
                                }
                                __netif_tx_unlock(slave_txq);
@@ -364,37 +364,38 @@ restart:
                                busy = 1;
                        break;
                case 1:
-                       spin_lock_bh(&master->slaves_lock);
+                       spin_lock(&master->slaves_lock);
                        if (rcu_dereference_protected(master->slaves,
                                                      lockdep_is_held(&master->slaves_lock)) == q)
                                rcu_assign_pointer(master->slaves,
                                                   rcu_dereference_protected(NEXT_SLAVE(q),
                                                                             lockdep_is_held(&master->slaves_lock)));
-                       spin_unlock_bh(&master->slaves_lock);
-                       rcu_read_unlock_bh();
+                       spin_unlock(&master->slaves_lock);
+                       rcu_read_unlock();
                        return NETDEV_TX_OK;
                default:
                        nores = 1;
                        break;
                }
                __skb_pull(skb, skb_network_offset(skb));
-       } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start);
+       } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start);
 
        if (nores && skb_res == NULL) {
                skb_res = skb;
+               rcu_read_unlock();
                goto restart;
        }
 
        if (busy) {
                netif_stop_queue(dev);
-               rcu_read_unlock_bh();
+               rcu_read_unlock();
                return NETDEV_TX_BUSY;
        }
        master->tx_errors++;
 
 drop:
        master->tx_dropped++;
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
index d23d935e128e96e8917f3c7a600eb8cd220b5817..3893b44448b3816ec1359e49f150fd26a56a1165 100644 (file)
@@ -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
index 619b3bab38248b3ed65f5fc0a8fb625fd310781c..32d6d03df321459f44d3a5284f61e138a7194348 100644 (file)
@@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
 {
        struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
        struct smc_cdc_msg *cdc = buf;
+       struct smc_sock *smc = NULL;
        struct smc_connection *conn;
        struct smc_link_group *lgr;
-       struct smc_sock *smc;
 
        if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved))
                return; /* short message */
@@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
        lgr = smc_get_lgr(link);
        read_lock_bh(&lgr->conns_lock);
        conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
-       read_unlock_bh(&lgr->conns_lock);
-       if (!conn || conn->out_of_sync)
+       if (!conn || conn->out_of_sync) {
+               read_unlock_bh(&lgr->conns_lock);
                return;
+       }
        smc = container_of(conn, struct smc_sock, conn);
+       sock_hold(&smc->sk);
+       read_unlock_bh(&lgr->conns_lock);
 
        if (cdc->prod_flags.failover_validation) {
                smc_cdc_msg_validate(smc, cdc, link);
-               return;
+               goto out;
        }
        if (smc_cdc_before(ntohs(cdc->seqno),
                           conn->local_rx_ctrl.seqno))
                /* received seqno is old */
-               return;
+               goto out;
 
        smc_cdc_msg_recv(smc, cdc);
+out:
+       sock_put(&smc->sk);
 }
 
 static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {
index bc8ca470718b7ae3b7ecc2efc453c91c072f5dc6..efa26899bc7dcd97425d324d63ed7b55aed9c559 100644 (file)
@@ -1026,8 +1026,23 @@ rpc_free_auth(struct rpc_clnt *clnt)
        return NULL;
 }
 
-/*
- * Release reference to the RPC client
+/**
+ * rpc_hold_client - acquire a reference on an rpc_clnt
+ * @clnt: rpc_clnt to pin
+ *
+ * Pairs with rpc_release_client().
+ */
+void rpc_hold_client(struct rpc_clnt *clnt)
+{
+       refcount_inc(&clnt->cl_count);
+}
+
+/**
+ * rpc_release_client - release a reference on an rpc_clnt
+ * @clnt: rpc_clnt to release
+ *
+ * Pairs with rpc_hold_client(). The rpc_clnt's resources are
+ * freed once its reference count drops to zero.
  */
 void
 rpc_release_client(struct rpc_clnt *clnt)
index 2e1fe601336151d50b936afbb5df1114ffb07551..359407aae03e6dc64c06ef5de80368f69b5ee41a 100644 (file)
@@ -2734,8 +2734,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
        lower_xprt = rcu_dereference(lower_clnt->cl_xprt);
        rcu_read_unlock();
 
-       if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE))
+       if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {
+               /* XPRT_LOCKED was never acquired. */
+               rpc_shutdown_client(lower_clnt);
                goto out_unlock;
+       }
 
        status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec);
        if (status) {
@@ -2758,6 +2761,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
 out_unlock:
        current_restore_flags(pflags, PF_MEMALLOC);
        upper_transport->clnt = NULL;
+       rpc_release_client(upper_clnt);
        xprt_unlock_connect(upper_xprt, upper_transport);
        return;
 
@@ -2805,7 +2809,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
        } else
                dprintk("RPC:       xs_connect scheduled xprt %p\n", xprt);
 
-       transport->clnt = task->tk_client;
+       /*
+        * Only the TLS connect_worker reads transport->clnt; pinning
+        * the upper rpc_clnt unconditionally would form a cycle with
+        * cl_xprt and prevent xprt destruction.
+        */
+       if (xprt->xprtsec.policy != RPC_XPRTSEC_NONE) {
+               rpc_hold_client(task->tk_client);
+               transport->clnt = task->tk_client;
+       }
        queue_delayed_work(xprtiod_workqueue,
                        &transport->connect_worker,
                        delay);
index 9324e4ed20a3eead461707df793d390482902b39..d4afc90fd7966ebf291c4962374295b784f9f7d0 100644 (file)
@@ -2115,6 +2115,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
                        goto read_sock_requeue;
                }
 
+               /* An empty data record (legal in TLS 1.3) gives a zero
+                * read_actor return, indistinguishable from the consumer
+                * stalling; the used <= 0 path would requeue it at the
+                * head of rx_list and block all later records. Consume it
+                * here instead.
+                */
+               if (rxm->full_len == 0) {
+                       consume_skb(skb);
+                       continue;
+               }
+
                used = read_actor(desc, skb, rxm->offset, rxm->full_len);
                if (used <= 0) {
                        if (!copied)
index 3dcf63b04c41db43a6e626f4ee8f9ceb1fee220e..610238d723fff798039e54ad4472c3077cd3596b 100644 (file)
@@ -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);
index df47ed6208a50de3c680c0302591b8b752fcbcc4..ac6ce9f967ec70221f15323e24c2640e71213dae 100644 (file)
@@ -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);
index 2a2c173058bacada2f36a05f88742b487e8f60c8..7824b7ac2770fbb180a3834202cd7d1d892c20d5 100644 (file)
@@ -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);
index 53b4b3f7669782ac222a642bed3610fc444b6d10..5adcb6bd0fc56db46d111cdac0ca7ccc57fa2d27 100644 (file)
@@ -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);
index c8447448f3a505d202d7109bc10fdf79ce936efb..34c3625f7fd5e51d34bb4c6ad6f9b0651ad231c6 100644 (file)
@@ -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);
index 05b7dc6b766ce94b5769c5488f02f01061a058e4..071083cc336725e2cdf01804783e35b8b6e1cee0 100644 (file)
@@ -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;
index b451df3096dd1eebbc8c91a4f170450e0ef1d6ed..2a719b5c487e39fa85da5526e2491ec2039f277b 100644 (file)
@@ -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) {
index 630f3dd31cc5cea094da1d4819547bfe503a1225..f153bf695b9dd19acbea3885de93ac2a8bf8d09e 100644 (file)
@@ -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)
index ad810d1f97c067d0680a1a4a6b7a038997aa206e..597aedeac26ebaae07b46ac49c11400e9b4e6cec 100644 (file)
@@ -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;
index 458931062a04ef6271ec8e7bf8d5a98c8bfd48c0..eb1b6f67739e1dc3e0ffdf67a7c94545335c149c 100644 (file)
@@ -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 {
index 7ef861a0e8231b63ece816b5237b03fa1367ccf9..932a313b9460a5ecc6ef449284f5336b94cb70d4 100644 (file)
@@ -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);
index c58cd024e3c6756e4a5363e2fb74aa27ae88e7ec..36a4f6793edef2296be49decb76d37f03fa7b5ae 100644 (file)
@@ -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;
index 6384795ee6b26995a31bfb212693d74a05b71b44..d6db63304ba6bd5bbd9c09c665170936e1107ced 100644 (file)
@@ -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));
index eb1143de8df17e4d7f9b16b9f68e3ecd92ddcc62..3785b0c7ffb17571d41567d0d898fd19e988272f 100644 (file)
@@ -120,6 +120,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
                addr.end = promote ? node1_end_addr : node0_end_addr;
        }
 
+       if (addr.start >= addr.end)
+               goto free_out;
+
        range.start = addr.start;
        range.end = addr.end;
 
index 68d6685c80bdb1680fd2f0ddcd653e5c402954cc..152ffc1a30b612795b94551e1c910f4242773d8a 100644 (file)
@@ -232,8 +232,8 @@ static int __init ftrace_ops_sample_init(void)
        ops_destroy(ops_irrelevant, nr_ops_irrelevant);
 
        /*
-        * The benchmark completed sucessfully, but there's no reason to keep
-        * the module around. Return an error do the user doesn't have to
+        * The benchmark completed successfully, but there's no reason to keep
+        * the module around. Return an error so the user doesn't have to
         * manually unload the module.
         */
        return -EINVAL;
index cbff59ec3abaa0ed267fe5ea5560453e7201c3bf..46c17116fcf4287f7c82c9276b392f1f1f3fa109 100644 (file)
@@ -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
index 61f3c253d5c90dcc4a88fec953b3fded403c97b6..0437adf1742876ca7405880d498e171f0855ce97 100644 (file)
@@ -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;
index 7ddf211f75c3771ae8e6ef98d0fc77c59292dca1..360d226d0f51d3cd14334b0663b5dbbdce1360ab 100644 (file)
@@ -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);
index 1a713d96206f59b547af72ea5fa1f0c2055f84c4..8d6945edae7aacf34fd48a1876469305160c1a93 100644 (file)
@@ -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;
        }
@@ -4994,9 +5000,8 @@ static int selinux_socket_socketpair(struct socket *socka,
    Need to determine whether we should perform a name_bind
    permission check between the socket and the port number. */
 
-static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+static int __selinux_socket_bind(struct sock *sk, struct sockaddr *address, int addrlen)
 {
-       struct sock *sk = sock->sk;
        struct sk_security_struct *sksec = selinux_sock(sk);
        u16 family;
        int err;
@@ -5126,13 +5131,17 @@ err_af:
        return -EAFNOSUPPORT;
 }
 
+static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+       return __selinux_socket_bind(sock->sk, address, addrlen);
+}
+
 /* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
  * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst
  */
-static int selinux_socket_connect_helper(struct socket *sock,
+static int selinux_socket_connect_helper(struct sock *sk,
                                         struct sockaddr *address, int addrlen)
 {
-       struct sock *sk = sock->sk;
        struct sk_security_struct *sksec = selinux_sock(sk);
        int err;
 
@@ -5221,7 +5230,7 @@ static int selinux_socket_connect(struct socket *sock,
        int err;
        struct sock *sk = sock->sk;
 
-       err = selinux_socket_connect_helper(sock, address, addrlen);
+       err = selinux_socket_connect_helper(sk, address, addrlen);
        if (err)
                return err;
 
@@ -5262,7 +5271,24 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
 static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                  int size)
 {
-       return sock_has_perm(sock->sk, SOCKET__WRITE);
+       int rc;
+       struct sockaddr *const addr = msg->msg_name;
+       const int addrlen = msg->msg_namelen;
+
+       rc = sock_has_perm(sock->sk, SOCKET__WRITE);
+       if (rc)
+               return rc;
+
+       if (addr && (msg->msg_flags & MSG_FASTOPEN) &&
+           (sk_is_tcp(sock->sk) ||
+            (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM &&
+             sock->sk->sk_protocol == IPPROTO_MPTCP))) {
+               rc = selinux_socket_connect(sock, addr, addrlen);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
 }
 
 static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
@@ -5706,13 +5732,11 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
        int len, err = 0, walk_size = 0;
        void *addr_buf;
        struct sockaddr *addr;
-       struct socket *sock;
 
        if (!selinux_policycap_extsockclass())
                return 0;
 
        /* Process one or more addresses that may be IPv4 or IPv6 */
-       sock = sk->sk_socket;
        addr_buf = address;
 
        while (walk_size < addrlen) {
@@ -5741,14 +5765,14 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
                case SCTP_PRIMARY_ADDR:
                case SCTP_SET_PEER_PRIMARY_ADDR:
                case SCTP_SOCKOPT_BINDX_ADD:
-                       err = selinux_socket_bind(sock, addr, len);
+                       err = __selinux_socket_bind(sk, addr, len);
                        break;
                /* Connect checks */
                case SCTP_SOCKOPT_CONNECTX:
                case SCTP_PARAM_SET_PRIMARY:
                case SCTP_PARAM_ADD_IP:
                case SCTP_SENDMSG_CONNECT:
-                       err = selinux_socket_connect_helper(sock, addr, len);
+                       err = selinux_socket_connect_helper(sk, addr, len);
                        if (err)
                                return err;
 
index 4d73ecb30d79bc13ac9dacd0f4e1c16a23628f9e..41e019b9e24e5f6d86e30f5c5a7e4932dcfc01d6 100644 (file)
@@ -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>
index 7744ea6a0791d3256da8e3392dd2b9be61b1d76b..5abae26d2bba2150e7fc459418485b3b3aea6420 100644 (file)
@@ -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>
index 82b647d383c5cc3c1d850d8a1cec09e647eb906d..60d17a6fddd1b053889fa527a814e4265007ffa7 100644 (file)
@@ -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>
index 7e42f5778a8a3767ef75318947df8ee52da2f070..c9c35c67db27db9af26594f7aa87a2f9141d920f 100644 (file)
@@ -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>
index c8d5879efe2865927a425d7a575fdac7209bc629..a8aadf754e72f825b66575832983d02d6cbd382a 100644 (file)
@@ -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>
index c66be0a89ccf174fcf298e8eacd21c084707d42b..7333a76b50aa18f0ebf699e8d7ff3cd5e6684bef 100644 (file)
@@ -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>
index 39ea9a6dde33a5c5a72a26741bc65c0330f9a001..aeb83e1595b71ed0f4e96f6514a278ab409aa1c2 100644 (file)
@@ -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>
index d07ffcb27be6ead9b6a4307d870e6a1866458487..9c42c9b48f214a5d1ac33e33c7515d4255973bb5 100644 (file)
@@ -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>
index 3d92262763f62c4f15aa1df99fbf3a23b3f0c41b..40da2832ba6648556368281b833641e86ea0c7fb 100644 (file)
@@ -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.
         */
index f7700713dc628381c4d6f38d3b0e8d842c0fbb55..443bc92c5e4b0985215a9d10dcba99da9c3a18ba 100644 (file)
@@ -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.
index 2a2e8804bf9e48f8edd2295e0057bf179a31570a..1cfd83e251e4b898c2fd888151ce3712355dc2b8 100644 (file)
@@ -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
index a0ea08eb96a93f209fc4d450e504bc6c13c8de14..78c2cf387a001fdcfebeb4b9ffd46885aaae37cc 100644 (file)
@@ -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);
index e7b866fc52c1575b1d5ab26c855901117d624816..d6eb17aa9e0860acccf14f5e2afb7e99b2a83142 100644 (file)
@@ -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);
 }
index 977e4f2a7a7015a7bac53c812380ab4674172ade..a04c301df4d616bc92ff23eefa9f622d29a35b2e 100644 (file)
@@ -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
 
index 81d23aded348d15c89900633810e27893df8b35f..ab5f9dc871a6776c9a26ba3184702711c31ba520 100644 (file)
@@ -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
index 18f2918d4ada4de8b81d1cc6128d78b50b4388af..ccd01152c87d0f2372a64fe4e427e842875d1b26 100644 (file)
@@ -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,
@@ -659,6 +699,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,
@@ -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
index 63bf9e3c0ae18071b60ebccb0ba44591ed890cbc..9248b6d09e76fe43e1407eedbbb9a19c52b86653 100644 (file)
@@ -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");
index 4ecda224157b5f58ab2a4474e6ad1fe980a000e8..729f9aaba69e76a87a4426e01ccaa4ee5cd62af9 100644 (file)
@@ -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);
 }
 
index 934666295ee307cfc8991996f5fadb647bf808b2..dbf45cabfffe3b9876e552bb73af1cba9689df82 100644 (file)
@@ -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,
index 4796fce084ff46e601cf80b43d90ca0a3104f4d8..d6353af07380c19fb897148fbd9c3e196b7de0ef 100644 (file)
@@ -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;
index 2f7cfc2be970b513bc9064fea0a022bd7dde35f3..e1d62f30418ac2b9e08f0547a133edd92cddbf50 100644 (file)
@@ -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)
index d43daf9b025dffd5714e98c66c3740039f3bf14f..59eb0328bbb526b7319585d815568c0e25a6d40c 100644 (file)
@@ -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),
        {}
 };
 
index e0e00ec026dcc478c014d7771f9aead9fc003f0b..a9861c5d663745e54657a20e613e6de262f9c3f9 100644 (file)
@@ -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:
index 7925aa3f63ba038a81ee8b767ffc6a198ccc1d80..98b15a527e37e1b41ca59d68ba5aa21361dc12d0 100644 (file)
@@ -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"},
index b20aae0caf60a4be06a5109d243d030a5c248290..a16e5924848033ba872465f2bc0774b8e195246b 100644 (file)
@@ -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;
                }
index eabbf41fdfb2b21874542a75a2c49ef312054fcf..8d924330c54c884878cc556888ffebcdfb4cae9a 100644 (file)
@@ -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);
index b4c855c25eef41e07f7d7e55d25fd0051e510839..703c118f9d4eda964bf817593c6e2543ad199c75 100644 (file)
@@ -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++;
index 1cb588691e16df2c3de9ab2f1263f6a584ca9ad0..41149561aa0669eb865276345e4ed6c3d622b5bb 100644 (file)
@@ -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 */
index 3427d788032641cd2af3cedcbbf9cd8316c9f301..98963688143f3c77c25735b1da32e6adf5ffa9d2 100644 (file)
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 OR MIT */
+#include <stdint.h>
 #ifndef __LINUX_OVERFLOW_H
 #define __LINUX_OVERFLOW_H
 
index db247e42fb45fa359cc5b5ed3f298dc456ccf836..6b0b4e41b288f90eae69e195d26a5c18a1012de9 100644 (file)
@@ -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
@@ -488,16 +460,66 @@ static __always_inline u32 cmask_first_set(const struct scx_cmask __arena *m)
             (cid) < (m)->base + (m)->nr_cids;                                  \
             (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;
index d4f845c3280456e80c1357ca79a0c2085e6d742b..a329f901c5edf4f1d4c4c010fbf550543e4dc3a9 100644 (file)
@@ -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;
                }
 
index 72e82bfbecc99e34da71d56cf5401c137d6e9a48..ddb0b2b201554980e5b793043b0a405fe7df046e 100644 (file)
@@ -130,7 +130,7 @@ TEST(gcs_find_terminator)
  * We can access a GCS via ptrace
  *
  * This could usefully have a fixture but note that each test is
- * fork()ed into a new child whcih causes issues.  Might be better to
+ * fork()ed into a new child which causes issues.  Might be better to
  * lift at least some of this out into a separate, non-harness, test
  * program.
  */
index 67d138057707081d0e8f8231225681f6d84f7c75..f4b859c75a5abe2b7bcbdf235a934beef03942f7 100644 (file)
@@ -292,7 +292,7 @@ TEST(single_thread_different_keys)
 
 /*
  * fork() does not change keys. Only exec() does so call a worker program.
- * Its only job is to sign a value and report back the resutls
+ * Its only job is to sign a value and report back the results
  */
 TEST(exec_changed_keys)
 {
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 (file)
index 0000000..61cc5aa
--- /dev/null
@@ -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 (file)
index 0000000..b8538fc
--- /dev/null
@@ -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 (file)
index 216b0df..0000000
+++ /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);
-}
index cb3229711f93a36ab10652eda20617f5ecea4063..e5fc038d747b6f5082eac568a456602a46eda6d1 100644 (file)
@@ -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"))
index cc0c68bab907964401da88ba680c6d745952d799..1c96a3cf4b979223d4c6cb78502cbe2b35641af9 100644 (file)
@@ -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),
index 8a3d69e2453c3a0ddc2d0d523f26dac48fb09b40..be97f6887f0e7fa41048fcf7d16039d9f04b0484 100644 (file)
@@ -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 (file)
index 0000000..995f8b8
--- /dev/null
@@ -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 (file)
index 0000000..12cf24d
--- /dev/null
@@ -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";
index 14a0172e2141e8f3d0155f3e8f595d076f664ffe..4055a6443bc20a29a8af7b9fe052da201e8dbe40 100644 (file)
@@ -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";
index c32da7bd8be2761580c63493c45e0476bffd317e..6a2641ee7897547aca3b9d18ea7efd326727f15e 100644 (file)
@@ -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++) {
index cb59cf436dd0a327260ee684f125864e06bb2433..d9111f2102bcd1df524e0a95a8606695cb958614 100755 (executable)
@@ -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
index 2915206777b6c358d8b300e45d9b4ed8bd7b670d..89660a9adf445dda78fd780a90d6a6c7737063fb 100644 (file)
@@ -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
index 8d275e3238d9f24159db4657587a60acea3c1e5e..04eb8546fc07fc81e17b4a6447ee2cb3c696e2a7 100644 (file)
@@ -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.
index ededb077a3a69c5335d2d4785a7cb6dc5951ba9d..16f74de479f17677083df7153f9d7b3dff19cc25 100644 (file)
@@ -2,3 +2,4 @@
 gpio-mockup-cdev
 gpio-chip-info
 gpio-line-name
+gpio-cdev-uaf
index 96071b4800e82a8f434c0ad1f91d88e0cfc77081..2f423de831473a4a3370a1a49a61f86290a97758 100644 (file)
@@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++
 
 HOSTPKG_CONFIG := pkg-config
 
-CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT)
+CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES)
 CFLAGS += -I$(OUTPUT)/tools/include
 
 LDLIBS += -lelf -lz -lrt -lpthread
index 1e979fb3542bab79803ae2c772e048d93927df33..b851339308c214f71e3ed26c017bb90563d2f2e1 100644 (file)
@@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[],
        self->skel = hid__open();
        ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open");
 
+       /*
+        * Disable all struct_ops maps by default so libbpf does not autoload
+        * programs referenced by maps that are unrelated to the current test.
+        */
+       bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) {
+               if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) {
+                       err = bpf_map__set_autocreate(iter_map, false);
+                       ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'",
+                                             bpf_map__name(iter_map));
+               }
+
+               bpf_map__set_autoattach(iter_map, false);
+       }
+
        for (int i = 0; i < progs_count; i++) {
                struct bpf_program *prog;
                struct bpf_map *map;
@@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[],
                ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
                                          programs[i].name + 4);
 
+               err = bpf_map__set_autocreate(map, true);
+               ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'",
+                                     programs[i].name + 4);
+
                /* hid_id is the first field of struct hid_bpf_ops */
                ops_hid_id = bpf_map__initial_value(map, NULL);
                ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
@@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[],
                *ops_hid_id = self->hid.hid_id;
        }
 
-       /* we disable the auto-attach feature of all maps because we
-        * only want the tested one to be manually attached in the next
-        * call to bpf_map__attach_struct_ops()
-        */
-       bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj)
-               bpf_map__set_autoattach(iter_map, false);
-
        err = hid__load(self->skel);
        ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
 
@@ -887,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup)
        ASSERT_EQ(rpt_desc.value[4], 0x42);
 }
 
+TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
+{
+       const struct test_program progs[] = {
+               { .name = "hid_rdesc_fixup_get_data_overflow" },
+       };
+
+       LOAD_PROGRAMS(progs);
+
+       ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
+}
+
 static int libbpf_print_fn(enum libbpf_print_level level,
                           const char *format, va_list args)
 {
index 5ecc845ef79216d42e141e42167289109ea79267..b21fbb13c926f82585fb7ff4f6dfc93025c1fe9e 100644 (file)
@@ -13,6 +13,7 @@ struct attach_prog_args {
 
 __u64 callback_check = 52;
 __u64 callback2_check = 52;
+__u64 get_data_overflow_check;
 
 SEC("?struct_ops/hid_device_event")
 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
@@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = {
        .hid_rdesc_fixup = (void *)hid_rdesc_fixup,
 };
 
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx)
+{
+       if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */))
+               get_data_overflow_check = 1;
+
+       return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
+       .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
+};
+
 SEC("?struct_ops/hid_device_event")
 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
 {
index fa4fb2054bd4febb1d2497f2787944f538b27889..7897340118b4311d553f7eafab2c86ec25e84e19 100644 (file)
@@ -513,6 +513,79 @@ class SmartTechDigitizer(Digitizer):
         return absinfo is not None and absinfo.resolution == 3
 
 
+class MinWin8TSParallelBigContactMax(Digitizer):
+    """A parallel Win8 touchscreen that advertises a ContactCountMaximum much
+    larger than the number of contacts it actually reports.
+
+    Such firmware makes the driver allocate that many input slots (up to 255)
+    while the input report only carries a few contacts. This is what used to
+    drive the per-slot bit operations on mt_io_flags out of bounds. The number
+    of contacts a HID report can describe is limited by the descriptor size,
+    so a large ContactCountMaximum can only be expressed this way, decoupled
+    from the number of finger collections."""
+
+    def __init__(self, n_fingers=5, contact_max=250):
+        self.phys_max = 120, 90
+        rdesc_finger_str = f"""
+            Usage Page (Digitizers)
+            Usage (Finger)
+            Collection (Logical)
+             Report Size (1)
+             Report Count (1)
+             Logical Minimum (0)
+             Logical Maximum (1)
+             Usage (Tip Switch)
+             Input (Data,Var,Abs)
+             Report Size (7)
+             Logical Maximum (127)
+             Input (Cnst,Var,Abs)
+             Report Size (8)
+             Logical Maximum (255)
+             Usage (Contact Id)
+             Input (Data,Var,Abs)
+             Report Size (16)
+             Unit Exponent (-1)
+             Unit (SILinear: cm)
+             Logical Maximum (4095)
+             Physical Minimum (0)
+             Physical Maximum ({self.phys_max[0]})
+             Usage Page (Generic Desktop)
+             Usage (X)
+             Input (Data,Var,Abs)
+             Physical Maximum ({self.phys_max[1]})
+             Usage (Y)
+             Input (Data,Var,Abs)
+            End Collection
+"""
+        rdesc_str = f"""
+           Usage Page (Digitizers)
+           Usage (Touch Screen)
+           Collection (Application)
+            Report ID (1)
+            {rdesc_finger_str * n_fingers}
+            Unit Exponent (-4)
+            Unit (SILinear: s)
+            Logical Maximum (65535)
+            Physical Maximum (65535)
+            Usage Page (Digitizers)
+            Usage (Scan Time)
+            Input (Data,Var,Abs)
+            Report Size (8)
+            Logical Maximum (255)
+            Usage (Contact Count)
+            Input (Data,Var,Abs)
+            Report ID (2)
+            Logical Maximum ({contact_max})
+            Usage (Contact Max)
+            Feature (Data,Var,Abs)
+          End Collection
+          {Digitizer.msCertificationBlob(68)}
+"""
+        super().__init__(
+            f"uhid test parallel big contact max {contact_max}", rdesc_str
+        )
+
+
 class BaseTest:
     class TestMultitouch(base.BaseTestCase.TestUhid):
         kernel_modules = [KERNEL_MODULE]
@@ -1735,6 +1808,47 @@ class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch):
         return MinWin8TSParallel(10)
 
 
+class TestMinWin8TSParallelBigContactMax(base.BaseTestCase.TestUhid):
+    """Regression test for the out-of-bounds bit operations on
+    struct mt_device.mt_io_flags.
+
+    A Win8 touchscreen may advertise a ContactCountMaximum much larger than
+    the number of contacts it reports. The driver used to keep the per-slot
+    active state in the bits of a single unsigned long while indexing
+    set_bit()/clear_bit() by the slot number, so such a device drove those bit
+    operations out of bounds. The sticky-fingers release timer made it fatal:
+    mt_release_contacts() cleared one bit per slot, overwrote the adjacent
+    struct mt_device members and panicked the kernel.
+
+    Send a single contact, let the 100ms sticky-fingers timer release it, and
+    check that the kernel reports the release cleanly instead of crashing."""
+
+    kernel_modules = [KERNEL_MODULE]
+
+    def create_device(self):
+        return MinWin8TSParallelBigContactMax()
+
+    def test_sticky_fingers_release_big_contact_max(self):
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+
+        assert evdev.num_slots == uhdev.max_contacts
+
+        t0 = Touch(1, 5, 10)
+        r = uhdev.event([t0])
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+
+        # do not release the contact; the sticky-fingers timer must do it
+        # after 100ms, which is where the out-of-bounds release used to hit
+        time.sleep(0.2)
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
+        assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+
 class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch):
     def create_device(self):
         return MinWin8TSHybrid()
index d28a057fa6c2d602ed02997d5b094081e0ecd926..6fc34e9bf8e1b4831eb28677d9c3e1ddde9c8a1d 100644 (file)
@@ -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 (file)
index 0000000..25196f1
--- /dev/null
@@ -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();
+}
index 8db88c355f1654525822700657b492ad025e2580..689390c10f7c37a5905085862247c3eb868d6960 100644 (file)
@@ -130,12 +130,18 @@ int main(int argc, char *argv[])
                            KVM_X86_SEV_VMSA_FEATURES,
                            &supported_vmsa_features);
 
-       have_sev = kvm_cpu_has(X86_FEATURE_SEV);
-       TEST_ASSERT(have_sev == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)),
-                   "sev: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)",
-                   kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM);
+       /*
+        * Whether a VM type is available depends on KVM, not just CPUID: e.g.
+        * when all SEV ASIDs are assigned to SEV-SNP, KVM does not offer the
+        * SEV VM type even though X86_FEATURE_SEV is set.  Derive availability
+        * from KVM_CAP_VM_TYPES and only assert the one-way implication that a
+        * type offered by KVM must also be reported in CPUID.
+        */
+       have_sev = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM);
+       TEST_ASSERT(!have_sev || kvm_cpu_has(X86_FEATURE_SEV),
+                   "sev: SEV_VM supported without SEV in CPUID");
 
-       TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM));
+       TEST_REQUIRE(have_sev);
        have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM);
 
        TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES),
index 42bc023d51937b853a3b4cde6e1e95eb80994467..d59abb198d86f09b95c5bb398cacd388d25002fe 100644 (file)
@@ -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;
 }
index 6b2cbe2a90b7c559857f9c9b6d8fca9db79bbc2e..bf27b6187afac1ac17b920aa662fb8e709817302 100644 (file)
@@ -247,7 +247,14 @@ int main(int argc, char *argv[])
 {
        TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));
 
-       test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);
+       /*
+        * Only exercise VM types the host actually offers.  CPUID reporting
+        * SEV does not guarantee KVM offers the SEV VM type: when all SEV
+        * ASIDs are assigned to SEV-SNP, KVM_X86_SEV_VM is unavailable even
+        * though X86_FEATURE_SEV is set.  Gate every type on KVM_CAP_VM_TYPES.
+        */
+       if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM))
+               test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);
 
        if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM))
                test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES);
index 2ed1f76b7a8b15eef16ca9c9760d11d9485d975c..be2eb88092fb34fa7179e1c09fde79b6f012c1f5 100644 (file)
@@ -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;
index f24f2c28f62e504bfecd3036e5575583f0525e03..2d37d0c06c069f25d44510bb1c12323821d7db4d 100644 (file)
@@ -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. */
index e4c49699f3f728aadaa935be6df2fc0d9fc482ac..2f2b9879d100514086a2c6aae3c9992c87ed096a 100644 (file)
@@ -23,6 +23,7 @@
 #include <time.h>
 #include <pthread.h>
 #include <limits.h>
+#include <linux/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
index 2c3137ae8bc8555a8fbdfeea3df9f7097ef15c0c..edad2d2d888f2927d504dfdfdbc52d0d213d6c09 100755 (executable)
@@ -1,4 +1,4 @@
 #!/bin/sh -e
 # SPDX-License-Identifier: GPL-2.0
 
-./run_vmtests.sh -t mmap
+./run_vmtests.sh -t process_madv
index 762306177ad813eeabf0bc6d71faf76d34cf464d..6f8971d5b3ce436d26324ccceecb2ed0e235f97c 100644 (file)
@@ -1368,7 +1368,7 @@ void *thread_proc(void *mem)
                        ksft_exit_fail_msg("pthread_barrier_wait\n");
 
                for (i = 0; i < access_per_thread; ++i)
-                       __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST);
+                       __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST);
 
                ret = pthread_barrier_wait(&end_barrier);
                if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD)
@@ -1403,15 +1403,15 @@ static void transact_test(int page_size)
        if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1))
                ksft_exit_fail_msg("pthread_barrier_init\n");
 
-       mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE,
+       mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE,
                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
        if (mem == MAP_FAILED)
                ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno));
 
-       wp_init(mem, 0x1000 * nthreads * pages_per_thread);
-       wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread);
+       wp_init(mem, page_size * nthreads * pages_per_thread);
+       wp_addr_range(mem, page_size * nthreads * pages_per_thread);
 
-       memset(mem, 0, 0x1000 * nthreads * pages_per_thread);
+       memset(mem, 0, page_size * nthreads * pages_per_thread);
 
        count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size);
        ksft_test_result(count > 0, "%s count %u\n", __func__, count);
@@ -1420,7 +1420,7 @@ static void transact_test(int page_size)
 
        finish = 0;
        for (i = 0; i < nthreads; ++i)
-               pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread);
+               pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread);
 
        extra_pages = 0;
        for (i = 0; i < iter_count; ++i) {
index b3827b43782b0441f296186f54520e5a70f9a696..d46d2cec89e450355e2b56c6130b182e29db2a34 100644 (file)
@@ -70,12 +70,33 @@ ksft_exit_status_merge()
                $ksft_xfail $ksft_pass $ksft_skip $ksft_fail
 }
 
+timestamp_ms()
+{
+       local now
+       local seconds
+       local nanoseconds
+
+       now=$(date -u +%s:%N) || return
+       seconds=${now%:*}
+       nanoseconds=${now#*:}
+
+       if [[ $nanoseconds =~ ^[0-9]+$ ]]; then
+               nanoseconds=${nanoseconds:0:9}
+       else
+               nanoseconds=0
+       fi
+
+       echo $((seconds * 1000 + 10#$nanoseconds / 1000000))
+}
+
 loopy_wait()
 {
        local sleep_cmd=$1; shift
        local timeout_ms=$1; shift
+       local start_time
+       local current_time
 
-       local start_time="$(date -u +%s%3N)"
+       start_time=$(timestamp_ms) || return
        while true
        do
                local out
@@ -84,7 +105,7 @@ loopy_wait()
                        return 0
                fi
 
-               local current_time="$(date -u +%s%3N)"
+               current_time=$(timestamp_ms) || return
                if ((current_time - start_time > timeout_ms)); then
                        echo -n "$out"
                        return 1
index 08ad07500e8a7156eeec55c7bf0fc7a7076f5b73..fb1c59d45567aff498ea283742906f72a6cf4acc 100755 (executable)
@@ -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.
index 4fcce5150850dd38739ca15272d1f307b9f29cba..2544ae35d07a839a3a503c0bb58a084b3527bdf7 100644 (file)
@@ -313,6 +313,8 @@ end:
                                tcp_info_get_rcv_mss(fd));
        }
 error:
+       if (ctx)
+               EVP_MD_CTX_free(ctx);
        munmap(buffer, buffer_sz);
        close(fd);
        if (zflg)
@@ -606,6 +608,8 @@ int main(int argc, char *argv[])
                EVP_DigestFinal_ex(ctx, digest, &digest_len);
                send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0);
        }
+       if (ctx)
+               EVP_MD_CTX_free(ctx);
        close(fd);
        munmap(buffer, buffer_sz);
        return 0;
index 74b6f6bcf0676170bea8c05bfdb611b02689632d..a388b7963d4771b594f96f35bc540947ac8ae73b 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <sys/ptrace.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/uio.h>
@@ -25,9 +26,9 @@ TEST(ptrace_v_not_enabled)
                SKIP(return, "Vector not supported");
 
        chld_lock = 1;
-       pid = fork();
+       pid = (pid_t)syscall(SYS_clone, SIGCHLD, 0, NULL, 0, NULL);
        ASSERT_LE(0, pid)
-               TH_LOG("fork: %m");
+               TH_LOG("clone: %m");
 
        if (pid == 0) {
                while (chld_lock == 1)
@@ -74,7 +75,7 @@ TEST(ptrace_v_not_enabled)
                ASSERT_EQ(-1, ret);
 
                /* cleanup */
-
+               free(regset_data);
                ASSERT_EQ(0, kill(pid, SIGKILL));
        }
 }
@@ -206,7 +207,7 @@ TEST(ptrace_v_early_debug)
                EXPECT_EQ(vl_csr, regset_data->vl);
 
                /* cleanup */
-
+               free(regset_data);
                ASSERT_EQ(0, kill(pid, SIGKILL));
        }
 }
@@ -330,7 +331,7 @@ TEST(ptrace_v_syscall_clobbering)
                EXPECT_EQ(0UL, regset_data->vl);
 
                /* cleanup */
-
+               free(regset_data);
                ASSERT_EQ(0, kill(pid, SIGKILL));
        }
 }
@@ -648,7 +649,7 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values)
                ASSERT_EQ(ret, -1);
 
                /* cleanup */
-
+               free(regset_data);
                ASSERT_EQ(0, kill(pid, SIGKILL));
        }
 }
@@ -910,7 +911,7 @@ TEST_F(v_csr_valid, ptrace_v_valid_values)
                EXPECT_EQ(regset_data->vlenb, vlenb);
 
                /* cleanup */
-
+               free(regset_data);
                ASSERT_EQ(0, kill(pid, SIGKILL));
        }
 }
index 50d69e22ee7a67880eabc64bea1736467a791aa3..aba6317f6cb8e5e5974bcfa8853d447a460025bf 100644 (file)
@@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as
 endif
 
 top_srcdir = ../../../..
+include $(top_srcdir)/scripts/subarch.include
+ARCH ?= $(SUBARCH)
+LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
 
 CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \
-         $(CLANG_FLAGS) -I$(top_srcdir)/tools/include
+         $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \
+         -I$(LINUX_TOOL_ARCH_INCLUDE)
 LDLIBS += -lpthread -ldl
 
 # Own dependencies because we only want to build against 1st prerequisite, but
index 5d2dffca0e918ef2199ed9dc15440743f71a9aad..3cfe90e0f34fa264a0c3b89118d8a3f4183f4659 100644 (file)
@@ -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 (file)
index 0000000..6998c5d
--- /dev/null
@@ -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 (file)
index 0000000..028f543
--- /dev/null
@@ -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)
index 85892b3b719ccde0768c1095d1f39997a9cb5470..b71813eaf5c04879f727bef9618d2adfb49ec279 100644 (file)
@@ -132,6 +132,33 @@ static int event_delete(void)
        return ret;
 }
 
+/*
+ * Deleting an event drops its last reference, but an unregister may defer
+ * that put (and the freeing of the associated enabler) past an RCU grace
+ * period. The delete can therefore transiently fail with -EBUSY while the
+ * previous reference is still being dropped. Retry only on that transient
+ * failure; treat an already-deleted event (-ENOENT) as success and return
+ * any other error immediately rather than spinning for the full timeout.
+ */
+static int wait_for_event_delete(void)
+{
+       int i, ret;
+
+       for (i = 0; i < 10000; ++i) {
+               ret = event_delete();
+
+               if (ret == 0 || errno == ENOENT)
+                       return 0;
+
+               if (errno != EBUSY)
+                       return ret;
+
+               usleep(1000);
+       }
+
+       return ret;
+}
+
 static int reg_enable_multi(void *enable, int size, int bit, int flags,
                            char *args)
 {
@@ -262,7 +289,7 @@ TEST_F(user, flags) {
        ASSERT_TRUE(event_exists());
 
        /* Ensure we can delete it */
-       ASSERT_EQ(0, event_delete());
+       ASSERT_EQ(0, wait_for_event_delete());
 
        /* USER_EVENT_REG_MAX or above is not allowed */
        ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
index cafec0e52eb31eff048645ee0a1b4aad1e144570..5727cb5b914cf583937029663db93853eb0c5532 100644 (file)
@@ -85,6 +85,7 @@ static int get_offset(void)
 static int clear(int *check)
 {
        struct user_unreg unreg = {0};
+       int i, ret;
 
        unreg.size = sizeof(unreg);
        unreg.disable_bit = 31;
@@ -99,13 +100,32 @@ static int clear(int *check)
                if (errno != ENOENT)
                        return -1;
 
-       if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
-               if (errno != ENOENT)
+       /*
+        * Deleting the event drops its last reference, but the unregister
+        * above defers that put (and the freeing of the enabler) past an RCU
+        * grace period. The delete can therefore transiently fail with -EBUSY
+        * until that reference is dropped. Retry for up to ~10 seconds so the
+        * event is actually gone before the next test registers the same name.
+        */
+       for (i = 0; i < 10000; ++i) {
+               ret = ioctl(fd, DIAG_IOCSDEL, "__test_event");
+
+               if (ret == 0 || errno == ENOENT) {
+                       ret = 0;
+                       break;
+               }
+
+               if (errno != EBUSY) {
+                       close(fd);
                        return -1;
+               }
+
+               usleep(1000);
+       }
 
        close(fd);
 
-       return 0;
+       return ret;
 }
 
 FIXTURE(user) {
index 60a1025389881cf1a459a0f2400019df567aa0ce..387bc6cc18f02506c29427332f3f4ccf1eb24558 100644 (file)
@@ -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)
index d0a8a6edbf0cba3569215106541071b40ecbeb97..8c7f5e75b2ec8ddd5ccb92390f6315bcc2ae2ba9 100644 (file)
@@ -5,6 +5,7 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <sys/sysinfo.h>
 
 #include "common.h"
index b8d7d480595a8477db79c090cd46ee2f671a4e56..f4734f552d314e0c4f1a8876af88a6b027c210fd 100644 (file)
@@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = {
        { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
        { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
        { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+       { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" },
+       { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" },
        { 0, NULL, NULL }
 };
 
index 1dfbb76ab26c3d43e19953f407498e91a02944f9..c9b3619d86f31ecbfa9952a5a73c054a606ea327 100644 (file)
@@ -57,6 +57,10 @@ static struct {
                .speed = USB_SPEED_SUPER,
                .name = "super-speed",
        },
+       {
+               .speed = USB_SPEED_SUPER_PLUS,
+               .name = "super-speed-plus",
+       },
 };
 
 static
index 8159fd98680b4d729ce69b58335a3834855babda..4ca3783ee5b7c15abaa0bea55500debffbe4322e 100644 (file)
@@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed)
 
                switch (speed) {
                case    USB_SPEED_SUPER:
+               case    USB_SPEED_SUPER_PLUS:
                        if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
                                continue;
                break;
diff --git a/tools/virtio/asm/percpu_types.h b/tools/virtio/asm/percpu_types.h
new file mode 100644 (file)
index 0000000..4eb53d9
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_PERCPU_TYPES_H
+#define _ASM_PERCPU_TYPES_H
+
+#define __percpu_qual
+
+#endif /* _ASM_PERCPU_TYPES_H */
diff --git a/tools/virtio/linux/completion.h b/tools/virtio/linux/completion.h
new file mode 100644 (file)
index 0000000..5e54b67
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_COMPLETION_H
+#define _LINUX_COMPLETION_H
+
+struct completion {
+       unsigned int done;
+};
+
+#endif /* _LINUX_COMPLETION_H */
index 075c2140d975a91294cb0a3809175b893c04a138..abf100cb0023a977d53f400f089d8924b69273a6 100644 (file)
@@ -1,4 +1,5 @@
 #ifndef LINUX_DEVICE_H
+#define LINUX_DEVICE_H
 
 struct device {
        void *parent;
index 8d1a16cb20db40799461c557a44a4b1a25580bb4..b9fc5e8338e36166899de1bce7827d1f3df58665 100644 (file)
@@ -61,5 +61,6 @@ enum dma_data_direction {
 #define DMA_MAPPING_ERROR              (~(dma_addr_t)0)
 
 #define DMA_ATTR_CPU_CACHE_CLEAN       (1UL << 11)
+#define DMA_ATTR_DEBUGGING_IGNORE_CACHELINES   0
 
 #endif
diff --git a/tools/virtio/linux/mod_devicetable.h b/tools/virtio/linux/mod_devicetable.h
new file mode 100644 (file)
index 0000000..3ba594b
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MOD_DEVICETABLE_H
+#define _LINUX_MOD_DEVICETABLE_H
+
+#include <linux/types.h>
+
+struct virtio_device_id {
+       __u32 device;
+       __u32 vendor;
+};
+
+#define VIRTIO_DEV_ANY_ID      0xffffffff
+
+#endif /* _LINUX_MOD_DEVICETABLE_H */
diff --git a/tools/virtio/linux/virtio_features.h b/tools/virtio/linux/virtio_features.h
new file mode 100644 (file)
index 0000000..04cbb96
--- /dev/null
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VIRTIO_FEATURES_H
+#define _LINUX_VIRTIO_FEATURES_H
+
+#include <linux/bug.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define VIRTIO_FEATURES_U64S   2
+#define VIRTIO_FEATURES_BITS   (VIRTIO_FEATURES_U64S * 64)
+
+#define VIRTIO_BIT(b)          (1ULL << ((b) & 0x3f))
+#define VIRTIO_U64(b)          ((b) >> 6)
+
+#define VIRTIO_DECLARE_FEATURES(name)                  \
+       union {                                         \
+               u64 name;                                       \
+               u64 name##_array[VIRTIO_FEATURES_U64S];\
+       }
+
+static inline bool virtio_features_chk_bit(unsigned int bit)
+{
+       return bit < VIRTIO_FEATURES_BITS;
+}
+
+static inline bool virtio_features_test_bit(const u64 *features,
+                                           unsigned int bit)
+{
+       return virtio_features_chk_bit(bit) &&
+              !!(features[VIRTIO_U64(bit)] & VIRTIO_BIT(bit));
+}
+
+static inline void virtio_features_set_bit(u64 *features, unsigned int bit)
+{
+       if (virtio_features_chk_bit(bit))
+               features[VIRTIO_U64(bit)] |= VIRTIO_BIT(bit);
+}
+
+static inline void virtio_features_clear_bit(u64 *features, unsigned int bit)
+{
+       if (virtio_features_chk_bit(bit))
+               features[VIRTIO_U64(bit)] &= ~VIRTIO_BIT(bit);
+}
+
+static inline void virtio_features_zero(u64 *features)
+{
+       memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_U64S);
+}
+
+static inline void virtio_features_from_u64(u64 *features, u64 from)
+{
+       virtio_features_zero(features);
+       features[0] = from;
+}
+
+static inline bool virtio_features_equal(const u64 *f1, const u64 *f2)
+{
+       int i;
+
+       for (i = 0; i < VIRTIO_FEATURES_U64S; ++i)
+               if (f1[i] != f2[i])
+                       return false;
+       return true;
+}
+
+static inline void virtio_features_copy(u64 *to, const u64 *from)
+{
+       memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_U64S);
+}
+
+static inline void virtio_features_andnot(u64 *to, const u64 *f1, const u64 *f2)
+{
+       int i;
+
+       for (i = 0; i < VIRTIO_FEATURES_U64S; i++)
+               to[i] = f1[i] & ~f2[i];
+}
+
+#endif /* _LINUX_VIRTIO_FEATURES_H */
index e44c20c049610de4d06623e24061bb03dca28171..45e784462ec6ebe6cbbaf4e79c09b288d3f4555f 100644 (file)
@@ -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);