* Linux 6.6.157
@ 2026-09-14 11:58 Greg Kroah-Hartman
2026-09-14 11:58 ` Greg Kroah-Hartman
0 siblings, 1 reply; 2+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-14 11:58 UTC (permalink / raw)
To: linux-kernel, akpm, torvalds, stable; +Cc: lwn, jslaby, Greg Kroah-Hartman
I'm announcing the release of the 6.6.157 kernel.
All users of the 6.6 kernel series must upgrade.
The updated 6.6.y git tree can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-6.6.y
and can be browsed at the normal kernel.org git web browser:
https://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=summary
thanks,
greg k-h
------------
Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy | 3
Documentation/ABI/testing/sysfs-bus-nvdimm | 3
Documentation/hwmon/emc1403.rst | 25
Documentation/usb/gadget-testing.rst | 3
Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst | 12
Makefile | 2
arch/alpha/include/uapi/asm/fpu.h | 8
arch/alpha/kernel/pci-sysfs.c | 6
arch/alpha/kernel/sys_marvel.c | 25
arch/alpha/kernel/traps.c | 6
arch/alpha/math-emu/math.c | 88 +
arch/arm/Kconfig | 2
arch/arm/boot/dts/allwinner/sun4i-a10.dtsi | 2
arch/arm/boot/dts/marvell/armada-388-helios4.dts | 13
arch/arm/mach-imx/src.c | 3
arch/arm/mach-lpc32xx/common.c | 5
arch/arm/mach-lpc32xx/common.h | 2
arch/arm/mach-lpc32xx/phy3250.c | 2
arch/arm/mach-lpc32xx/pm.c | 5
arch/arm/mm/fault.c | 16
arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 4
arch/arm64/boot/dts/amlogic/meson-axg-s400.dts | 4
arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts | 3
arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts | 2
arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 4
arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts | 5
arch/arm64/boot/dts/qcom/msm8998.dtsi | 24
arch/arm64/boot/dts/qcom/sc8180x-lenovo-flex-5g.dts | 61
arch/arm64/boot/dts/qcom/sc8180x-pmics.dtsi | 2
arch/arm64/boot/dts/qcom/sc8180x-primus.dts | 12
arch/arm64/boot/dts/qcom/sc8180x.dtsi | 20
arch/arm64/boot/dts/qcom/sc8280xp-crd.dts | 4
arch/arm64/boot/dts/qcom/sdm632-motorola-ocean.dts | 2
arch/arm64/boot/dts/qcom/sdm845.dtsi | 64
arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts | 6
arch/arm64/boot/dts/qcom/sm8150.dtsi | 8
arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 2
arch/arm64/boot/dts/qcom/sm8250.dtsi | 48
arch/arm64/boot/dts/qcom/sm8350.dtsi | 8
arch/arm64/boot/dts/qcom/sm8450.dtsi | 8
arch/arm64/boot/dts/qcom/sm8550.dtsi | 12
arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi | 2
arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi | 3
arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 12
arch/arm64/boot/dts/ti/k3-am64-main.dtsi | 2
arch/arm64/kernel/compat_alignment.c | 4
arch/arm64/kernel/hibernate.c | 16
arch/arm64/lib/Makefile | 4
arch/arm64/mm/fault.c | 12
arch/arm64/mm/mmu.c | 58
arch/csky/kernel/entry.S | 6
arch/loongarch/kernel/kprobes.c | 3
arch/loongarch/kernel/rethook_trampoline.S | 2
arch/m68k/emu/nfcon.c | 2
arch/m68k/kernel/traps.c | 4
arch/mips/kernel/ptrace.c | 6
arch/openrisc/kernel/entry.S | 43
arch/parisc/kernel/head.S | 1
arch/powerpc/configs/85xx-hw.config | 1
arch/powerpc/include/asm/papr-watchdog.h | 64
arch/powerpc/kernel/irq.c | 16
arch/powerpc/kernel/smp.c | 3
arch/powerpc/mm/book3s64/radix_pgtable.c | 7
arch/powerpc/platforms/powermac/low_i2c.c | 2
arch/powerpc/platforms/pseries/iommu.c | 23
arch/powerpc/platforms/pseries/setup.c | 28
arch/riscv/kernel/cpufeature.c | 3
arch/s390/kernel/debug.c | 10
arch/s390/kernel/perf_cpum_cf.c | 215 +-
arch/s390/kvm/guestdbg.c | 9
arch/s390/kvm/interrupt.c | 2
arch/s390/kvm/kvm-s390.c | 15
arch/x86/coco/tdx/tdx.c | 10
arch/x86/events/amd/uncore.c | 753 +++++-----
arch/x86/events/intel/lbr.c | 12
arch/x86/events/intel/uncore.c | 3
arch/x86/events/intel/uncore_snbep.c | 13
arch/x86/include/asm/insn-eval.h | 36
arch/x86/include/asm/linkage.h | 2
arch/x86/include/asm/pkeys.h | 3
arch/x86/kvm/emulate.c | 26
arch/x86/kvm/hyperv.c | 25
arch/x86/kvm/vmx/nested.c | 18
block/blk-cgroup.c | 2
block/blk-iocost.c | 2
block/blk-stat.c | 2
block/genhd.c | 8
block/kyber-iosched.c | 2
drivers/accessibility/speakup/keyhelp.c | 17
drivers/accessibility/speakup/kobjects.c | 6
drivers/acpi/ec.c | 40
drivers/acpi/pci_root.c | 3
drivers/acpi/pfr_update.c | 45
drivers/acpi/processor_core.c | 31
drivers/acpi/processor_driver.c | 18
drivers/acpi/processor_idle.c | 116 +
drivers/acpi/scan.c | 13
drivers/acpi/video_detect.c | 4
drivers/ata/ahci.c | 49
drivers/ata/libata-scsi.c | 28
drivers/auxdisplay/charlcd.c | 21
drivers/base/arch_numa.c | 12
drivers/base/cacheinfo.c | 9
drivers/base/firmware_loader/fallback.c | 10
drivers/base/property.c | 19
drivers/base/soc.c | 15
drivers/base/swnode.c | 2
drivers/block/mtip32xx/mtip32xx.c | 7
drivers/block/mtip32xx/mtip32xx.h | 2
drivers/block/null_blk/main.c | 96 -
drivers/block/null_blk/null_blk.h | 1
drivers/block/ublk_drv.c | 14
drivers/bluetooth/btintel.c | 2
drivers/bluetooth/btmtk.c | 7
drivers/bluetooth/btmtksdio.c | 33
drivers/bluetooth/btqca.c | 3
drivers/bluetooth/btusb.c | 80 -
drivers/bluetooth/hci_bcm.c | 1
drivers/bluetooth/hci_h5.c | 4
drivers/bluetooth/hci_intel.c | 1
drivers/bluetooth/hci_ldisc.c | 2
drivers/bluetooth/hci_serdev.c | 2
drivers/bluetooth/virtio_bt.c | 8
drivers/bus/mhi/ep/main.c | 15
drivers/bus/mhi/host/main.c | 6
drivers/bus/qcom-ebi2.c | 51
drivers/bus/ti-sysc.c | 12
drivers/char/hw_random/ks-sa-rng.c | 22
drivers/char/ipmi/ipmb_dev_int.c | 5
drivers/char/ipmi/ipmi_msghandler.c | 1
drivers/char/ppdev.c | 10
drivers/char/tpm/st33zp24/st33zp24.c | 8
drivers/char/tpm/tpm_i2c_nuvoton.c | 6
drivers/char/xilinx_hwicap/xilinx_hwicap.c | 5
drivers/clk/clk-moxart.c | 14
drivers/clk/clk-palmas.c | 14
drivers/clk/clk-versaclock7.c | 2
drivers/clk/hisilicon/reset.c | 2
drivers/clk/imx/clk-scu.c | 1
drivers/clk/mediatek/clk-mt8135.c | 5
drivers/clk/meson/gxbb.c | 2
drivers/clk/nuvoton/clk-ma35d1-pll.c | 52
drivers/clk/qcom/common.c | 4
drivers/clk/qcom/gcc-mdm9607.c | 182 --
drivers/clk/qcom/gcc-msm8916.c | 5
drivers/clk/qcom/gcc-msm8939.c | 5
drivers/clk/qcom/gcc-qcm2290.c | 12
drivers/clk/qcom/gdsc.c | 25
drivers/clk/rockchip/clk-pll.c | 9
drivers/clk/rockchip/clk-rk3588.c | 2
drivers/clk/tegra/clk-tegra124-emc.c | 1
drivers/clk/ti/adpll.c | 4
drivers/clk/ti/divider.c | 2
drivers/clk/ti/mux.c | 22
drivers/clk/visconti/clkc.c | 2
drivers/clk/x86/clk-pmc-atom.c | 3
drivers/clocksource/clps711x-timer.c | 2
drivers/clocksource/timer-armada-370-xp.c | 18
drivers/clocksource/timer-sun4i.c | 2
drivers/cpufreq/imx6q-cpufreq.c | 6
drivers/cpufreq/intel_pstate.c | 19
drivers/cpufreq/spear-cpufreq.c | 4
drivers/cpuidle/dt_idle_genpd.c | 3
drivers/crypto/atmel-ecc.c | 45
drivers/crypto/atmel-sha204a.c | 6
drivers/crypto/gemini/sl3516-ce-cipher.c | 8
drivers/crypto/hisilicon/sec2/sec_crypto.c | 12
drivers/crypto/intel/keembay/keembay-ocs-aes-core.c | 9
drivers/crypto/intel/qat/qat_common/qat_algs.c | 1
drivers/crypto/rockchip/rk3288_crypto_ahash.c | 7
drivers/crypto/sa2ul.c | 6
drivers/cxl/core/mbox.c | 7
drivers/cxl/core/pmem.c | 10
drivers/cxl/core/region.c | 7
drivers/cxl/cxl.h | 3
drivers/cxl/cxlmem.h | 2
drivers/cxl/pci.c | 14
drivers/cxl/pmem.c | 2
drivers/dax/super.c | 12
drivers/dma/dw-edma/dw-edma-core.c | 127 +
drivers/dma/dw-edma/dw-hdma-v0-core.c | 2
drivers/dma/hisi_dma.c | 2
drivers/dma/mediatek/mtk-uart-apdma.c | 2
drivers/dma/qcom/bam_dma.c | 1
drivers/dma/xilinx/xilinx_dma.c | 12
drivers/firmware/arm_scmi/bus.c | 69
drivers/firmware/arm_scmi/driver.c | 54
drivers/firmware/google/coreboot_table.c | 26
drivers/fpga/altera-cvp.c | 10
drivers/fpga/stratix10-soc.c | 21
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 7
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 14
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 74
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 10
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 10
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 3
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 6
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c | 5
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 24
drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c | 35
drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 2
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3
drivers/gpu/drm/bridge/tc358767.c | 2
drivers/gpu/drm/drm_atomic_uapi.c | 2
drivers/gpu/drm/drm_drv.c | 34
drivers/gpu/drm/drm_dumb_buffers.c | 1
drivers/gpu/drm/gud/gud_connector.c | 12
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 52
drivers/gpu/drm/i915/i915_pci.c | 3
drivers/gpu/drm/lima/lima_device.c | 12
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 5
drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4
drivers/gpu/drm/msm/dsi/dsi_host.c | 14
drivers/gpu/drm/mxsfb/lcdif_kms.c | 15
drivers/gpu/drm/nouveau/nouveau_gem.c | 11
drivers/gpu/drm/nouveau/nouveau_sgdma.c | 4
drivers/gpu/drm/nouveau/nouveau_uvmm.c | 3
drivers/gpu/drm/omapdrm/dss/dsi.c | 7
drivers/gpu/drm/omapdrm/dss/dsi.h | 2
drivers/gpu/drm/panel/panel-edp.c | 19
drivers/gpu/drm/panel/panel-samsung-s6d16d0.c | 10
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2
drivers/gpu/drm/sun4i/sun4i_backend.c | 2
drivers/gpu/drm/sun4i/sun4i_crtc.c | 2
drivers/gpu/drm/sun4i/sun4i_tcon.c | 22
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 2
drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 2
drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 18
drivers/gpu/drm/tve200/tve200_drv.c | 1
drivers/gpu/host1x/debug.c | 4
drivers/gpu/host1x/hw/channel_hw.c | 5
drivers/hid/hid-core.c | 9
drivers/hid/hid-lg4ff.c | 8
drivers/hid/hid-mcp2221.c | 9
drivers/hid/hid-nintendo.c | 1
drivers/hid/hid-picolcd_debugfs.c | 9
drivers/hid/hid-rmi.c | 46
drivers/hid/hid-roccat-kone.c | 8
drivers/hid/hid-roccat.c | 13
drivers/hid/hid-sensor-custom.c | 9
drivers/hid/hid-sensor-hub.c | 42
drivers/hid/i2c-hid/Makefile | 2
drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c | 104 +
drivers/hid/i2c-hid/i2c-hid-acpi.c | 52
drivers/hid/i2c-hid/i2c-hid-acpi.h | 33
drivers/hid/i2c-hid/i2c-hid-core.c | 119 -
drivers/hid/i2c-hid/i2c-hid-of-goodix.c | 4
drivers/hid/wacom_wac.c | 13
drivers/hsi/controllers/omap_ssi_core.c | 6
drivers/hwmon/emc1403.c | 752 ++++++---
drivers/hwmon/max6621.c | 8
drivers/hwspinlock/hwspinlock_core.c | 2
drivers/hwtracing/coresight/coresight-etm4x-cfg.c | 2
drivers/hwtracing/coresight/coresight-etm4x-core.c | 15
drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 6
drivers/hwtracing/coresight/coresight-etm4x.h | 9
drivers/hwtracing/ptt/hisi_ptt.c | 20
drivers/i2c/busses/i2c-ocores.c | 6
drivers/i2c/i2c-core-base.c | 6
drivers/i2c/i2c-mux.c | 1
drivers/i2c/muxes/i2c-demux-pinctrl.c | 1
drivers/i3c/master.c | 11
drivers/i3c/master/dw-i3c-master.c | 10
drivers/i3c/master/mipi-i3c-hci/core.c | 30
drivers/i3c/master/mipi-i3c-hci/dma.c | 4
drivers/i3c/master/mipi-i3c-hci/ext_caps.c | 55
drivers/i3c/master/mipi-i3c-hci/pio.c | 54
drivers/i3c/master/svc-i3c-master.c | 10
drivers/iio/accel/dmard09.c | 7
drivers/iio/chemical/atlas-sensor.c | 19
drivers/iio/chemical/sgp30.c | 3
drivers/iio/dac/m62332.c | 17
drivers/iio/gyro/mpu3050-core.c | 2
drivers/iio/light/cm32181.c | 2
drivers/iio/light/gp2ap002.c | 24
drivers/iio/light/isl29028.c | 2
drivers/iio/light/opt4001.c | 37
drivers/iio/light/tsl2583.c | 2
drivers/iio/light/tsl2772.c | 2
drivers/iio/orientation/hid-sensor-rotation.c | 20
drivers/iio/pressure/dps310.c | 3
drivers/iio/pressure/mpl115.c | 11
drivers/iio/proximity/srf04.c | 1
drivers/iio/temperature/hid-sensor-temperature.c | 3
drivers/infiniband/core/cma.c | 6
drivers/infiniband/core/core_priv.h | 1
drivers/infiniband/core/counters.c | 5
drivers/infiniband/core/cq.c | 14
drivers/infiniband/core/device.c | 3
drivers/infiniband/core/nldev.c | 106 +
drivers/infiniband/core/restrack.c | 186 ++
drivers/infiniband/core/restrack.h | 4
drivers/infiniband/core/ucma.c | 7
drivers/infiniband/core/uverbs_cmd.c | 13
drivers/infiniband/core/verbs.c | 28
drivers/infiniband/hw/bnxt_re/ib_verbs.c | 6
drivers/infiniband/hw/cxgb4/device.c | 8
drivers/infiniband/hw/cxgb4/mem.c | 4
drivers/infiniband/hw/efa/efa_verbs.c | 13
drivers/infiniband/hw/erdma/Kconfig | 2
drivers/infiniband/hw/erdma/erdma_eq.c | 7
drivers/infiniband/hw/erdma/erdma_verbs.c | 12
drivers/infiniband/hw/erdma/erdma_verbs.h | 25
drivers/infiniband/hw/hfi1/chip.c | 4
drivers/infiniband/hw/hfi1/hfi.h | 2
drivers/infiniband/hw/hfi1/init.c | 44
drivers/infiniband/hw/hfi1/user_sdma.c | 13
drivers/infiniband/hw/mlx5/cong.c | 8
drivers/infiniband/hw/mlx5/main.c | 2
drivers/infiniband/hw/mlx5/qp.c | 25
drivers/infiniband/sw/rxe/rxe_req.c | 15
drivers/infiniband/sw/rxe/rxe_resp.c | 9
drivers/infiniband/sw/siw/siw_cm.c | 32
drivers/infiniband/ulp/ipoib/ipoib_main.c | 2
drivers/infiniband/ulp/isert/ib_isert.c | 85 -
drivers/infiniband/ulp/isert/ib_isert.h | 1
drivers/infiniband/ulp/srp/ib_srp.c | 45
drivers/infiniband/ulp/srpt/ib_srpt.c | 9
drivers/input/tablet/aiptek.c | 13
drivers/interconnect/core.c | 7
drivers/iommu/amd/init.c | 42
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 5
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 6
drivers/iommu/dma-iommu.c | 13
drivers/iommu/intel/dmar.c | 15
drivers/iommu/intel/iommu.c | 18
drivers/iommu/intel/iommu.h | 2
drivers/iommu/msm_iommu.c | 2
drivers/iommu/mtk_iommu_v1.c | 2
drivers/ipack/devices/ipoctal.c | 70
drivers/irqchip/irq-gic-v3-its.c | 23
drivers/irqchip/irq-mbigen.c | 20
drivers/irqchip/irq-renesas-irqc.c | 1
drivers/irqchip/irq-renesas-rzg2l.c | 4
drivers/leds/leds-pca9532.c | 18
drivers/mailbox/pcc.c | 70
drivers/mailbox/qcom-ipcc.c | 17
drivers/mailbox/rockchip-mailbox.c | 17
drivers/md/dm-era-target.c | 5
drivers/md/dm-log-userspace-base.c | 1
drivers/md/dm-stats.c | 6
drivers/md/dm-switch.c | 2
drivers/md/dm.c | 14
drivers/md/md-bitmap.c | 6
drivers/md/md.c | 9
drivers/md/persistent-data/dm-array.c | 16
drivers/md/raid1.c | 2
drivers/md/raid5-ppl.c | 4
drivers/md/raid5.c | 23
drivers/media/cec/core/cec-adap.c | 8
drivers/media/cec/core/cec-core.c | 2
drivers/media/cec/core/cec-pin.c | 8
drivers/media/cec/platform/meson/ao-cec-g12a.c | 1
drivers/media/cec/platform/stm32/stm32-cec.c | 3
drivers/media/dvb-frontends/rtl2832_sdr.c | 21
drivers/media/i2c/ov02a10.c | 12
drivers/media/i2c/ov7740.c | 4
drivers/media/i2c/rdacm21.c | 5
drivers/media/i2c/video-i2c.c | 8
drivers/media/pci/cobalt/cobalt-alsa-main.c | 1
drivers/media/pci/cx23885/cimax2.c | 1
drivers/media/pci/saa7164/saa7164-core.c | 8
drivers/media/pci/zoran/zoran_card.c | 2
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 6
drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 2
drivers/media/platform/qcom/venus/hfi_parser.c | 9
drivers/media/rc/sunxi-cir.c | 9
drivers/media/test-drivers/vicodec/vicodec-core.c | 4
drivers/media/test-drivers/vimc/vimc-capture.c | 4
drivers/media/tuners/tda18250.c | 4
drivers/media/usb/airspy/airspy.c | 17
drivers/media/usb/cx231xx/cx231xx-video.c | 4
drivers/media/usb/em28xx/em28xx-cards.c | 3
drivers/media/usb/em28xx/em28xx-core.c | 2
drivers/media/usb/go7007/snd-go7007.c | 10
drivers/media/usb/s2255/s2255drv.c | 11
drivers/media/usb/usbtv/usbtv-audio.c | 11
drivers/media/v4l2-core/v4l2-async.c | 19
drivers/media/v4l2-core/v4l2-ctrls-core.c | 49
drivers/media/v4l2-core/v4l2-dev.c | 14
drivers/media/v4l2-core/v4l2-fwnode.c | 2
drivers/mfd/iqs62x.c | 2
drivers/mfd/rave-sp.c | 64
drivers/misc/ad525x_dpot-i2c.c | 1
drivers/misc/ad525x_dpot-spi.c | 1
drivers/misc/ad525x_dpot.c | 177 +-
drivers/misc/ad525x_dpot.h | 3
drivers/misc/bcm-vk/bcm_vk_msg.c | 6
drivers/misc/cardreader/rtsx_pcr.c | 2
drivers/misc/cardreader/rtsx_usb.c | 23
drivers/misc/vmw_vmci/vmci_event.c | 4
drivers/mmc/host/via-sdmmc.c | 8
drivers/mtd/mtdoops.c | 3
drivers/mtd/mtdpart.c | 3
drivers/mtd/mtdswap.c | 1
drivers/mtd/nand/raw/nand_onfi.c | 27
drivers/mtd/parsers/afs.c | 7
drivers/mtd/ubi/attach.c | 4
drivers/mtd/ubi/build.c | 35
drivers/mtd/ubi/cdev.c | 3
drivers/mtd/ubi/eba.c | 3
drivers/mtd/ubi/fastmap-wl.c | 53
drivers/mtd/ubi/fastmap.c | 68
drivers/mtd/ubi/io.c | 11
drivers/mtd/ubi/ubi.h | 12
drivers/mtd/ubi/wl.c | 32
drivers/mtd/ubi/wl.h | 6
drivers/net/bonding/bond_netlink.c | 2
drivers/net/can/usb/peak_usb/pcan_usb_core.c | 2
drivers/net/dsa/b53/b53_common.c | 2
drivers/net/dsa/mv88e6xxx/pcs-6352.c | 5
drivers/net/ethernet/allwinner/sun4i-emac.c | 4
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 3
drivers/net/ethernet/freescale/fec_main.c | 6
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 12
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c | 3
drivers/net/ethernet/intel/ice/ice_lib.c | 3
drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c | 2
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 11
drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2
drivers/net/ethernet/nvidia/forcedeth.c | 6
drivers/net/ethernet/qlogic/qede/qede.h | 8
drivers/net/ethernet/qlogic/qede/qede_fp.c | 1
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 1
drivers/net/ethernet/realtek/8139cp.c | 2
drivers/net/ethernet/renesas/ravb.h | 1
drivers/net/ethernet/renesas/ravb_main.c | 3
drivers/net/ethernet/renesas/ravb_ptp.c | 15
drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 2
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 2
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c | 2
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c | 41
drivers/net/ethernet/sun/niu.c | 2
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 4
drivers/net/gtp.c | 2
drivers/net/ipa/ipa_main.c | 29
drivers/net/ntb_netdev.c | 38
drivers/net/ppp/pppox.c | 17
drivers/net/slip/slip.c | 19
drivers/net/thunderbolt/main.c | 18
drivers/net/tun.c | 21
drivers/net/usb/qmi_wwan.c | 1
drivers/net/vxlan/vxlan_core.c | 2
drivers/net/vxlan/vxlan_mdb.c | 365 ++++
drivers/net/vxlan/vxlan_private.h | 4
drivers/net/vxlan/vxlan_vnifilter.c | 6
drivers/net/wireless/ath/ath10k/snoc.c | 9
drivers/net/wireless/ath/ath11k/Makefile | 4
drivers/net/wireless/ath/ath11k/core.c | 8
drivers/net/wireless/ath/ath11k/core.h | 15
drivers/net/wireless/ath/ath11k/dp_rx.c | 30
drivers/net/wireless/ath/ath11k/fw.c | 168 ++
drivers/net/wireless/ath/ath11k/fw.h | 27
drivers/net/wireless/ath/ath11k/mhi.c | 18
drivers/net/wireless/ath/ath11k/p2p.c | 149 +
drivers/net/wireless/ath/ath11k/p2p.h | 22
drivers/net/wireless/ath/ath11k/pci.c | 2
drivers/net/wireless/ath/ath11k/qmi.c | 56
drivers/net/wireless/ath/ath11k/testmode.c | 2
drivers/net/wireless/ath/ath11k/wmi.c | 167 +-
drivers/net/wireless/ath/ath11k/wmi.h | 35
drivers/net/wireless/ath/ath12k/wmi.c | 12
drivers/net/wireless/ath/ath6kl/cfg80211.c | 5
drivers/net/wireless/ath/ath6kl/wmi.c | 17
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 3
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 2
drivers/net/wireless/intel/iwlwifi/fw/dump.c | 2
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 8
drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h | 10
drivers/net/wireless/intel/iwlwifi/mei/main.c | 66
drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 12
drivers/net/wireless/marvell/mwifiex/usb.c | 2
drivers/net/wireless/mediatek/mt76/mt7615/main.c | 5
drivers/net/wireless/mediatek/mt76/mt76x02_mac.c | 2
drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c | 2
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 9
drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 6
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 8
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 10
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 6
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 18
drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 9
drivers/net/wireless/mediatek/mt76/mt7915/soc.c | 2
drivers/net/wireless/mediatek/mt76/mt7915/testmode.c | 5
drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c | 6
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 1
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 2
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 16
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 2
drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 12
drivers/net/wireless/mediatek/mt76/sdio.c | 11
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2
drivers/net/wireless/realtek/rtlwifi/pci.c | 6
drivers/net/wireless/realtek/rtw88/tx.c | 1
drivers/net/wireless/realtek/rtw89/mac.c | 2
drivers/net/wireless/virtual/mac80211_hwsim.c | 5
drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 8
drivers/nfc/pn533/pn533.c | 21
drivers/ntb/ntb_transport.c | 38
drivers/nvdimm/btt.c | 8
drivers/nvdimm/label.c | 17
drivers/nvme/host/apple.c | 227 ++-
drivers/nvme/host/core.c | 2
drivers/nvme/host/fc.c | 8
drivers/nvme/host/ioctl.c | 117 +
drivers/nvme/host/pci.c | 1
drivers/nvme/host/tcp.c | 52
drivers/nvme/target/auth.c | 6
drivers/nvme/target/core.c | 2
drivers/nvme/target/fabrics-cmd-auth.c | 12
drivers/nvme/target/nvmet.h | 2
drivers/nvme/target/rdma.c | 121 +
drivers/nvme/target/tcp.c | 10
drivers/nvme/target/zns.c | 2
drivers/of/base.c | 2
drivers/of/property.c | 91 +
drivers/parisc/eisa.c | 5
drivers/pci/controller/cadence/pci-j721e.c | 4
drivers/pci/controller/dwc/pci-meson.c | 2
drivers/pci/controller/pci-hyperv.c | 1
drivers/pci/controller/pci-xgene.c | 11
drivers/pci/msi/msi.c | 10
drivers/pci/pci-sysfs.c | 4
drivers/pci/proc.c | 16
drivers/pci/quirks.c | 6
drivers/perf/arm_spe_pmu.c | 2
drivers/perf/cxl_pmu.c | 2
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 2
drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c | 12
drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 12
drivers/phy/qualcomm/phy-qcom-sgmii-eth.c | 43
drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 26
drivers/phy/rockchip/phy-rockchip-inno-csidphy.c | 2
drivers/phy/starfive/phy-jh7110-dphy-rx.c | 5
drivers/phy/sunplus/phy-sunplus-usb2.c | 6
drivers/pinctrl/bcm/pinctrl-bcm2835.c | 1
drivers/pinctrl/mediatek/mtk-eint.c | 344 ++--
drivers/pinctrl/mediatek/mtk-eint.h | 28
drivers/pinctrl/mediatek/pinctrl-moore.h | 12
drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c | 40
drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h | 1
drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 14
drivers/pinctrl/mediatek/pinctrl-paris.h | 19
drivers/pinctrl/pinctrl-rockchip.c | 10
drivers/platform/chrome/cros_ec_debugfs.c | 6
drivers/platform/chrome/cros_ec_sensorhub_ring.c | 28
drivers/platform/chrome/cros_ec_typec.c | 6
drivers/platform/mellanox/mlxbf-bootctl.c | 1
drivers/platform/mellanox/mlxbf-pmc.c | 10
drivers/platform/surface/surface_acpi_notify.c | 6
drivers/platform/x86/dell/dell-wmi-base.c | 17
drivers/platform/x86/dell/dell-wmi-privacy.c | 4
drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c | 1
drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h | 2
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 18
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 8
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 15
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c | 6
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 14
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 19
drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c | 4
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c | 6
drivers/platform/x86/intel/ishtp_eclite.c | 5
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 30
drivers/pmdomain/bcm/bcm2835-power.c | 7
drivers/power/supply/bd99954-charger.h | 23
drivers/power/supply/bq24257_charger.c | 16
drivers/power/supply/bq256xx_charger.c | 29
drivers/power/supply/bq25890_charger.c | 12
drivers/power/supply/bq27xxx_battery.c | 6
drivers/power/supply/cros_usbpd-charger.c | 17
drivers/power/supply/isp1704_charger.c | 1
drivers/power/supply/lp8727_charger.c | 4
drivers/power/supply/max17040_battery.c | 6
drivers/power/supply/qcom_battmgr.c | 2
drivers/power/supply/rt9455_charger.c | 21
drivers/power/supply/sbs-battery.c | 7
drivers/power/supply/sc2731_charger.c | 8
drivers/power/supply/twl4030_charger.c | 12
drivers/power/supply/ucs1002_power.c | 6
drivers/pps/clients/pps-gpio.c | 62
drivers/pps/pps.c | 10
drivers/rapidio/devices/rio_mport_cdev.c | 10
drivers/rapidio/rio-scan.c | 1
drivers/regulator/Kconfig | 4
drivers/regulator/as3722-regulator.c | 1
drivers/regulator/core.c | 6
drivers/regulator/max8998.c | 1
drivers/regulator/qcom-refgen-regulator.c | 4
drivers/regulator/qcom-rpmh-regulator.c | 6
drivers/regulator/tps6594-regulator.c | 437 ++++-
drivers/remoteproc/mtk_scp.c | 9
drivers/remoteproc/qcom_common.c | 4
drivers/remoteproc/qcom_q6v5_adsp.c | 1
drivers/remoteproc/remoteproc_core.c | 150 +
drivers/remoteproc/remoteproc_sysfs.c | 1
drivers/rpmsg/qcom_glink_native.c | 42
drivers/rpmsg/qcom_glink_smem.c | 7
drivers/rtc/rtc-gamecube.c | 4
drivers/rtc/rtc-pcf85363.c | 17
drivers/rtc/rtc-pcf8563.c | 4
drivers/rtc/rtc-rzn1.c | 10
drivers/rtc/rtc-zynqmp.c | 7
drivers/s390/block/dasd.c | 6
drivers/s390/block/dasd_eckd.c | 40
drivers/s390/block/dasd_erp.c | 3
drivers/s390/crypto/vfio_ap_ops.c | 176 +-
drivers/scsi/mpt3sas/mpt3sas_base.c | 2
drivers/scsi/pm8001/pm8001_init.c | 4
drivers/scsi/qla2xxx/qla_attr.c | 7
drivers/scsi/qla2xxx/qla_bsg.c | 39
drivers/scsi/qla2xxx/qla_dbg.c | 2
drivers/scsi/qla2xxx/qla_dfs.c | 4
drivers/scsi/qla2xxx/qla_edif.c | 3
drivers/scsi/qla2xxx/qla_gs.c | 4
drivers/scsi/qla2xxx/qla_init.c | 61
drivers/scsi/qla2xxx/qla_inline.h | 13
drivers/scsi/qla2xxx/qla_isr.c | 39
drivers/scsi/qla2xxx/qla_mbx.c | 28
drivers/scsi/qla2xxx/qla_nvme.c | 31
drivers/scsi/qla2xxx/qla_nx2.c | 2
drivers/scsi/qla2xxx/qla_os.c | 14
drivers/scsi/scsi_lib.c | 2
drivers/scsi/sd.c | 4
drivers/scsi/smartpqi/smartpqi_init.c | 18
drivers/soc/fsl/qe/qe_ic.c | 3
drivers/soc/qcom/rpmh-rsc.c | 37
drivers/soc/samsung/exynos-pmu.c | 4
drivers/soundwire/qcom.c | 2
drivers/spi/spi-bcm63xx-hsspi.c | 8
drivers/spi/spi-bcm63xx.c | 6
drivers/spi/spi-bcmbca-hsspi.c | 8
drivers/spi/spi-davinci.c | 22
drivers/spi/spi-img-spfi.c | 5
drivers/spi/spi-sprd-adi.c | 6
drivers/staging/fbtft/fbtft-sysfs.c | 2
drivers/staging/greybus/audio_gb.c | 13
drivers/staging/greybus/audio_manager_sysfs.c | 2
drivers/staging/greybus/hid.c | 2
drivers/staging/media/meson/vdec/vdec.c | 4
drivers/staging/media/sunxi/cedrus/cedrus.c | 4
drivers/staging/media/tegra-video/vi.c | 9
drivers/staging/media/tegra-video/vip.c | 10
drivers/staging/octeon/ethernet-mem.c | 43
drivers/staging/octeon/ethernet-mem.h | 8
drivers/staging/octeon/ethernet-rx.c | 51
drivers/staging/octeon/ethernet-rx.h | 11
drivers/staging/octeon/ethernet-tx.c | 2
drivers/staging/octeon/ethernet.c | 43
drivers/staging/octeon/octeon-ethernet.h | 14
drivers/staging/rtl8723bs/core/rtw_cmd.c | 8
drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 7
drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 4
drivers/staging/sm750fb/sm750.c | 4
drivers/target/iscsi/iscsi_target_login.c | 2
drivers/thermal/imx_thermal.c | 9
drivers/thermal/intel/int340x_thermal/int3400_thermal.c | 4
drivers/thermal/qoriq_thermal.c | 13
drivers/thermal/rcar_thermal.c | 15
drivers/tty/hvc/Kconfig | 2
drivers/tty/serial/amba-pl011.c | 10
drivers/tty/serial/ma35d1_serial.c | 12
drivers/tty/tty_io.c | 4
drivers/ufs/core/ufs-debugfs.c | 2
drivers/ufs/core/ufshcd.c | 18
drivers/uio/uio.c | 5
drivers/usb/atm/usbatm.c | 4
drivers/usb/common/common.c | 3
drivers/usb/core/driver.c | 88 -
drivers/usb/core/usb.c | 2
drivers/usb/core/usb.h | 8
drivers/usb/dwc2/gadget.c | 11
drivers/usb/gadget/configfs.c | 4
drivers/usb/gadget/function/f_fs.c | 14
drivers/usb/gadget/function/f_mass_storage.c | 2
drivers/usb/gadget/function/f_midi2.c | 19
drivers/usb/gadget/function/f_tcm.c | 23
drivers/usb/gadget/function/f_uac1_legacy.c | 56
drivers/usb/gadget/function/f_uvc.c | 7
drivers/usb/gadget/function/u_audio.c | 24
drivers/usb/gadget/function/u_uac1_legacy.h | 3
drivers/usb/gadget/functions.c | 2
drivers/usb/gadget/legacy/inode.c | 3
drivers/usb/gadget/udc/aspeed_udc.c | 43
drivers/usb/gadget/udc/r8a66597-udc.c | 1
drivers/usb/gadget/udc/snps_udc_plat.c | 5
drivers/usb/host/xhci-ring.c | 32
drivers/usb/image/mdc800.c | 4
drivers/usb/misc/onboard_usb_hub.c | 2
drivers/usb/mtu3/mtu3_core.c | 11
drivers/usb/phy/phy-fsl-usb.c | 52
drivers/usb/phy/phy-fsl-usb.h | 6
drivers/usb/renesas_usbhs/common.c | 6
drivers/usb/serial/bus.c | 6
drivers/usb/serial/usb-serial.c | 6
drivers/usb/storage/ene_ub6250.c | 2
drivers/usb/storage/uas.c | 2
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c | 2
drivers/usb/typec/ucsi/debugfs.c | 1
drivers/usb/typec/ucsi/displayport.c | 2
drivers/usb/typec/ucsi/ucsi.c | 12
drivers/usb/typec/ucsi/ucsi.h | 3
drivers/usb/typec/ucsi/ucsi_acpi.c | 3
drivers/usb/usbip/stub_main.c | 8
drivers/vdpa/mlx5/core/mr.c | 2
drivers/vdpa/vdpa_sim/vdpa_sim.c | 25
drivers/vfio/pci/vfio_pci_config.c | 1
drivers/video/fbdev/kyro/fbdev.c | 24
drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c | 4
drivers/video/fbdev/pvr2fb.c | 4
drivers/video/fbdev/ssd1307fb.c | 72
drivers/video/fbdev/tdfxfb.c | 2
drivers/video/fbdev/uvesafb.c | 2
drivers/video/sticore.c | 22
drivers/virtio/virtio_balloon.c | 6
drivers/w1/masters/ds2482.c | 4
drivers/w1/slaves/w1_ds28e17.c | 8
drivers/watchdog/pseries-wdt.c | 53
drivers/xen/xenbus/xenbus_xs.c | 16
fs/btrfs/verity.c | 16
fs/ceph/caps.c | 11
fs/ceph/crypto.c | 34
fs/ceph/export.c | 26
fs/ceph/file.c | 48
fs/ceph/mdsmap.c | 11
fs/ceph/xattr.c | 17
fs/ecryptfs/crypto.c | 2
fs/ecryptfs/ecryptfs_kernel.h | 3
fs/ecryptfs/keystore.c | 45
fs/ecryptfs/messaging.c | 11
fs/ecryptfs/miscdev.c | 5
fs/ecryptfs/super.c | 7
fs/efivarfs/super.c | 30
fs/erofs/Kconfig | 8
fs/ext4/file.c | 7
fs/ext4/inline.c | 11
fs/ext4/inode.c | 28
fs/ext4/migrate.c | 3
fs/ext4/namei.c | 2
fs/ext4/orphan.c | 10
fs/f2fs/file.c | 116 +
fs/f2fs/namei.c | 11
fs/f2fs/xattr.c | 2
fs/fat/misc.c | 4
fs/fat/nfs.c | 4
fs/fuse/control.c | 2
fs/fuse/cuse.c | 5
fs/fuse/dev.c | 40
fs/fuse/dir.c | 21
fs/fuse/fuse_i.h | 36
fs/fuse/inode.c | 87 -
fs/fuse/readdir.c | 42
fs/hfsplus/catalog.c | 25
fs/hfsplus/hfsplus_fs.h | 6
fs/hugetlbfs/inode.c | 3
fs/isofs/compress.c | 7
fs/lockd/svcsubs.c | 55
fs/nfs/client.c | 21
fs/nfs/filelayout/filelayoutdev.c | 3
fs/nfs/flexfilelayout/flexfilelayoutdev.c | 3
fs/nfs/inode.c | 4
fs/nfs/internal.h | 1
fs/nfs/netns.h | 6
fs/nfs/nfs4client.c | 1
fs/nfs/pnfs.c | 36
fs/nfs/pnfs.h | 3
fs/nfs/pnfs_nfs.c | 39
fs/nfsd/flexfilelayoutxdr.c | 20
fs/nfsd/nfs2acl.c | 52
fs/nfsd/nfs3acl.c | 17
fs/nfsd/nfs4proc.c | 39
fs/nfsd/nfs4state.c | 202 ++
fs/nfsd/nfs4xdr.c | 17
fs/nfsd/nfscache.c | 2
fs/nfsd/nfsfh.c | 4
fs/nfsd/nfsproc.c | 7
fs/nfsd/nfssvc.c | 4
fs/nfsd/nfsxdr.c | 32
fs/nfsd/vfs.c | 4
fs/nilfs2/bmap.c | 2
fs/nilfs2/bmap.h | 2
fs/nilfs2/btnode.c | 30
fs/nilfs2/btree.c | 39
fs/nilfs2/direct.c | 4
fs/nilfs2/page.c | 66
fs/nilfs2/page.h | 8
fs/nilfs2/segment.c | 40
fs/nilfs2/the_nilfs.c | 6
fs/notify/fanotify/fanotify_user.c | 17
fs/notify/inotify/inotify_user.c | 15
fs/ntfs3/attrib.c | 11
fs/ntfs3/frecord.c | 88 -
fs/ntfs3/fslog.c | 47
fs/ntfs3/fsntfs.c | 8
fs/ntfs3/ntfs_fs.h | 21
fs/ntfs3/record.c | 125 +
fs/ntfs3/super.c | 13
fs/ntfs3/xattr.c | 21
fs/ocfs2/acl.c | 135 +
fs/ocfs2/acl.h | 19
fs/ocfs2/cluster/heartbeat.c | 191 +-
fs/ocfs2/cluster/heartbeat.h | 5
fs/ocfs2/cluster/nodemanager.c | 10
fs/ocfs2/cluster/nodemanager.h | 1
fs/ocfs2/cluster/tcp.c | 83 -
fs/ocfs2/cluster/tcp.h | 1
fs/ocfs2/dir.c | 2
fs/ocfs2/dlm/dlmmaster.c | 6
fs/ocfs2/dlm/dlmrecovery.c | 9
fs/ocfs2/namei.c | 17
fs/ocfs2/refcounttree.c | 47
fs/ocfs2/xattr.c | 91 -
fs/ocfs2/xattr.h | 9
fs/orangefs/devorangefs-req.c | 1
fs/orangefs/orangefs-debugfs.c | 1
fs/smb/client/cifs_debug.c | 2
fs/smb/client/cifsfs.c | 15
fs/smb/client/dfs_cache.c | 33
fs/smb/client/smb1ops.c | 2
fs/smb/client/smb2file.c | 5
fs/smb/client/smb2pdu.c | 16
fs/smb/server/auth.c | 1
fs/smb/server/connection.c | 19
fs/smb/server/connection.h | 1
fs/smb/server/mgmt/user_config.c | 1
fs/smb/server/mgmt/user_session.c | 11
fs/smb/server/mgmt/user_session.h | 1
fs/smb/server/server.c | 4
fs/smb/server/smb2ops.c | 11
fs/smb/server/smb2pdu.c | 39
fs/smb/server/smbacl.c | 20
fs/smb/server/transport_ipc.c | 12
fs/smb/server/transport_tcp.c | 6
fs/smb/server/vfs.c | 3
fs/smb/server/vfs_cache.c | 13
fs/squashfs/cache.c | 2
fs/tracefs/event_inode.c | 7
fs/ubifs/auth.c | 2
fs/udf/inode.c | 2
fs/udf/partition.c | 2
fs/udf/super.c | 23
fs/udf/symlink.c | 2
include/acpi/pcc.h | 5
include/acpi/processor.h | 2
include/linux/bpf.h | 4
include/linux/efi.h | 4
include/linux/irq.h | 35
include/linux/irqdomain.h | 46
include/linux/kprobes.h | 1
include/linux/list.h | 15
include/linux/maple_tree.h | 2
include/linux/mfd/tps6594.h | 347 ++++
include/linux/netdevice.h | 10
include/linux/nfs_xdr.h | 2
include/linux/nvme.h | 81 +
include/linux/of_graph.h | 53
include/linux/rcutiny.h | 2
include/linux/rcutree.h | 3
include/linux/remoteproc.h | 284 ---
include/linux/rsc_table.h | 364 ++++
include/linux/rtsx_usb.h | 3
include/linux/sunrpc/svc_rdma_pcl.h | 2
include/linux/usb.h | 29
include/linux/virtio_net.h | 4
include/media/rc-map.h | 2
include/net/bluetooth/hci_core.h | 2
include/net/bluetooth/l2cap.h | 5
include/net/dst.h | 34
include/net/inet_connection_sock.h | 12
include/net/inet_hashtables.h | 2
include/net/inetpeer.h | 4
include/net/ip.h | 11
include/net/ip_tunnels.h | 11
include/net/pkt_sched.h | 1
include/net/route.h | 2
include/net/sctp/structs.h | 2
include/net/tcp.h | 13
include/net/xsk_buff_pool.h | 19
include/rdma/ib_verbs.h | 14
include/rdma/restrack.h | 7
include/sound/core.h | 2
include/uapi/linux/if_bridge.h | 19
include/uapi/linux/if_link.h | 1
include/uapi/linux/landlock.h | 1
include/uapi/linux/time_types.h | 2
include/uapi/rdma/rdma_netlink.h | 8
kernel/auditfilter.c | 6
kernel/bpf/bloom_filter.c | 19
kernel/bpf/cgroup.c | 18
kernel/bpf/net_namespace.c | 17
kernel/bpf/ringbuf.c | 2
kernel/bpf/syscall.c | 21
kernel/bpf/task_iter.c | 36
kernel/bpf/verifier.c | 20
kernel/cgroup/cpuset.c | 4
kernel/dma/swiotlb.c | 31
kernel/entry/common.c | 3
kernel/futex/requeue.c | 12
kernel/irq/generic-chip.c | 91 -
kernel/irq/irqdomain.c | 91 +
kernel/kcsan/core.c | 12
kernel/kprobes.c | 14
kernel/locking/lockdep.c | 2
kernel/power/snapshot.c | 5
kernel/power/wakelock.c | 2
kernel/rcu/rcutorture.c | 4
kernel/rcu/tree.c | 4
kernel/rcu/tree_nocb.h | 2
kernel/rcu/tree_plugin.h | 16
kernel/sched/cpufreq_schedutil.c | 17
kernel/sched/fair.c | 27
kernel/signal.c | 4
kernel/stop_machine.c | 2
kernel/sys.c | 2
kernel/taskstats.c | 3
kernel/time/clocksource.c | 8
kernel/time/itimer.c | 2
kernel/time/timekeeping.c | 6
kernel/time/timer.c | 1
kernel/trace/trace.c | 28
kernel/trace/trace.h | 4
kernel/trace/trace_events.c | 134 +
kernel/trace/trace_events_hist.c | 4
kernel/trace/trace_events_user.c | 3
kernel/trace/trace_mmiotrace.c | 24
kernel/trace/trace_osnoise.c | 4
lib/string.c | 3
lib/test_hmm.c | 2
lib/ucs2_string.c | 2
lib/xarray.c | 3
mm/damon/core-test.h | 15
mm/damon/core.c | 17
mm/damon/paddr.c | 21
mm/damon/sysfs-schemes.c | 10
mm/damon/sysfs.c | 19
mm/damon/vaddr-test.h | 5
mm/damon/vaddr.c | 34
mm/hugetlb.c | 14
mm/kasan/quarantine.c | 7
mm/memcontrol.c | 6
mm/migrate.c | 2
mm/mm_init.c | 5
mm/vmscan.c | 2
net/batman-adv/bridge_loop_avoidance.c | 78 -
net/batman-adv/distributed-arp-table.c | 66
net/batman-adv/main.c | 4
net/batman-adv/types.h | 2
net/bluetooth/eir.c | 3
net/bluetooth/hci_conn.c | 23
net/bluetooth/hci_core.c | 2
net/bluetooth/hci_event.c | 7
net/bluetooth/hci_sync.c | 14
net/bluetooth/iso.c | 8
net/bluetooth/l2cap_sock.c | 36
net/bluetooth/mgmt.c | 27
net/bluetooth/msft.c | 5
net/bluetooth/rfcomm/core.c | 12
net/bridge/br_mrp_netlink.c | 4
net/bridge/br_netlink_tunnel.c | 3
net/bridge/br_vlan.c | 4
net/ceph/messenger_v2.c | 5
net/ceph/osd_client.c | 30
net/ceph/osdmap.c | 2
net/core/dst.c | 4
net/core/filter.c | 26
net/core/lwt_bpf.c | 15
net/core/net_namespace.c | 4
net/core/skbuff.c | 10
net/core/sock.c | 8
net/core/xdp.c | 4
net/dccp/dccp.h | 4
net/dccp/ipv4.c | 4
net/dccp/ipv6.c | 7
net/dccp/minisocks.c | 2
net/hsr/hsr_device.c | 70
net/hsr/hsr_device.h | 4
net/hsr/hsr_forward.c | 85 +
net/hsr/hsr_framereg.c | 52
net/hsr/hsr_framereg.h | 4
net/hsr/hsr_main.h | 7
net/hsr/hsr_netlink.c | 30
net/hsr/hsr_slave.c | 23
net/ipv4/icmp.c | 28
net/ipv4/igmp.c | 2
net/ipv4/inet_connection_sock.c | 9
net/ipv4/inetpeer.c | 38
net/ipv4/ip_fragment.c | 6
net/ipv4/ip_output.c | 4
net/ipv4/ip_tunnel.c | 2
net/ipv4/ip_vti.c | 4
net/ipv4/ipip.c | 2
net/ipv4/ipmr.c | 2
net/ipv4/netfilter.c | 4
net/ipv4/route.c | 10
net/ipv4/syncookies.c | 2
net/ipv4/tcp_fastopen.c | 6
net/ipv4/tcp_ipv4.c | 11
net/ipv4/tcp_metrics.c | 8
net/ipv4/tcp_minisocks.c | 2
net/ipv4/tcp_output.c | 2
net/ipv4/xfrm4_output.c | 2
net/ipv6/ip6_gre.c | 14
net/ipv6/ip6_tunnel.c | 22
net/ipv6/route.c | 2
net/ipv6/seg6_local.c | 9
net/ipv6/sit.c | 2
net/ipv6/tcp_ipv6.c | 97 -
net/ipv6/xfrm6_input.c | 2
net/iucv/af_iucv.c | 2
net/kcm/kcmsock.c | 3
net/mac80211/link.c | 3
net/mac80211/s1g.c | 2
net/mac80211/tx.c | 3
net/mpls/af_mpls.c | 2
net/mptcp/subflow.c | 6
net/mptcp/syncookies.c | 1
net/netfilter/Kconfig | 2
net/netfilter/ipvs/ip_vs_ftp.c | 10
net/netfilter/nf_nat_sip.c | 5
net/netfilter/nf_tables_api.c | 14
net/netfilter/nft_counter.c | 20
net/netfilter/xt_cgroup.c | 21
net/netfilter/xt_hl.c | 27
net/nfc/digital_core.c | 2
net/nfc/llcp_core.c | 29
net/nfc/llcp_sock.c | 14
net/nfc/nci/data.c | 10
net/nfc/nci/rsp.c | 41
net/openvswitch/conntrack.c | 130 +
net/openvswitch/conntrack.h | 6
net/openvswitch/datapath.c | 58
net/openvswitch/datapath.h | 26
net/openvswitch/vport.h | 9
net/phonet/pep.c | 2
net/rds/cong.c | 4
net/sched/act_skbmod.c | 12
net/sched/cls_api.c | 18
net/sched/sch_api.c | 7
net/sched/sch_codel.c | 2
net/sched/sch_fq_codel.c | 6
net/sched/sch_fq_pie.c | 3
net/sched/sch_generic.c | 2
net/sched/sch_hhf.c | 4
net/sched/sch_htb.c | 7
net/sched/sch_sfq.c | 3
net/sched/sch_teql.c | 1
net/sctp/inqueue.c | 7
net/sctp/sm_sideeffect.c | 4
net/sctp/stream.c | 48
net/smc/af_smc.c | 11
net/smc/smc_core.c | 27
net/smc/smc_llc.c | 3
net/smc/smc_rx.c | 11
net/smc/smc_tx.h | 6
net/sunrpc/auth_gss/auth_gss.c | 6
net/sunrpc/auth_gss/gss_krb5_wrap.c | 13
net/sunrpc/auth_gss/gss_rpc_upcall.c | 6
net/sunrpc/auth_gss/gss_rpc_upcall.h | 1
net/sunrpc/auth_gss/svcauth_gss.c | 8
net/sunrpc/cache.c | 7
net/sunrpc/rpcb_clnt.c | 4
net/sunrpc/sunrpc_syms.c | 1
net/sunrpc/svc.c | 74
net/sunrpc/xdr.c | 2
net/sunrpc/xprtrdma/svc_rdma_rw.c | 9
net/sunrpc/xprtsock.c | 32
net/tls/tls_strp.c | 16
net/vmw_vsock/af_vsock.c | 6
net/vmw_vsock/virtio_transport.c | 2
net/xdp/xsk.c | 46
net/xdp/xsk_buff_pool.c | 4
net/xdp/xsk_queue.h | 16
net/xfrm/xfrm_output.c | 4
samples/ftrace/ftrace-direct-modify.c | 12
samples/ftrace/ftrace-direct-multi-modify.c | 12
scripts/mod/modpost.c | 6
scripts/tags.sh | 2
security/apparmor/lib.c | 20
security/integrity/ima/ima_appraise.c | 2
security/landlock/errata/abi-1.h | 23
security/landlock/fs.c | 40
security/smack/smack.h | 5
security/smack/smack_lsm.c | 67
security/smack/smackfs.c | 218 --
sound/core/control.c | 464 ++----
sound/core/control_compat.c | 87 -
sound/core/control_led.c | 9
sound/core/init.c | 4
sound/core/pcm_native.c | 2
sound/core/seq/seq_midi.c | 61
sound/core/ump_convert.c | 1
sound/drivers/aloop.c | 104 -
sound/drivers/mpu401/mpu401.c | 6
sound/drivers/mts64.c | 6
sound/drivers/portman2x4.c | 6
sound/drivers/serial-u16550.c | 6
sound/drivers/virmidi.c | 6
sound/parisc/harmony.c | 6
sound/pci/asihpi/hpi6000.c | 5
sound/pci/hda/hda_proc.c | 2
sound/pci/ice1712/ice1712.c | 7
sound/pci/pcxhr/pcxhr.c | 14
sound/pci/via82xx_modem.c | 26
sound/soc/amd/yc/acp6x-mach.c | 7
sound/soc/apple/mca.c | 6
sound/soc/codecs/cs35l33.c | 14
sound/soc/codecs/cs35l34.c | 14
sound/soc/codecs/rt700-sdw.c | 6
sound/soc/fsl/fsl-asoc-card.c | 4
sound/soc/fsl/mpc5200_psc_i2s.c | 1
sound/soc/intel/atom/sst/sst_pci.c | 4
sound/soc/meson/meson-card-utils.c | 17
sound/soc/pxa/pxa-ssp.c | 7
sound/soc/qcom/qdsp6/q6apm.c | 12
sound/soc/samsung/aries_wm8994.c | 1
sound/soc/soc-dapm.c | 2
sound/soc/sof/topology.c | 18
sound/soc/xilinx/xlnx_formatter_pcm.c | 14
sound/usb/6fire/comm.c | 9
sound/usb/bcd2000/bcd2000.c | 11
sound/usb/midi.c | 2
tools/bpf/bpftool/map.c | 16
tools/bpf/bpftool/struct_ops.c | 4
tools/build/Makefile.feature | 3
tools/build/feature/Makefile | 3
tools/include/linux/compiler.h | 2
tools/include/nolibc/arch-powerpc.h | 2
tools/lib/bpf/libbpf.c | 3
tools/perf/builtin-sched.c | 40
tools/perf/builtin-stat.c | 9
tools/perf/pmu-events/jevents.py | 5
tools/perf/pmu-events/metric.py | 6
tools/perf/tests/shell/lib/perf_metric_validation.py | 238 +--
tools/perf/tests/shell/stat_all_metrics.sh | 151 +-
tools/perf/tests/shell/stat_bpf_counters.sh | 101 -
tools/perf/tests/shell/stat_metrics_values.sh | 11
tools/perf/ui/browsers/hists.c | 6
tools/perf/util/auxtrace.c | 15
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 21
tools/perf/util/cs-etm.c | 587 +++++--
tools/perf/util/cs-etm.h | 8
tools/perf/util/data-convert-json.c | 1
tools/perf/util/dso.c | 19
tools/perf/util/intel-bts.c | 2
tools/perf/util/intel-pt.c | 2
tools/perf/util/machine.c | 37
tools/perf/util/metricgroup.c | 7
tools/perf/util/synthetic-events.c | 6
tools/perf/util/thread-stack.c | 2
tools/perf/util/trace-event-read.c | 33
tools/scripts/Makefile.include | 38
tools/testing/kunit/kunit.py | 2
tools/testing/kunit/kunit_tool_test.py | 12
tools/testing/selftests/arm64/mte/check_buffer_fill.c | 2
tools/testing/selftests/arm64/mte/check_child_memory.c | 2
tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c | 1
tools/testing/selftests/arm64/mte/check_hugetlb_options.c | 298 +++
tools/testing/selftests/arm64/mte/check_ksm_options.c | 31
tools/testing/selftests/arm64/mte/check_mmap_options.c | 2
tools/testing/selftests/arm64/mte/check_tags_inclusion.c | 2
tools/testing/selftests/arm64/mte/check_user_mem.c | 2
tools/testing/selftests/bpf/README.rst | 32
tools/testing/selftests/bpf/prog_tests/global_map_resize.c | 8
tools/testing/selftests/bpf/test_progs.c | 11
tools/testing/selftests/bpf/test_sockmap.c | 3
tools/testing/selftests/bpf/veristat.c | 76 -
tools/testing/selftests/bpf/vmtest.sh | 71
tools/testing/selftests/mm/Makefile | 4
tools/testing/selftests/mm/cow.c | 547 ++++---
tools/testing/selftests/mm/khugepaged.c | 417 +----
tools/testing/selftests/mm/ksm_tests.c | 210 +-
tools/testing/selftests/mm/migration.c | 107 +
tools/testing/selftests/mm/run_vmtests.sh | 2
tools/testing/selftests/mm/soft-dirty.c | 9
tools/testing/selftests/mm/thp_settings.c | 394 +++++
tools/testing/selftests/mm/thp_settings.h | 89 +
tools/testing/selftests/rseq/rseq-x86-thread-pointer.h | 4
tools/testing/selftests/timers/leap-a-day.c | 19
tools/testing/selftests/zram/zram_lib.sh | 2
1183 files changed, 17639 insertions(+), 8298 deletions(-)
Abdun Nihaal (4):
wifi: brcmfmac: Fix memory leak in brcmf_sdio_read_control()
wifi: rtw88: Fix potential memory leak in rtw_txq_push_skb()
wifi: rtlwifi: pci: fix error path in rtl_pci_probe()
misc: vmc_vmci: Fix potential memory leak in vmci_event_subscribe()
Abdurrahman Hussain (1):
of: fix out-of-bounds read in of_alias_scan() stem parser
Ada Couprie Diaz (1):
arm64: hibernate: mask DAIF before restoring hibernated kernel
Adam Young (1):
mailbox/pcc: support mailbox management of the shared buffer
Adrian Hunter (5):
i3c: master: Fix info leak and UAF in device unregister path
i3c: master: Fix device_register() error path
i3c: mipi-i3c-hci: Quieten initialization messages
i3c: mipi-i3c-hci: Switch PIO data allocation to devm_kzalloc()
i3c: mipi-i3c-hci: Refactor PIO register initialization
Ahmad Byagowi (1):
i2c: mux: Fix channel node leak on adapter add failure
Ahmet Sezgin Duran (1):
staging: sm750fb: gate dualview dataflow using g_dualview
Ai Chao (1):
HID: i2c-hid: Fix "(null)" output when reading report descriptor fails
Akari Tsuyukusa (2):
iommu/mediatek-v1: Fix off-by-one in MT2701_LARB_NR_MAX
clk: mediatek: mt8135: Fix inverted gate control for devapc_ck
Akif (1):
ksmbd: fix use-after-free in smb2_open during durable reconnect
Al Viro (1):
parse_longname(): strrchr() expects NUL-terminated string
Alban Bedel (1):
software node: Fix software_node_get_reference_args() with index -1
Aleksandr Khromov (1):
slip: fix use-after-free in sl_sync()
Aleksandr Nogikh (4):
usb: gadget: midi2: Fix null-pointer dereference in f_midi2_free_ep_reqs
udf: Mark LVID buffer as uptodate before marking it dirty
ALSA: core: Fix use-after-free in snd_card_do_free()
libceph: validate banner payload length
Alexander A. Klimov (2):
clk: moxart: remove unused variables, fix refcount leak
staging: greybus: audio: correct sscanf() return value check
Alexandra Winter (1):
net/iucv: filter frames in afiucv_hs_rcv() by ingress device
Alexey Charkov (1):
clk: rockchip: Fix the fractional part denominator on RK3588/RK3576 PLLs
Ali Ahmet Memis (2):
openrisc: fix arbitrary kernel memory access via or1k_atomic syscall
Bluetooth: MSFT: validate evt_prefix_len against the response length
Alice Mikityanska (1):
virtio-net: Ensure that TCP packets don't overflow gso_segs
Alison Schofield (2):
cxl/pmem: Format the nvdimm serial number as unsigned decimal
cxl/region: Fix use-after-free in find_pos_and_ways() error path
Alistair Francis (1):
nvme: Add the DHCHAP maximum HD IDs
Allison Henderson (2):
RDMA/rxe: Avoid reprocessing the current packet after the QP enters the error state
net/rds: use wq_has_sleeper() in rds_cong_map_updated()
Andre Przywara (1):
ARM: dts: allwinner: a10: Fix PMU interrupt
Andreas Kemnade (1):
drm/omap: dsi: Do not copy isr table
Andreas Schwab (1):
m68k: nfcon: Do not call console_is_registered() in nfcon_device()
Andrew Halaney (1):
scsi: ufs: core: Remove unnecessary wmb() after ringing doorbell
Andy Shevchenko (2):
usb: f_mass_storage: Bump local buffer size in fsg_common_create_luns()
pinctrl: mediatek: Use C99 initializers in PINCTRL_PIN_GROUP()
Aneesh Kumar K.V (Arm) (2):
iommu/dma: Check atomic pool allocation result directly
swiotlb: Preserve allocation virtual address for dynamic pools
Anilkumar Kolli (1):
wifi: ath11k: add firmware-2.bin support
Anirudh Prasad (1):
ACPI: pfr_update: fix stack buffer overflow in query_capability()
Anthony Krowiak (8):
s390/vfio-ap: fix stale pqap_hook pointer on error in vfio_ap_mdev_set_kvm()
s390/vfio-ap: Fix stale do_remove flag across iterations in vfio_ap_mdev_cfg_remove
s390/vfio-ap: Fix control domain removal in vfio_ap_mdev_cfg_remove
s390/vfio-ap: Fix dereference matrix_mdev->kvm without checking for NULL
s390/vfio-ap: Fix hot-unplug skipped when last AP adapter or domain removed
s390/vfio-ap: Fix NULL deref in status_show() during queue probe
s390/vfio-ap: fix potential use of uninitialized apm_filtered bitmap
s390/vfio-ap: Fix required lock not held during update of ap_matrix_mdev object
Anton Bambura (1):
arm64: dts: qcom: sc8180x-lenovo-flex-5g: move pinctrl to appropriate nodes
Anton Danilov (1):
ipip: fix skb leak in collect_md mode when metadata_dst allocation fails
Anup Vishwakarma (1):
mailbox: qcom-ipcc: fix duplicate channel allocation across holes
Arash Golgol (1):
media: vimc: fix pixel format lookup in enum_framesizes
Ard Biesheuvel (1):
efivarfs: Rate limit statfs() handler
Armin Wolf (2):
platform/x86: dell-privacy: Fix race condition
platform/x86: dell-wmi-base: Fix resource leak on module load failure
Arnaldo Carvalho de Melo (15):
perf sched: Fix register_pid() overflow, strcpy, and BUG_ON
perf machine: Fix NULL parent dereference in fork event processing
perf machine: Guard against NULL strlist in machines__findnew()
perf machine: Use snprintf() for guestmount path construction
perf machine: Check snprintf truncation in machines__findnew()
perf machine: Don't abort guest map creation on first inaccessible dir
perf machine: Reset errno before strtol in guest kernel map creation
perf machine: Free scandir entries in guest kernel map creation
perf machine: Check snprintf truncation for guest kallsyms path
perf thread-stack: Fix heap buffer overflow on branch stack wrap copy
perf auxtrace: Fix queue grow overflow and old array leak
perf intel-pt: Fix off-by-one in auxtrace_info minimum size check
perf intel-bts: Fix off-by-one in auxtrace_info minimum size check
perf dso: Guard close() against invalid fd in dso__decompress_kmodule_path()
perf dso: Guard against cache underflow on short reads in dso_cache__memcpy()
Arnaud Bonnet (2):
nfs: replace atomic bitops sequence with clear_and_wake_up_bit helper
nfs: refactor pNFS functions using clear_and_wake_up_bit
Arthur Gautier (1):
xhci: fix lost bounce buffers on TDs spanning several ring segments
Asad Kamal (2):
drm/amdgpu/pm/powerplay: bounds-check voltage index in SMU7 lookup
drm/amdgpu/pm/powerplay: bounds-check voltage index in Vega10 lookup
Asim Viladi Oglu Manizada (1):
net: tun: bound receive headroom
Avraham Stern (3):
wifi: iwlwifi: mei: add support for SAP version 4
wifi: iwlwifi: mei: check SAP message length before reading it
wifi: iwlwifi: mei: pass correct argument to function
Avri Altman (1):
scsi: ufs: core: Remove redundant host_lock calls around UTMRLDBR
Ayush Mukkanwar (4):
staging: octeon: add missing tasklet_kill in cvm_oct_tx_shutdown
staging: octeon: ethernet-mem: replace pr_warn with dev_warn in free functions
staging: octeon: replace pr_warn with dev_warn in fill and rx paths
staging: octeon: add missing napi_disable in cvm_oct_rx_shutdown
Babanpreet Singh (3):
power: supply: sbs-battery: Use a per-device serial number buffer
w1: ds2482: Fix signedness bug in ds2482_w1_triplet()
spi: sprd-adi: Fix probe succeeding without registering the controller
Baochen Qiang (1):
wifi: ath11k: cap out-of-range rx MCS instead of leaving bogus rate
Baokun Li (3):
ext4: drain in-flight DIO before buffered write fallback
cuse: wait for pending RCU callbacks on module exit
fuse: check for NULL root inode in fuse_fill_super_submount
Baolin Wang (1):
selftests: mm: support shmem mTHP collapse testing
Bart Van Assche (1):
scsi: ufs: core: Set task state before io_schedule_timeout()
Bartosz Golaszewski (1):
powerpc/powermac: fix OF node refcount
Bastien Curutchet (2):
spi: davinci: Unset POWERDOWN bit when releasing resources
pps: clients: gpio: Bypass edge's direction check when not needed
Baul Lee (3):
ALSA: 6fire: bound the MIDI event length from the device
ALSA: bcd2000: clear the URB pointers on disconnect
vxlan: mdb: Fix use-after-free in vxlan_mdb_flush()
Besar Wicaksono (1):
perf cs-etm: Fix incorrect or missing decoder for raw trace
Bijan Tabatabai (1):
x86/pkeys: Fix pkey_alloc() return value when pkeys are not supported
Biju Das (2):
usb: renesas_usbhs: Fix power-off ordering on unbind
irqchip/renesas-rzg2l: Fix loss of interrupt
Biren Pandya (4):
media: i2c: ov02a10: fix endpoint parsing use-after-free
media: i2c: ov7740: fix use-after-destroy in remove
media: v4l2-fwnode: Fix fwnode leak in v4l2_fwnode_parse_link
media: i2c: rdacm21: Fix missing media_entity_cleanup()
Bjorn Andersson (4):
arm64: dts: qcom: sc8180x: Enable the power key
usb: typec: ucsi: unregister debugfs entries on teardown
remoteproc: Allow shutdown of crashed processors
remoteproc: Prevent crash handling to race with rproc_del()
Bradley Morgan (5):
signal: avoid shared siginfo namespace rewrites
taskstats: fix cpumask parsing cutting off the last character
ima: Check for ERR_PTR from dentry_path() in validate_hash_algo()
lib/string: fix memchr_inv() for large ranges
prctl: fix PR_SET_MM_AUXV losing the forced AT_NULL terminator
Breno Leitao (8):
mm/migrate: report RCU-tasks quiescent states in migrate_pages_batch()
mm/vmscan: report RCU-tasks quiescent states in shrink_lruvec()
net: fix spurious TX timeout after dev_activate()
efi: fix stale reference to efi_recover_from_page_fault()
cacheinfo: don't propagate DT/ACPI error when arch supplies info (arm64)
phonet: pep: do not write beyond optlen in getsockopt
nfc: llcp: avoid userspace overflow on invalid optlen
nfc: llcp: read llcp_sock->local under the socket lock in getsockopt
Brian Masney (2):
clk: nuvoton: ma35d1-pll: convert from round_rate() to determine_rate()
clk: imx: scu: drop redundant init.ops variable assignment
Brian Witte (1):
netfilter: nft_counter: serialize reset with spinlock
Bruce Johnston (1):
md/raid1: don't set array_frozen in raid1_takeover()
Bryam Vargas (12):
dm array: reject an array block whose value size is not the caller's
libnvdimm/labels: Prevent integer overflow in __nd_label_validate()
ocfs2: bound namelen in dlm_migrate_request_handler
ocfs2: validate lengths in dlm_mig_lockres_handler
power: supply: cros_usbpd-charger: bound the EC-reported port count
platform/chrome: sensorhub: Bound the EC-reported sensor number
net/smc: do not dereference an unset send buffer on the SMC-D teardown path
net/smc: unregister the connection before draining the rx tasklet
nvdimm/btt: reject an arena whose nfree is below the lane count
media: cx231xx: reject geometry changes while the VBI queue is busy
greybus: audio: bound the topology section sizes against the fetched size
libnvdimm/labels: Bound the on-media label size before the shift
Caleb Sander Mateos (1):
ublk: check for ublk_unmap_io() returning 0
Calvin Owens (2):
pps: don't try to wait for negative timeouts in PPS_FETCH
pps-gpio: remove dead capture_clear code
Can Peng (10):
thermal/drivers/imx: Disable clock on runtime resume failure
thermal/drivers/qoriq: Disable clock on resume failure
spi: bcm63xx-hsspi: disable clocks on resume failure
spi: bcm63xx: disable clock on resume failure
spi: bcmbca-hsspi: disable clocks on resume failure
iio: pressure: mpl115: Fix runtime PM cleanup
phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
drm/tve200: add OF module alias for autoloading
crypto: keembay - publish OF module alias for OCS AES/SM4
ACPI: processor: Unregister cpufreq notifier on init failure
Carlos López (1):
KVM: x86: hyper-v: Clamp stimer deadline to avoid livelock
Catalin Marinas (2):
kselftest/arm64: mte: Skip the hugetlb tests if MTE not supported on such mappings
kselftest/arm64: mte: Use the correct naming for tag check modes in check_hugetlb_options.c
Cen Zhang (2):
wifi: mac80211_hwsim: avoid NULL skb in stop queue drain
ocfs2: synchronize heartbeat callbacks with o2net teardown
Cen Zhang (Microsoft) (2):
xsk: fix NULL pointer dereference in __xsk_rcv()
gtp: add synchronize_net() in gtp_newlink() error path to prevent use-after-free
Chandrakanth Patil (1):
scsi: mpt3sas: Avoid freeing unallocated PCIe SGL buffers
Chao Huang (1):
HID: i2c-hid: goodix: Disable VDD on VDDIO enable failure
Chao Shi (1):
nvme: reject passthrough of driver-managed Set Features
Chao Yu (2):
f2fs: fix to off-by-one issue in f2fs_zero_post_eof_page()
f2fs: fix to zero post-EOF data when extending file size
Chen Cheng (1):
md/raid5: protect bitmap batch counters aka seq_flush/seq_write consistency
Chen Pei (1):
ACPI: PCI: Clear driver_data on all paths that free the acpi_pci_root
Chen, Yu C (1):
sched/fair: Fix overflow in update_tg_cfs_runnable()
Chen-Yu Tsai (2):
pinctrl: mediatek: eint: Drop base from mtk_eint_chip_write_mask()
pinctrl: mediatek: common-v1: Fix EINT breakage on older controllers
Cheng Xu (1):
RDMA/erdma: Hold CQ references when processing EQ events
Cheng-Han Wu (1):
selftests/zram: fix kernel_gte() for POSIX sh
Chengfeng Ye (2):
Bluetooth: RFCOMM: serialize security confirmation handling
vsock/virtio: flush works in dependency order
Chiara Meiohas (1):
RDMA/core: Add an option to display driver-specific QPs in the rdmatool
Chris Lu (2):
Bluetooth: btmtksdio: Take exclusive ownership of the SKB before TX
Bluetooth: btmtksdio: Fix out-of-bounds DMA read in the TX path
Chris Mason (12):
nfsd: release path refs on follow_down() error
nfsd: fix cpntf publish race in nfs4_init_cp_state
nfsd: gate nfs3 setacl by argp->mask
SUNRPC: xdr_buf_trim: clamp buf->len to avoid underflow
SUNRPC: Zero rpc_gss_wire_cred at svcauth_gss_decode_credbody() entry
SUNRPC: svcauth_gss: enforce krb5 token minimum length
SUNRPC: Guard svcauth_gss_release() dispatch on rq_auth_stat
SUNRPC: harden gss_krb5_unwrap_v2 against short tokens
SUNRPC: harden gss_unwrap_resp_priv length checks
sunrpc: init gssp_lock before publishing proc entry
svcrdma: Fix offset arithmetic in read_chunk_range
svcrdma: Fix pcl_for_each_segment for empty chunks
Christian Borntraeger (8):
KVM: s390: Fix length check __import_wp_info()
KVM: s390: Fix memory leak in guest debug handling
KVM: s390: Fix old_data leak in guest debug error path
KVM: s390: Free guest debug data on vcpu destroy
KVM: s390: Take srcu when importing watchpoint data
KVM: s390: Zero initialize irq in reinject_machine_check
KVM: s390: pv: Fix rc/rrc offset for PVM_DUMP
KVM: s390: Restore sigset on error path
Christian Hewitt (1):
media: meson: vdec: fix NULL pointer deref in vdec_try_fmt_common
Christoph Zwerschke (2):
Bluetooth: btusb: Add ASUS USB-BT540 for Realtek 8761CU
Bluetooth: btusb: Add ASUS USB-BT600 for Realtek 8761CU
Christophe JAILLET (3):
regulator: tps6594-regulator: Constify struct tps6594_regulator_irq_type
accessibility: speakup: Fix incorrect string length computation in report_char_chartab_status()
wifi: ath11k: Fix error handling in ath11k_wmi_p2p_noa_event()
Christopher Lusk (1):
smb: client: fix request buffer leak in smb2_new_read_req()
Christopher Tolang (1):
ASoC: amd: yc: Add DMI entry for MSI Thin A15 B7UC
Christos Maragkos (1):
HID: nintendo: Fix imu_timestamp_us double increment per report
Chuck Lever (8):
NFSD: Encode only the status in NFS-ACL v2 GETACL error replies
NFSD: Fix off-by-one in DRC bucket pruning limit
nfsd: Reset write verifier when async COPY writeback fails
nfsd: sample writeback error cursor before async COPY loop
nfsd: gate nfs2 setacl by argp->mask
NFSD: Prevent lock owner use-after-free during client teardown
SUNRPC: Check svc pool percpu counter allocation
SUNRPC: Reject krb5 v2 wrap tokens with oversized ec field
Chunkai Deng (1):
rpmsg: glink: smem: order FIFO read after availability check
Coly Li (1):
md: do overflow check for sb->bblog_shift in super_1_load()
Cong Nguyen (4):
hwmon: (max6621) fix negative temperature offset and crit readings
hwmon: (max6621) fix temperature clamp range
iio: gyro: mpu3050: fix sign of raw angular velocity readings
iio: srf04: fix pm_runtime handling on probe error path
Cosmo Chou (3):
leds: pca9532: Fix inverted GPIO output polarity
leds: pca9532: Fix phantom device registration on missing hardware
rtc: pcf85363: Add error checking to regmap calls in probe()
Cristian Marussi (1):
firmware: arm_scmi: Add multiple protocols registration support
Cássio Gabriel (2):
ALSA: aloop: Fix peer runtime UAF during format-change stop
ALSA: core: Add scoped cleanup helper for card references
Daisuke Matsuda (1):
fpga: altera-cvp: Avoid out-of-bounds read in trailing byte write
Damien Le Moal (1):
block: flag zoned disks with GENHD_FL_NO_PART
Dan Carpenter (5):
staging: fbtft: Use sysfs_emit_at() to print to sysfs file
thermal/drivers/rcar: Fix error checking in probe()
cpufreq: spear: Fix an IS_ERR() vs NULL bug in spear1340_set_cpu_rate()
scsi: qla2xxx: Fix an loop timeout test
pinctrl: mediatek: common-v1: Fix error checking in mtk_eint_init()
Daniel Borkmann (1):
bpf: Fix vmlinux BTF prep race in bpf_get_btf_vmlinux
Daniel McCarthy (1):
pinctrl: bcm2835: Don't remove an unregistered GPIO chip
Daniel Vogelbacher (1):
ceph: fix oops due to invalid pointer for kfree() in parse_longname()
Danila Chernetsov (2):
RDMA/hfi1: Propagate sdma_txinit_ahg() errors
fbdev: kyro: Validate overlay viewport coordinates
Danilo Krummrich (1):
drm: fix race between partial drm_dev_register() failure and ioctl
Dapeng Mi (1):
perf/x86/intel: Fix kernel address leakages in LBR stack
Dave Airlie (1):
nouveau/gem: reserve the bo in the info ioctl around the vma lookup
Dave Jiang (2):
cxl/mbox: Break poison list loop on an empty payload
cxl/pci: Honor -EPROBE_DEFER from component register setup
David (Ming Qiang) Wu (1):
drm/amdgpu/vcn: fix integer overflow in dec_msg buffer count check
David Hildenbrand (1):
selftests: mm: cow: flag vmsplice() hugetlb tests as XFAIL
David Lee (3):
udf: reject VAT indexes equal to the entry count
UDF symlink pathComponent header OOB read
nilfs2: prevent out-of-bounds read in super root block parsing
David Strahan (1):
scsi: smartpqi: Fix AIO retry marker cleared by SCSI core between dispatches.
David Woodhouse (1):
timekeeping: Account for monotonicity adjustment in ntp_error
Davidlohr Bueso (1):
perf/cxlpmu: Fix 64-bit write to 32-bit HDM filter register
Dawei Feng (2):
media: cedrus: fix memory leak in cedrus_init_ctrls()
staging: rtl8723bs: fix mismatched free of HalData in rtw_sdio_if1_init()
Dawid Wróbel (1):
arm64: dts: qcom: sm8250-xiaomi-elish: correct the board ID
Deepanshu Kartikey (4):
eventfs: Initialize ei->children and ei->list in init_ei()
drm/i915: Guard against NULL driver_data in i915_pci_probe()
drm/gud: NUL-terminate TV mode names read from the device
usb: atm: usbatm: fix invalid ci_range initialization
Delphine CC Chiu (1):
hwmon: (emc1403) Add support for EMC1442
Desnes Nunes (1):
iommu/vt-d: Fix UCTP context table slot when copying root entries
Devin Wittmayer (1):
wifi: mt76: mt76x02: do not WARN on invalid rx descriptor length
Diego Fernando Mancera Gomez (1):
media: em28xx: defer audio-only extension registration
Dmitry Antipov (3):
ocfs2: always run deallocs on copy-on-write completion
smack: simplify write handlers of sysfs entries
wifi: ath11k: refactor ath11k_wmi_tlv_parse_alloc()
Dmitry Baryshkov (3):
arm64: dts: qcom: sm8250: sort out Iris power domains
arm64: dts: qcom: sm8250: correct frequencies in the Iris OPP table
clk: qcom: gcc-qcm2290: don't park QUP RCGs upon registration
Donet Tom (1):
selftests/mm: add new test cases to the migration test
Doruk Tan Ozturk (2):
wifi: ath6kl: clamp assoc request/response lengths before subtracting IE offsets
nfc: llcp: bound SNL TLV parsing to the skb and add length checks
Dragos Tatulea (1):
net/mlx5e: xsk: Fix unlocked writing to ICOSQ
Eason Lai (1):
wifi: mt76: mt792x: Fix memory leak in SDIO TX path
Eduard Zingerman (1):
selftests/bpf: Fix for veristat file/prog filters processing
Emmanuel Grumbach (3):
wifi: iwlwifi: fix counter type in iwl_fwrt_dump_error_logs
wifi: iwlwifi: mvm: fix off-by-one in TXF key sanitiser
wifi: iwlwifi: guard against division by zero in iwl_dbg_tlv_alloc_fragments
Enzo Matsumiya (1):
smb: server: fix leak of ksmbd_ipc_login_request_ext() returned buffer
Eric Dumazet (10):
net: dst: add four helpers to annotate data-races around dst->dev
ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu]
net: dst: introduce dst->dev_rcu
ipv4: start using dst_dev_rcu()
tcp: fix potential race in tcp_v6_syn_recv_sock()
inetpeer: randomize RB-tree node comparison using SipHash
net/sched: act_skbmod: fix length calculations and avoid invalid header warnings
slip: remove slip_hangup() to fix use-after-free in slip_receive_buf()
net: hsr: init prune_proxy_timer sooner
net: hsr: must allocate more bytes for RedBox support
Erick Henrique (1):
iio: dac: m62332: Fix regulator reference count imbalance
Esteban Urrutia (1):
phy: qcom: qmp-combo: Correct pre-emphasis table for QMP v4 DP PHYs
Ethan Carter Edwards (1):
clk: ti: use kcalloc() instead of kzalloc()
Ethan Nelson-Moore (1):
ARM: 9477/1: Disable broken eBPF JIT on the Risc PC
Evgenii Burenchev (2):
ALSA: via82xx: Remove unreachable branch in snd_via686_pcm_pointer()
ALSA: hpi: Check transport errors during HPI6000 adapter initialization
Ewan D. Milne (1):
nvme: nvme-fc: Fix nvme_fc_create_hw_io_queues() queue deletion in error path
Fabio Estevam (3):
arm64: dts: rockchip: Fix rk3399-roc-pc-plus analog audio
wifi: mwifiex: Detach sync cmd buffer on interrupted wait
arm64: dts: rockchip: Fix Gru WLAN sideband interrupt
Fabio Porcedda (1):
net: usb: qmi_wwan: add Telit Cinterion FE990D50 composition
Faith Ekstrand (1):
drm/nouveau: Use write-combined maps for coherent
Fan Wu (12):
RDMA/cxgb4: Cancel reg_work before freeing device on remove
power: supply: bq24257: fix use-after-free on remove
power: supply: bq256xx: drain usb_work before freeing the charger
power: supply: lp8727: fix use-after-free in lp8727_release_irq()
power: supply: rt9455: quiesce delayed work before teardown
power: supply: ucs1002: fix use-after-free on remove
mmc: via-sdmmc: stop card-detect handling on probe failure
usb: typec: qcom-pmic: cancel reset_work on stop
iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF
media: cx23885: cancel NetUP CI work before teardown
spi: davinci: switch to managed controller allocation
RDMA/cxgb4: Free debugfs on registration failure
Fan Ye (3):
net: thunderbolt: Release the Rx HopID that was handed out on mismatch
net: thunderbolt: Mark the connection down when bringing it up fails
net: thunderbolt: Count delivered packets in rx_packets and rx_bytes
Farhan Ali (1):
PCI/MSI: Enable memory decoding before restoring MSI-X messages
Fei Shao (1):
usb: mtu3: allow system suspend during active gadget connection
Felix Fietkau (14):
wifi: mt76: mt7996: set MT76_MCU_RESET before waking MCU waiters on full reset
wifi: mt76: mt7915: avoid nss underflow in mt7915_mcu_get_sta_nss
wifi: mt76: mt7996: don't report a zero TX bitrate
wifi: mt76: mt7915: poll the correct SLP CTRL register for the second adie
wifi: mt76: mt7996: bound TLV walk in mt7996_mcu_get_chip_config
wifi: mt76: mt7996: reserve space for the CSA-abort countdown TLV
wifi: mt76: mt7915: use little-endian for bss_info_ra wire fields
wifi: mt76: mt7915: unlink TWT flow if the MCU rejects the agreement
wifi: mt76: mt7915: fix double hif2 init on the non-WED path
wifi: mt76: mt7915: fix ext PHY use-after-free on register error path
wifi: mt76: mt7915: release hif2 reference on probe IRQ failure
wifi: mt76: mt7915: fix chainmask handling for non-dbdc phys on band 1
wifi: mt76: mt7915: report RX chain signal for all RX paths
wifi: mac80211: skip default WMM setup for AP_VLAN links
Felix Gu (3):
remoteproc: qcom_q6v5_adsp: Fix reference leak for device node
phy: sunplus: fix error handling in sp_uphy_init()
spi: img-spfi: don't disable runtime PM on DMA deferred probe
Felix Yan (1):
clocksource/drivers/timer-sun4i: Advertise a real minimum delta
Feng Yang (3):
selftests/bpf: Fix memory leak in msg_alloc_iov
selftests/bpf: Fix incorrect error checking for pthread_create
selftests/bpf: Fix memory leak on subtest_states reallocation
Florian Fuchs (1):
fbdev: pvr2fb: correct user pointer annotation and sentinel initializer
Florian Westphal (1):
netfilter: nf_nat_sip: rewind offset when NAT shrinks the packet
Francesco Lavra (1):
usb: dwc2: gadget: Exit partial power down state when changing USB pull-up
Frank Sorenson (4):
cifs: fix loff_t underflow in cifs_remap_file_range() when len == 0
smb: client: fix ALIGN() overflow in symlink_data() error context loop
smb: client: fix copy-paste error in WSL EA length accounting for $LXDEV
cifs: fix clearing stats for fastest execution of each smb2 command
Fredric Cover (2):
smb: client: clear ce->tgthint in free_tgts()
smb: client: harden DFS cache against invalid target hints
Fu Hao (1):
iommu/amd: Add support for Hygon family 18h model 4h IOAPIC
Gary Guo (1):
usb: fix UAF when probe runs concurrent to dyn ID removal
Gaurav Batra (1):
powerpc/pseries/iommu: switch to Default DMA window during kdump
Geert Uytterhoeven (3):
clk: ti: Make sure clk_init_data is fully initialized
clk: visconti: Make sure clk_init_data is fully initialized
erofs: Fix EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS default logic
Gerald Loacker (1):
phy: rockchip: phy-rockchip-inno-csidphy: fix rk1808 hsfreq table
Gerald Yang (1):
ext4: clear stale xarray tags on folios skipped during writeback
Giorgi Tchankvetadze (1):
iio: light: cm32181: return zero after writing calibscale
Giovanni Cabiddu (1):
crypto: qat - clear AES key schedule from stack
Gleb Markov (1):
misc: rtsx: add missing write register handling
Gongwei Li (1):
Bluetooth: hci_uart: Fix false success return in hci_uart_setup()
Gou Hao (1):
powerpc/smp: add NULL guard for cause_ipi in smp_muxed_ipi_message_pass
Greg Kroah-Hartman (3):
USB: make single lock for all usb dynamic id lists
USB: make to_usb_driver() use container_of_const()
Linux 6.6.157
Griffin Kroah-Hartman (1):
usb: image: mdc800: change kmalloc() to kzalloc()
Guanghui Yang (1):
ext4: fix buffer_head leak in ext4_init_orphan_info
Guangshuo Li (11):
Bluetooth: hci_bcm: fix usage_count leak when autosuspend_delay is negative
Bluetooth: hci_h5: fix usage_count leak when autosuspend_delay is negative
Bluetooth: hci_intel: fix usage_count leak when autosuspend_delay is negative
media: saa7164: fix cleanup on resource allocation failure
drm/amdgpu: fix autosuspend cleanup during removal
clk: tegra: tegra124-emc: put EMC node on register failure
rapidio: clear mport->net when rio_add_net() fails
dmaengine: qcom-bam-dma: fix autosuspend cleanup during removal
clocksource/drivers/clps711x: Do not unmap clocksource MMIO
ksmbd: Do not skip lock checks for single-byte ranges
platform/x86: hp-bioscfg: fix password encoding bounds check
Guenter Roeck (3):
hwmon: (emc1403) Convert to with_info API
hwmon: (emc1403) Support 11 bit accuracy
hwmon: (emc1403) Rely on subsystem locking
Gui-Dong Han (3):
media: cec-pin: Fix event FIFO ordering
nvme-apple: Use acquire/release for queue enabled state
misc: bcm-vk: Use acquire/release for msgq_inited
Guixin Liu (6):
nvmet-rdma: use sbitmap to replace rsp free list
nvme-fc: unmap cmd_iu DMA on rsp_iu mapping failure in init_request
nvme: add reservation command's defines
nvme: introduce change ptpl and iekey definition
nvmet: fix heap out-of-bounds read in nvmet_auth_negotiate()
nvmet: fix NULL pointer dereference in nvmet_execute_identify_ns_zns()
Guodong Xu (1):
riscv: cpufeature: Clarify ISA spec version for canonical order
Guoniu Zhou (3):
media: nxp: imx8-isi: Fix stream ID validation bypass in crossbar routing
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
Guopeng Zhang (1):
mm: memcg: stop reclaim when a limit update is superseded
Guoqing Jiang (1):
RDMA/siw: Introduce siw_free_cm_id
Günther Noack (1):
landlock: Require LANDLOCK_ACCESS_FS_MAKE_REG for whiteout creation
HE WEI(ギカク) (1):
fs/ntfs3: fix slab-out-of-bounds write in ni_create_attr_list()
Hajo Noerenberg (1):
ata: ahci: work around lost interrupts on Marvell 88SE61xx
HanQuan (1):
ecryptfs: reject oversized encrypted_key_size in parse_tag_3_packet
Hang Nan (2):
Bluetooth: ISO: fix use-after-free of listener socket in iso_conn_ready
ksmbd: bound smb_check_perm_dacl() ACE walks by DACL size
Hanlin Song (1):
csky: Fix a4/a5 restoration in syscall trace path
Hannes Reinecke (1):
nvmet-rdma: avoid circular locking dependency on install_queue()
Hans Verkuil (2):
Revert "media: v4l2-dev: fix error handling in __video_register_device()"
media: v4l2-async: Unregister sub-device if asc_list is empty
Hans de Goede (4):
HID: i2c-hid: Fold i2c_hid_execute_reset() into i2c_hid_hwreset()
HID: i2c-hid: Split i2c_hid_hwreset() in start() and finish() functions
HID: i2c-hid: Switch i2c_hid_parse() to goto style error handling
HID: i2c-hid: Move i2c_hid_finish_hwreset() to after reading the report-descriptor
Hao Chang (3):
pinctrl: mediatek: Add EINT support for multiple addresses
pinctrl: mediatek: Fix the invalid conditions
pinctrl: mediatek: Fix new design debounce issue
Hao Ge (1):
selftests/mm/cow: modify the incorrect checking parameters
Hao-Qun Huang (4):
staging: greybus: hid: fix SET_REPORT return value
staging: media: tegra-video: fix of_node_put() on VIP parse errors
staging: media: tegra-video: vi: fix probe failure on skipped last port
f2fs: reject overlapping move range after len expansion
Haotian Zhang (4):
dm-switch: use WRITE_ONCE() in switch_region_table_write()
ALSA: ice1712: Fix the card leak at probe error with the auto-cleanup
samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify
samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify
Haowen Tu (1):
PM: sleep: Fix off-by-one in wakelocks number limit check
Haoxiang Li (4):
HID: sensor: custom: Fix field sysfs group cleanup on failure
ASoC: fsl: mpc5200-i2s: Free DMA resources on probe failure
AsoC: intel: sst: fix PCI device reference leak on probe failure
iommu/qcom: Remove sysfs device on probe failure path
Hari Mishal (1):
drm/amd/display: fix division by zero in get_estimated_bw()
Harry Wentland (1):
drm/amd/display: avoid divide-by-zero in __is_lut_linear()
Harshit Varu (1):
mptcp: fix uninitialized local_id in syncookie MP_JOIN reconstruction
Heiko Stuebner (1):
clk: rockchip: rk3588: Don't change PLL rates when setting dclk_vop2_src
Helge Deller (2):
sticon/parisc: Detect default STI graphics card for console output
parisc: Fix alignment of asm statements in head.S
Henrik Grimler (4):
drm/lima: call drm_mm_init() with a valid allocation range
power: supply: bq27xxx: bq27520g4: fix REG_TTES address
power: supply: bq27xxx: bq28z610: fix invalid AverageEnergy address
power: supply: bq27xxx: bq27z561: fix invalid AverageEnergy address
Herman van Hazendonk (3):
clk: qcom: gdsc: propagate gdsc_check_status() errors from gdsc_poll_status
clk: qcom: gdsc: propagate gdsc_enable() failure for ALWAYS_ON domains
clk: qcom: gdsc: tear down per-domain genpds in gdsc_unregister()
Herve Codina (7):
irqdomain: Introduce irq_domain_free()
irqdomain: Introduce irq_domain_instantiate()
irqdomain: Handle additional domain flags in irq_domain_instantiate()
irqdomain: Handle domain hierarchy parent in irq_domain_instantiate()
irqdomain: Introduce init() and exit() hooks
genirq/generic_chip: Introduce irq_domain_{alloc,remove}_generic_chips()
irqdomain: Add support for generic irq chips creation before publishing a domain
Hidayath Khan (2):
net/smc: fix socket refcount leak in smc_switch_conns()
net/smc: fix use-after-free in smc_rx_pipe_buf_release()
Hisam Mehboob (1):
selftests/rseq: Replace glibc-specific __GNUC_PREREQ with portable check
Hongyan Xu (8):
auxdisplay: charlcd: cancel backlight work on registration failure
usb: gadget: r8a66597: avoid double free of ep0_req in probe error path
platform/chrome: cros_ec_debugfs: Clean up console log on probe failure
platform/chrome: cros_ec_debugfs: Unregister panic notifier
power: supply: isp1704_charger: cancel work on remove
power: supply: sc2731_charger: cancel work on remove
ACPI: scan: fix bus ID cleanup on device_add() failures
block: mtip32xx: synchronize ioctls with device removal
Huang Wei (1):
usb: typec: ucsi: use UCSI_TIMEOUT_MS for sync command completion
Hui Su (5):
tracing: Fix crash passing ERR_PTR to kthread_stop()
tracing: Fix use-after-free with same-name named triggers
fbdev: ssd1307fb: defer I2C transfers from damage callbacks
cpufreq: schedutil: Fix rate limit overflow
kasan: fix cache shrink race with CPU hotplug
Huisong Li (5):
ACPI: processor: idle: Optimize ACPI idle driver registration
mailbox: pcc: Fix command timeout due to missed interrupt
ACPI: processor: idle: Remove redundant cstate check in acpi_processor_power_init
ACPI: processor: idle: Move max_cstate update out of the loop
ACPI: processor: idle: Remove redundant static variable and rename cstate check function
HyeongJun An (11):
Bluetooth: eir: Fix OOB read in eir_get_service_data()
ALSA: ump: Fix corrupted data bytes at MIDI 1.0 SysEx to UMP conversion
power: supply: qcom_battmgr: terminate the strings from firmware
platform/x86: dell-wmi-sysman: Don't hex dump attribute security buffer
platform/x86: ISST: Validate level in perf mask ioctls
platform/x86: ISST: Validate socket ID in clos_assoc ioctl
ALSA: usb-audio: fix OOB write in snd_usbmidi_us122l_output()
media: s2255: bound JPEG frame size before copying into the buffer
Bluetooth: virtio_bt: avoid OOB read of build info string
platform/x86: dell-wmi-sysman: Fix instance ID bounds
ASoC: dapm: Fix off-by-one check on the second enum channel
Hyunwoo Kim (3):
sctp: stop processing a packet once its association is deleted
sctp: drop a chunk if its transport was removed
Bluetooth: RFCOMM: Validate MTU in rfcomm_apply_pn() to prevent infinite loop
Ian Rogers (12):
perf test stat_all_metrics: Ensure missing events fail test
perf tests metrics: Permission related fixes
perf test metrics: Update all metrics for possibly failing default metrics
perf test all metrics: Fully ignore Default metric failures
perf tests: Skip metrics validation if system-wide recording lacks permission
perf test bpf-counters: Add test for BPF event modifier
perf tests: Fix flakiness in BPF counters test on hybrid systems
perf jevents: Add more components to the metric sorting order
perf ui hists: Fix uninitialized stack memory free on pstack allocation failure
perf stat: Fix evsel_list leak in cmd_stat
perf synthetic-events: Fix uninitialized pthread_join
perf synthetic-events: Fix divide by zero in perf_event__synthesize_threads
Ibrahim Abdelkader (1):
Bluetooth: hci_sync: Clear HCI_CMD_PENDING when dropping the last request
Ibrahim Hashimov (5):
ubifs: fix out-of-bounds read in signature length check
HID: picolcd: clamp eeprom debugfs read to bytes actually received
ocfs2: validate rl_used against rl_count in refcount block validator
HID: wacom: validate report length in wacom_intuos_pro2_bt_irq
RDMA/rxe: Validate num_sge/cur_sge before indexing wqe->dma.sge[]
Ido Schimmel (8):
vxlan: mdb: Adjust function arguments
vxlan: mdb: Factor out a helper for remote entry size calculation
bridge: add MDB get uAPI attributes
net: Add MDB get device operation
vxlan: mdb: Add MDB get support
bridge: add MDB state mask uAPI attribute
net: Add MDB bulk deletion device operation
vxlan: mdb: Add MDB bulk deletion support
Ilya Krutskih (2):
dm raid1: reserve space for NUL-terminator in build_constructor_string()
media: tda18250: fix possible integer overflow
Ilya Maximets (2):
net: openvswitch: fix flow mask use-after-free on flow deletion
net: openvswitch: fix kernel-doc warnings in internal headers
Ismail Tarim (1):
Bluetooth: btmtk: Do not discard the subsystem reset timeout
Israel Téllez García (1):
bpf: Fix pending_pos walk on 32-bit ring position wrap
Itai Handler (1):
rcu: Mark accesses to ->rcu_urgent_qs and ->rcu_need_heavy_qs
Ivaylo Dimitrov (1):
hsi: omap_ssi_core: fix missing DMA mask setup for SSI controller device
Ivy Lopez (2):
usb: gadget: f_midi2: fix use-after-free in string attribute show path
staging: rtl8723bs: use kfree_sensitive() for key material
Jakub Kicinski (1):
i3c: dw: avoid shift-out-of-bounds when DAA assigns no devices
Jamal Hadi Salim (7):
net/sched: fq_codel: clamp default quantum and mtu
net/sched: sch_codel: clamp default mtu to avoid disabling CoDel
net/sched: fq_pie: clamp default quantum to avoid signed overflow
net/sched: hhf: clamp quantum before hhf_change() to avoid overflow
net/sched: sfq: clamp quantum to avoid signed overflow soft lockup
net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
net/sched: sch_htb: limit htb_classify inner-class filter hops
James Calligeros (1):
ASoC: apple: mca: increase SERDES reset delay
James Clark (4):
perf cs-etm: Create decoders after both AUX and HW_ID search passes
perf cs-etm: Queue context packets for frontend
tools/build: Allow versioning of all LLVM tools defined in Makefile.include
coresight: Change syncfreq to be a u8
James Kim (1):
rapidio: mport_cdev: fix use-after-free in dma_req_free()
Jameson Thies (2):
power: supply: cros_usbpd: Limit port counts to EC_USB_PD_MAX_PORTS
usb: typec: ucsi: displayport: Fix OOB altmode array index
Jan Kara (1):
ext4: fix spurious message about orphan cleanup on RO fs
Jann Horn (1):
smack: fix cred UAF in smack_file_send_sigiotask()
Jarkko Nikula (2):
i3c: mipi-i3c-hci: Fix DAT/DCT entry sizes
i3c: mipi-i3c-hci: Fix number of DAT/DCT entries for HCI versions < 1.1
Jason Gunthorpe (1):
RDMA/nldev: Fix locking when accessing mr->pd
Jeff Johnson (7):
wifi: ath6kl: avoid buffer overreads in WMI event handlers
wifi: ath12k: Correctly copy the hint BSSID in WMI scan request
wifi: ath11k: Correctly copy the hint BSSID in WMI scan request
wifi: ath12k: Avoid buffer overread in ath12k_wmi_op_rx()
wifi: ath11k: Avoid buffer overread in ath11k_wmi_tlv_op_rx()
wifi: ath11k: fix overreads in ath11k_wmi_process_csa_switch_count_event()
wifi: ath11k: fix leak in ath11k_service_ready_ext_event()
Jeff Layton (16):
nfsd: return NFS4ERR_NOTSUPP for unsupported netloc4 types
nfsd: validate symlink target length in NFSv4 CREATE
nfsd: add fh_want_write() for early-verified SETATTR in nfsd_proc_setattr()
nfsd: add filehandle match check to nfsd4_delegreturn()
nfsd: block non-SAVEFH ops after FOREIGN PUTFH to prevent NULL deref
nfsd: check client ownership when cancelling a copy-notify stateid
nfsd: fix version mismatch loops in nfsd_acl_init_request()
nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget
nfsd: fix XDR padding calculation in ff_encode_getdeviceinfo
nfsd: initialize copy-notify stateid before publishing it
nfsd: reject reclaim LOCK after RECLAIM_COMPLETE
nfsd: revoke copy-notify stateids before dropping their reference
sunrpc: route to a populated pool in svc_pool_for_cpu()
SUNRPC: always drain cache_cleaner before destroying a cache_detail
fuse: convert to new timestamp accessors
nfs: move the nfs4_data_server_cache into struct nfs_net
Jeffin Philip (2):
usb: gadget: uvc: fix dangling pointers in uvc_function_bind() and uvc_function_unbind()
usb: gadget: fix null pointer dereference in usb_put_function_instance()
Jens Remus (1):
x86/cfi: Use symmetric SYM_START and SYM_END in __CFI_TYPE()
Jernej Skrabec (7):
drm/sun4i: vi scaler: Fix coefficient selection
drm/sun4i: tcon: Set output mux for DSI and LVDS
drm/sun4i: tcon: Drop TCON TOP device reference
drm/sun4i: crtc: Propagate layer initialization error
drm/sun4i: tcon: Drop remote endpoint reference
drm/sun4i: dw-hdmi: Drop TCON TOP port reference
drm/sun4i: hdmi-phy: Fix H6 8-bit MPLL config at 594 MHz
Jian Shen (1):
net: hns3: don't auto enable misc vector
Jian-Ming Liao (1):
i3c: mipi-i3c-hci: Fix missing STAT_IBI_STATUS_THLD in PIO mode
Jiancheng Huang (1):
HID: lg4ff: validate report length before fixed offsets
Jiangong.Han (1):
media: em28xx: fix use-after-free of dev_next->devlist on disconnect
Jiangshan Yi (4):
HID: mcp2221: stop device IO before hid_hw_stop
HID: mcp2221: validate report size in mcp2221_raw_event()
bnx2x: fix double free in bnx2x_init_firmware() error path
selftests: timers: leap-a-day: Fix -w option and update usage comment
Jianing Li (2):
power: supply: max17040: drop incorrect I2C functionality check
power: supply: max17040: synchronize work cancellation on suspend
Jiawen Wu (1):
net: libwx: fix Tx L4 checksum
Jiayuan Chen (2):
bpf: Fix same-register dst/src OOB read and pointer leak in sock_ops
tcp: fix corruption of urgent data on multi-segment retransmit
Jie Wang (1):
net: hns3: fix kernel crash when 1588 is sent on HIP08 devices
Jingyi Wang (1):
remoteproc: core: Attach rproc asynchronously in rproc_add() path via schedule_work()
Jinjie Ruan (1):
entry: Fix seccomp bypass after ptrace with TSYNC
Jiri Slaby (SUSE) (1):
tty: ipoctal: convert to u8 and size_t
Joanne Koong (2):
fuse: support folios in struct fuse_args_pages and fuse_copy_pages()
fuse: convert readdir to use folios
Joas Antonio dos Santos (1):
ipvs: fix integer overflow in ftp helper port/address parsing
Joey Lu (3):
clk: nuvoton: ma35d1: fix ignored div_u64 return values in PLL freq calculation
clk: nuvoton: ma35d1: fix PLL_CTL1_FRAC bit field width and fractional calc
clk: nuvoton: ma35d1: fix ma35d1_clk_pll_determine_rate logic
Johan Hovold (5):
USB: phy: fsl-usb: fix missing static keywords
remoteproc: scp: Fix device reference leak on failed lookup
drm/bridge: dw-hdmi: fix i2c adapter leak on probe failure
drm/panel-edp: fix i2c adapter leak on probe failure
Bluetooth: btusb: refactor endpoint lookup
John Groves (1):
dax: read holder_ops once in dax_holder_notify_failure()
John Johansen (1):
apparmor: policy_int make sure list heads are initialized before fail path
John Keeping (1):
ALSA: seq: midi: Serialize input teardown with event_input
Jonas Karlman (1):
media: cec: core: Fix kmemleak due to missed rc_free_device() call
Jonathan Currier (1):
net/niu: Niu requires MSIX ENTRY_DATA fields touch before entry reads
Jordan R Abrahams-Whitehead (1):
include/linux/list.h: mark list_add and __list_add as __always_inline
Jorijn van der Graaf (1):
ASoC: qcom: q6apm: keep the graph start count in sync with the DSP
Joseph Qi (3):
ocfs2: cluster: don't sleep while holding o2hb_live_lock in o2hb_region_pin()
ocfs2: cluster: avoid lock order inversion in o2hb_region_pin() from drop_item
ocfs2: cluster: fix o2hb_dependent_users leak on pin failure
Joshua Crofts (2):
usb: gadget: midi2: remove default configfs groups on teardown
nilfs2: fix infinite loop in nilfs_clean_segments()
Joy H.J. Lee (1):
tools/compiler: match glibc 2.42 definition of __attribute_const__
Joy Zou (1):
regulator: core: use system_freezable_wq for init complete work
Jun Yan (1):
arm64: dts: amlogic: meson-axg-s400: enable mipi_pcie_analog_dphy for PCIe
Jun Yang (2):
sctp: distinguish sequence zero from wildcard in reconf lookup
sctp: fix stream->outcnt underflow on duplicate RECONF responses
Junrui Luo (4):
media: vicodec: fix out-of-bounds write in FWHT encoder
NFSv4.1: fix layout segment leak on the pnfs_layout_process() forget path
NFSv4.2: fix LAYOUTSTATS send buffer exhaustion
NFSv4/pnfs: key the data server cache on the NFS version
Junseo Lim (3):
lwt_bpf: Restore reserved headroom after xmit program
bpf: Reject negative optlen in cgroup getsockopt hook
net: kcm: Hold RCU read lock while running BPF parser
Junyi Liu (1):
ksmbd: fix durable reconnect error path file lifetime
Justin Yeh (1):
pinctrl: mediatek: free EINT resources on unbind
Jérémy Jean (8):
timers/itimer: Zero-init old itimerval before copy to userspace
tracing/user_events: Clear copied tracing state before fork duplication
libceph: reject buckets with mismatched CRUSH ids
ceph: reject export_targets ranks >= CEPH_MAX_MDS in mdsmap decode
audit: avoid dropping live tree ref on fsnotify rule autoremove
bpf: Harden bloom filter sizing and indexing on 32-bit kernels
SUNRPC: wait for in-flight client TLS handshake callback
LoongArch: Avoid preempt count underflow without probe
Kalesh AP (1):
RDMA/restrack: Fix typos in the comments
Kalle Valo (1):
wifi: ath11k: qmi: refactor ath11k_qmi_m3_load()
Kamal Wadhwa (1):
regulator: qcom-rpmh: Fix PMIC5 BOB bypass mode handling
Kang Yang (1):
wifi: ath11k: implement handling of P2P NoA event
Kanishka De Silva (1):
ublk: clear VM_MAYWRITE on read-only ublk char device mmap
Karl Mehltretter (13):
Revert "arm64: mm: Don't remap pgtables for allocate vs populate"
arm64: compat: Fix decrementing LDM/STM alignment emulation
cgroup/cpuset: Fix misplaced DL migration reset
arm64: mm: Fix the lockless page-table walk in show_pte()
tty: hvc: restrict HVC_DCC to ARMv6+ and ARM64
ARM: lpc32xx: only run SoC init on LPC32xx hardware
serial: amba-pl011: unprepare console clock on unregister
tty: clear cdev pointer after cdev_add() failure
cpufreq: imx6q: fix devres accumulation across driver rebind
cpufreq: imx6q: fix out-of-bounds write when probed more than once
m68k: Fix backtraces for non-running tasks
octeontx2-af: initialize lmac_bmap in rvu_mcs_set_lmac_bmap()
8139cp: fix Rx and Tx not being disabled in cp_suspend
Kathiravan Thirumoorthy (1):
regulator: qcom-refgen: correct the regulator type to CURRENT
Kavan Smith (1):
drm/msm/dsi: round 6G byte clock rate to the PLL-achievable value
Kazuki Hanai (1):
nvmet-auth: Synchronize timeout work during SQ teardown
Kemeng Shi (3):
irqchip/gic-v3-its: Fix memleak in its_probe_one()
irqchip/gic-v3-its: Fix its node leak in gic_acpi_parse_madt_its()
irqchip/gic-v3-its: Prevent leak in its_vpe_irq_domain_alloc()
Kenny Levinsen (1):
HID: i2c-hid: Revert to await reset ACK before reading report descriptor
Kevin Tian (2):
iommu/vt-d: Fix no_iommu to disable platform opt-in
iommu/vt-d: Force requesting ACS when tboot is enabled
Kiryl Shutsemau (Meta) (3):
x86/tdx: Fix off-by-one in port I/O handling
x86/insn-eval: Move assign_register() out of KVM as insn_assign_reg()
x86/tdx: Fix zero-extension for 32-bit port I/O
Koichiro Den (13):
NTB: ntb_transport: Recycle TX entries before client callbacks
NTB: ntb_transport: Fail TX enqueue when the QP link is down
NTB: ntb_transport: Reject oversized TX buffers
net: ntb_netdev: Avoid double-accounting netif_rx() drops
net: ntb_netdev: Count packets dropped on RX refill failure
net: ntb_netdev: Fix TX busy and drop handling
dmaengine: dw-edma: Fix HDMA channel status register access
dmaengine: dw-edma: Complete descriptors before pausing
dmaengine: dw-edma: Initialize IRQ data before requesting IRQs
dmaengine: dw-edma: Terminate all descriptors without callbacks
dmaengine: dw-edma: Serialize abort state updates
dmaengine: dw-edma: Serialize channel state checks
dmaengine: dw-edma: Clear stale requests on termination
Konrad Dybcio (8):
arm64: dts: qcom: sc8180x-primus: Rename regulator nodes
arm64: dts: qcom: sc8180x-lenovo-flex-5g: Rename regulator nodes
arm64: dts: qcom: msm8996-xiaomi-gemini: Fix up ti,drv2604 enable GPIO
arm64: dts: qcom: msm8998: Don't pull-up I2C pins by default in sleep
arm64: dts: qcom: sdm632-motorola-ocean: Fix LED default trigger property
arm64: dts: qcom: sc8280xp-crd: Fix the pin index for misc_3p3_reg_en
drm/msm/dpu: Drop sneaky dev_pm_opp_set_rate(0)
drm/msm/dsi: Drop dev_pm_opp_set_rate(0)
Konstantin Andreev (3):
smack: fix incorrect task context in smack_msg_queue_msgrcv
smack: deduplicate smackfs/{direct,mapped} file_operations
smack: restrict smackfs/{direct,mapped} values to 0-255
Konstantin Komarov (4):
fs/ntfs3: bound page_lcns[] index by the log record
fs/ntfs3: Add more checks in mi_enum_attr (part 2)
fs/ntfs3: Mark inode as bad as soon as error detected in mi_enum_attr()
fs/ntfs3: fix out-of-bounds read in read_log_rec_buf()
Krystian Kaniewski (1):
ocfs2: fix circular locking dependency in ocfs2_init_acl()
Krzysztof Kozlowski (2):
bus: qcom-ebi2: Simplify with scoped for each OF child loop
arm64: dts: qcom: sc8180x: drop duplicated PCI iommus property
Krzysztof Wilczyński (5):
alpha/PCI: Fix I/O port accessor argument order in pci_legacy_write()
PCI/sysfs: Avoid spurious runtime PM wakeup on config space accesses
PCI/proc: Avoid spurious runtime PM wakeup on config space accesses
PCI/proc: Use file_ns_capable() when checking config space read access
PCI/proc: Warn on writes to kernel-exclusive config space regions
Kuan-Wei Chiu (1):
interconnect: Fix use after free in icc_get() and of_icc_get_by_index()
Kuninori Morimoto (4):
of: property: add missing kerneldoc for of_graph_get_endpoint_count()
of: property: use unsigned int return on of_graph_get_endpoint_count()
of: property: add of_graph_get_next_port()
of: property: add of_graph_get_next_port_endpoint()
Kyle Zeng (1):
hfsplus: validate thread record before delete key rebuild
Lad Prabhakar (2):
rtc: rzn1: Fix weekday underflow when alarm crosses month boundary
rtc: rzn1: Disable alarm interrupt before reprogramming alarm registers
Lars Petter Mostad (1):
hwmon: (emc1403) Add support for EMC1428 and EMC1438.
Laxman Acharya Padhya (4):
wifi: mt76: mt7996: validate default EEPROM firmware size
iio: light: gp2ap002: Disable regulators on resume failure
drm/panel: samsung-s6d16d0: Power off on prepare failure
firmware: coreboot: Validate table bounds
Lei Huang (1):
media: s2255: check firmware size before reading trailing marker
Leo Yan (4):
perf cs-etm: Fix thread leaks on trace queue init failure
perf cs-etm: Flush thread stacks after decoder reset
perf cs-etm: Avoid truncating AUX buffer sizes to int
perf: arm_spe: Make wakeup range check overflow safe
Leon Hwang (1):
bpf: Copy per-CPU map value padding in copy_map_value_long()
Leon Romanovsky (14):
RDMA/core: Wait for RCU callbacks before unloading ib_core
RDMA/mlx5: Drain RCU callbacks during module teardown
RDMA/ipoib: Drain RCU callbacks during module teardown
RDMA/hfi1: Preserve unit 0 on allocation failure
RDMA/hfi1: Free RX data on late probe failure
RDMA/hfi1: Remove redundant PCI device ID validation
RDMA/hfi1: Create workqueues before device initialization
RDMA/hfi1: Stop flushing the global IB workqueue
RDMA/hfi1: Initialize debugfs after probe completes
RDMA/srpt: Pass the mapped task attribute to target_init_cmd()
RDMA/mlx5: Fix stack out-of-bounds read in cc_params debugfs
RDMA/mlx5: Send cong param changes to the resolved port mdev
RDMA/cxgb4: free STAG index when TPT entry write fails
RDMA/erdma: restrict the driver to little-endian systems
Li Qiang (1):
scsi: ufs: debugfs: Reserve space for a string terminator
Li RongQing (1):
vdpa/mlx5: fix wrong list iterated in add_direct_chain error path
Li Wang (1):
ksm_tests: skip hugepage test when Transparent Hugepages are disabled
Li Zetao (1):
spi: davinci: Use helper function devm_clk_get_enabled()
Liam R. Howlett (Oracle) (1):
maple_tree: fix argument name in header
LiangCheng Wang (1):
ASoC: fsl-asoc-card: defer probe when the CPU DAI device is not ready
Linfeng Sun (1):
vdpa_sim: fix cleanup after worker creation failure
Linghui Wu (1):
wifi: ath10k: snoc: use memcpy_fromio() for MSA ramdump
Linkai Gong (4):
cpuidle: dt_idle_genpd: kfree() the original name allocation
i2c: mux: demux-pinctrl: fix OF node leak on kstrdup failure
soc: fsl: qe: check platform_driver_register() in qe_ic_of_init()
rtc: gamecube: check return value of devm_rtc_register_device()
Linmao Li (11):
iio: chemical: sgp30: Handle IAQ thread creation failure
platform/mellanox: mlxbf-pmc: Check ACPI_COMPANION() against NULL
platform/surface: acpi-notify: Check ACPI companion before use
ppdev: prevent overflow when setting port timeout
ASoC: meson: Keep link pointers valid on realloc failure
crypto: keembay - Initialize completion before requesting IRQ
Bluetooth: hci_conn: fix the SCO setup context lifetime
Bluetooth: hci_sync: free the advertising instance on the failure and cancel paths
Bluetooth: MGMT: free the mesh send cancel command when it is cancelled
nfc: digital: Do not dump a NULL response in command completion
mailbox: rockchip: disable pclk on probe failure and unbind
Liu Qi (1):
usb-storage: ene_ub6250: fix race between scan work and probe
Loic Poulain (3):
phy: qcom: qmp-usb-legacy: Fix possible NULL-deref on early runtime suspend
phy: qcom: snps-femto-v2: Fix possible NULL-deref on early runtime suspend
phy: qcom: qmp-usb: Fix possible NULL-deref on early runtime suspend
Lothar Rubusch (1):
crypto: atmel-sha204a - fix heap info leak on I2C transfer failure
Lovekesh Solanki (1):
USB: gadget: fix NULL pointer dereference in gadget_dev_ioctl()
Luca Weiss (1):
arm64: dts: qcom: sm7225-fairphone-fp4: Fix address in fb node name
Lukasz Majewski (2):
net: hsr: Use full string description when opening HSR network device
net: hsr: Provide RedBox support (HSR-SAN)
MD Danish Anwar (1):
net: hsr: enable promiscuous mode on interlink port with fwd offload
Ma Ke (2):
power: supply: bq25890: Fix power_supply reference leak
platform/x86: ishtp_eclite: Fix ACPI device reference leak in probe error path
Maciej Fijalkowski (2):
xsk: Get rid of xdp_buff_xsk::orig_addr
xsk: avoid double checking against rx queue being full
Mahanta Jambigi (3):
net/smc: hash socket only after full initialisation in smc_sk_init()
net/smc: free stashed qentry before overwrite in REQ_ADD_LINK to ADD_LINK transition
net/smc: free pending qentry in smc_llc_flow_stop() before memset
Maher Sanalla (1):
RDMA/mlx5: Fix integer overflow of user QP buffer size
Malaya Kumar Rout (2):
selftests/bpf: Fix memory leak in msg_alloc_iov error path
PM: hibernate: Fix memory leak in snapshot_write_next() error path
Manish Rangankar (1):
scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
Manivannan Sadhasivam (11):
wifi: ath11k: Clear affinity hint before calling ath11k_pcic_free_irq() in error path
bus: mhi: host: Flush the posted write after writing to MHI_SOC_RESET_REQ_OFFSET
arm64: dts: qcom: sc8180x: Fix the PCIe iommu-map entries
arm64: dts: qcom: sdm845: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm8150: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm8250: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm8350: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm8450: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm8550: Fix the msi-map entries
arm64: dts: qcom: sm8550: Fix the PCIe iommu-map entries
arm64: dts: qcom: sm7225-fairphone-fp4: Fix swapped USB QMP PHY vdda-phy/vdda-pll supplies
Maoyi Xie (5):
power: supply: twl4030_charger: cancel workers via devm
i3c: master: svc: bound IBI payload to the requested max_payload_len
w1: ds28e17: reject an oversize length on an I2C block read
platform/chrome: cros_ec_typec: Reject out-of-bounds PD cap count
drm/bridge: tc358767: clamp the reported AUX read size to the request
Marc Zyngier (1):
PCI: xgene: Drop XGENE_PCIE_IP_VER_UNKN
Marco Elver (2):
kcsan: avoid unintended access checking in NMIs
arm64: Disable KCSAN instrumentation in delay.o
Marek Czernohous (2):
forcedeth: fix off-by-one when saving/restoring non-PCI config space
forcedeth: stop the tx_timeout register dump past the requested window
Marino Dzalto (1):
netfilter: xt_HL: add pr_fmt and checkentry validation
Marius Cristea (1):
hwmon: (emc1403) Drop hysteresis for low limit temperature
Mark Brown (1):
selftests/mm: report unique test names for each cow test
Mark Zhang (1):
RDMA/core: Add driver APIs pre_destroy_cq() and post_destroy_cq()
Martin Blumenstingl (1):
clk: meson: align gxbb_32k_clk_sel number of parents with actual count
Martin Kaiser (1):
hwrng: ks-sa - access private data via struct hwrng
Masami Hiramatsu (Google) (3):
tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions
kprobes: Protect kprobe_blacklist with RCU
tracing: Remove "__attribute__()" from the type field of event format
Mathieu Dubois-Briand (1):
clk: ti: mux: resolve parent clocks by DT index, not by name
Matt Turner (4):
alpha: fix ieee_swcr_to_fpcr setting FPCR_DNOD unconditionally
alpha: don't leak hardware-fabricated FP exception bits to user space
alpha: marvel: Fix irq_set_status_flags to use correct IRQ number
alpha: marvel: Fix lock ordering in init_io7_irqs()
Matthew Wilcox (Oracle) (6):
nilfs2: add nilfs_end_folio_io()
nilfs2: convert nilfs_forget_buffer to use a folio
nilfs2: convert to nilfs_folio_buffers_clean()
nilfs2: convert nilfs_btnode_prepare_change_key to use a folio
nilfs2: convert nilfs_btnode_commit_change_key to use a folio
nilfs2: convert nilfs_page_bug() to nilfs_folio_bug()
Matti Vaittinen (1):
power: supply: bd99954: Drop bad register fields
Matvey Kovalev (1):
wifi: ath11k: fix NULL dereference in ath11k_qmi_m3_load()
Maxime Chevallier (6):
net: stmmac: selftests: Pass the IP proto mask in the TC selftest
net: stmmac: selftests: Check multiple MMC counters
net: stmmac: dwmac1000: Account for the primary MAC address for UC filtering
net: stmmac: dwmac4: Account for the primary MAC address for UC filtering
net: stmmac: dwxgmac: Account for the primary MAC address for UC filtering
net: stmmac: selftests: Account for the UC filter list for filtering tests
Maxime Ripard (1):
drm/bridge: cdns-mhdp8546: Return an error pointer on allocation failure
Maximilian Immanuel Brandtner (1):
tls: fix RX desync on overlapping skbs
Meghana Malladi (1):
arm64: dts: ti: k3-am64: Fix MDIO clock reference for ICSSG0 node
Mert Seftali (1):
iio: accel: dmard09: Implement IIO_CHAN_INFO_SCALE
Michael Bommarito (8):
NFSD: restart ssc_expire_umount walk after dropping nfsd_ssc_lock
libceph: validate OSD extent maps before cursor advance
ceph: bound copied dentry name length in NFS export get_name
ceph: bound num_export_targets array for mds info v2/v3
ceph: bound xattr value length in __build_xattrs()
lockd: pin next file across nlm_inspect_file lock-drop
HID: roccat: bound device-supplied profile index
usb: gadget: configfs: fix out-of-bounds read of qw_sign
Michael Byczkowski (1):
pps: pps-gpio: split IRQ handler into hardirq timestamper + threaded handler
Michael S. Tsirkin (1):
virtio_balloon: disable indirect descriptors
Michael Walle (4):
regulator: tps6594-regulator: remove interrupt_count
regulator: tps6594-regulator: remove hardcoded buck config
regulator: tps6594-regulator: refactor variant descriptions
powerpc/configs: enable CONFIG_RAS to fix EDAC support
Michal Koutný (1):
netfilter: xt_cgroup: Make it independent from net_cls
Mike Rapoport (Microsoft) (1):
selftests/mm: ksm_tests: use kselftest framework
Mike Snitzer (1):
NFSD: remove flawed WARN_ON_ONCE from nfsd_mode_check
Mikko Perttunen (2):
gpu: host1x: Fix offset calculation in trace_write_gather
gpu: host1x: Avoid stack over-read in debug output helpers
Miklos Szeredi (1):
fuse: use FUSE_ROOT_ID in fuse_get_root_inode()
Mikulas Patocka (3):
dm-stats: fix a crash if allocation of per-cpu data fails
dm: fix race when loading and unloading a table
dm: fix resume-vs-remove race
Min zhang (1):
clk: hisilicon: reset: Use devm_kzalloc to initialize hisi_reset_controller
Ming Qian (1):
media: v4l2-ctrls: Allow unknown HDR10 white point and luminance
Mohamad Raizudeen (1):
PCI: Fix 32-bit config write in Intel PCH Root Port MPC ACS quirk
Mohammad Abu-Khader (1):
kunit: tool: fix _list_tests filtering wrong variable when list has TAP prefix
Mohammed EL Kadiri (2):
media: venus: fix payload size returned by parse_caps() and parse_alloc_mode()
media: venus: fix payload size calculation in parse_raw_formats()
Mohd Ayaan Anwar (1):
phy: qcom: sgmii-eth: vote for both voltage rails with correct current loads
Moksh Panicker (1):
iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable
Muchun Song (1):
powerpc/mm: fix wrong addr_pfn tracking in compound vmemmap population
Muhammad Bilal (12):
platform/x86: hp-bioscfg: accept reduced ACPI packages from older HP BIOS
platform/x86: hp-bioscfg: advance elem past consumed array elements
platform/x86: hp-bioscfg: bound ordered-list parsing by the package count
platform/x86: hp-bioscfg: fix heap OOB read in sk_store() and kek_store()
platform/x86: hp-bioscfg: fix heap OOB read on empty password write
platform/x86: hp-bioscfg: fix new_password_store() overwriting current_password
platform/x86: hp-bioscfg: fix off-by-one write in hp_get_string_from_buffer()
platform/x86: hp-bioscfg: fix ORD_LIST_ELEMENTS never being parsed
platform/x86: hp-bioscfg: pass validated element count to package parsers
platform/x86: hp-bioscfg: warn on element type mismatch instead of failing
staging: rtl8723bs: fix OOB read / stack overflow in rtw_get_wps_attr()
staging: rtl8723bs: fix OOB read in rtw_action_frame_parse()
Muhammad Usama Anjum (3):
selftests/mm: skip COW tmpfile cases when fallocate() is unsupported
selftests/arm64: Print missing MTE TAP headers
selftests/arm64: Treat KSM merge_across_nodes as optional
Mukesh Ojha (5):
iommu/qcom: Fix inverted fault report check in qcom_iommu_fault()
remoteproc: Move resource table data structure to its own header
remoteproc: use rsc_table_for_each_entry() in rproc_handle_resources()
remoteproc: fix OOB read via signed offset in rsc_table_for_each_entry()
firmware_loader: do not queue completed sysfs fallback requests
Myeonghun Pak (11):
usb: gadget: snps_udc_plat: clean up PHY on probe deferral
fbdev: uvesafb: unregister connector callback on init failure
nvme-pci: disable controller on admin queue IRQ setup failure
tpm: tpm_i2c_nuvoton: disable IRQ on wait timeout
media: rc: sunxi-cir: Unregister rc device on probe failure
clk: versaclock7: Fix APLL clock leak on probe failure
crypto: keembay - Fix AEAD unregister count in error path
char: xilinx_hwicap: unregister class on init errors
RDMA/erdma: Fix CEQ tasklet use-after-free on removal
clk: palmas: Manage external-control prepare with devm
fbdev: tdfxfb: fix PCI enable cleanup with pcim_enable_device()
Naman Jain (1):
PCI: hv: Set irq_retrigger callback for the Hyper-V PCI MSI irqchip
Namhyung Kim (3):
perf test: Update all metrics test like metricgroups test
perf test: Do not skip when some metrics tests succeeded
perf test: Fix perf stat --bpf-counters on hybrid machines
Namjae Jeon (4):
ksmbd: fix WARNING "do not call blocking ops when !TASK_RUNNING"
ksmbd: do not advertise unimplemented CA support
ksmbd: free preauth sessions on connection teardown
ksmbd: disconnect on SMB3 decryption failure
Nathan Chancellor (1):
arch_numa: avoid false positive fortify warning in setup_node_to_cpumask_map()
Naveen Kumar Chaudhary (1):
locking/lockdep: Fix NULL pointer dereference in __lock_set_class()
Naveen Mamindlapalli (1):
octeontx2-af: Fix TL3/TL2 link config ENA clearing
Neil Armstrong (1):
arm64: dts: qcom: sm8550: Use GIC-ITS for PCIe0 and PCIe1
Neill Kapron (1):
usb: gadget: f_fs: Prevent deadlock during ep0 read loop
Nguyen Dinh Phi (1):
vsock: use sock_error() to consume sk_err after a failed connect
Nick Chan (3):
nvme: apple: Add Apple A11 support
nvme-apple: Prevent shared tags across queues on Apple A11
nvme-apple: Reset q->sq_tail during queue init
Nicolas Frattaroli (1):
drm/rockchip: vop2: Recognise 10-bit YUV422 as YUV format
Nikhil Gautam (6):
iio: light: opt4001: Fix power down clearing bits of the wrong register
iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem()
iio: light: opt4001: Reject integration times with a non-zero seconds part
iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask
iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes
iio: light: gp2ap002: re-enable irq if runtime suspend fails
Niklas Cassel (2):
ata: libata-scsi: fix DSM TRIM for sector sizes larger than 2048 bytes
null_blk: serialize configfs attribute updates with device setup
Nikolay Aleksandrov (1):
net: bridge: vlan: fix inverted default vlan notification
Nikolay Kulikov (1):
mlxbf-bootctl: fix the build error with FIELD_PREP()
Nilesh Javali (28):
scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers
scsi: qla2xxx: Bound i2c->length in I2C bsg handlers
scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
scsi: qla2xxx: Initialize NVMe abort_work once at submission
scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation
scsi: qla2xxx: Serialize flash version read in reset handler
scsi: qla2xxx: Fix cs84xx use-after-free on host teardown
scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump
scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()
scsi: qla2xxx: Fix FCE trace enable parsing in debugfs
scsi: qla2xxx: Don't query firmware state while chip is down
scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path
scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb()
scsi: qla2xxx: Quiesce response IRQ before freeing request queue
scsi: qla2xxx: Avoid double completion in async IOCB timeout
scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read
scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()
scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort
scsi: qla2xxx: Drop vport reference under lock in report ID acquisition
scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition
scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics
scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak
scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started
Nirbhay Sharma (1):
fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list
Nirmala Devi Mal Nadar (2):
mfd: tps6594: Add register definitions for TI TPS65224 PMIC
regulator: tps6594-regulator: Add TI TPS65224 PMIC regulators
Norbert Szetei (5):
RDMA/ucma: Lock the handler in ucma_set_ib_path()
openvswitch: only skb_tx_error() a packet we are about to drop
net: skbuff: don't touch shared zerocopy state in skb_tx_error()
net: skbuff: don't skb_tx_error() the source skb in skb_zerocopy()
ksmbd: fix overflow in dacloffset bounds check
Nícolas F. R. A. Prado (1):
pinctrl: mediatek: eint: Fix invalid pointer dereference for v1 platforms
Ondrej Jirman (1):
arm64: dts: allwinner: sun50i-a64-pinephone: Fix mpu6050 mount matrix
Pablo Neira Ayuso (2):
netfilter: nf_tables: move hardware offload step after building the chain blob
netfilter: x_tables: replace pr_{info,err}() by pr_info_ratelimited()
Patrisious Haddad (8):
RDMA/core: Add support to set privileged QKEY parameter
RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations
RDMA/core: Fix use after free in ib_query_qp()
RDMA/core: Fix potential use after free in ib_destroy_cq_user()
RDMA/core: Fix potential use after free in ib_destroy_srq_user()
RDMA/core: Fix potential use after free in counter_release()
RDMA/core: Fix potential use after free in ib_free_cq()
RDMA/core: Fix potential use after free in ib_dealloc_pd_user()
Paul Chaignon (1):
bpf: Reject narrower access to pointer ctx fields
Paul Kocialkowski (1):
drm: lcdif: Wait for vblank before disabling DMA
Pauli Virtanen (2):
Bluetooth: L2CAP: reject accept queue add unless BT_LISTEN
Bluetooth: L2CAP: fix race l2cap_sock_cleanup_listen() vs. put_chan
Pavel Shpakovskiy (1):
Bluetooth: mgmt: fix 'hdev->discovery.uuids' NULL dereference
Pavel Zhigulin (1):
speakup: keyhelp: guard letter_offsets possible out-of-range indexing
Pei Xiao (2):
parisc: eisa: Fix infinite loop when parsing invalid IRQ value
ipack: ipoctal: fix UAF, null-ptr-deref, and use-after-free in cleanup on remove
Peng Fan (1):
remoteproc: core: Drop redundant initialization of 'ret' in rproc_shutdown()
Pengpeng Hou (18):
eCryptfs: bound the packet-length peek to the user buffer
Input: aiptek - validate raw macro indices before updating state
mtd: afs: validate v2 image info bounds
mtd: rawnand: validate ONFI extended parameter page sections
wifi: rtw89: fix HE extended capability length check
crypto: sa2ul - stop probe if context pool creation fails
crypto: rk3288 - fail ahash requests on HASH idle timeout
soc: qcom: rpmh-rsc: manage PM notifiers with devres
bus: qcom-ebi2: use managed resources for clocks and children
pmdomain: bcm: bcm2835: handle genpd provider registration errors
misc: ad525x_dpot: use driver core groups for sysfs files
RDMA/nldev: validate dynamic counter attribute length
ACPI: processor: validate MADT IOAPIC entry bounds
thermal: intel: int3400: clean up ODVP on probe failures
mfd: rave-sp: validate received frame payload lengths
mfd: iqs62x: Reject zero-length firmware records
ASoC: SOF: validate topology volume range before allocation
rtc: zynqmp: Return optional clock lookup errors
Peter Oberparleiter (1):
s390/debug: Fix deadlock during unregister
Petr Oros (1):
ice: clear the default forwarding VSI rule when releasing a VSI
Petr Vaganov (1):
scsi: core: Fill in DMA padding bytes in scsi_alloc_sgtables()
Philipp Weber (1):
HID: core: quiesce input in hid_hw_stop() to prevent use-after-free
Phillip Lougher (1):
Squashfs: check block offset is not negative
Pranjal Shrivastava (1):
iommu/arm-smmu-v3: Convert to use atomic poll timeout
Pu Lehui (6):
bpf: Fix potential UAF in bpf_netns_link_update_prog
bpf: Fix potential UAF when reading bpf link info
bpf, cgroup: Fix invalid storage access after __cgroup_bpf_attach failed
selftests/bpf: Limit URLS parsing logic to actual scope in vmtest
selftests/bpf: Support local rootfs image for vmtest
selftests/bpf: Add description for running vmtest on RV64
Qing Ming (1):
mpls: reload header after pskb_may_pull()
Qingfang Deng (1):
pppox: drain queued packets on channel handoff
Qingshuang Fu (1):
irqchip/renesas-irqc: Fix generic interrupt chip leak on remove
Quentin Schulz (1):
arm64: dts: rockchip: fix eMMC reset polarity on PX30 Ringneck
Rafael J. Wysocki (3):
cpufreq: intel_pstate: Fix setting minimum P-state at init time
ACPI: processor: idle: Expand _LPI package sanity checks
ACPI: processor: Update cpuidle driver check in __acpi_processor_start()
Ran Hongyun (1):
UBI: fix two issues in the ubi.mtd MODULE_PARM_DESC
Ricardo B. Marlière (1):
libbpf: Search /lib64 and /lib in resolve_full_path()
Ricardo Neri (1):
sched/fair: Check CPU capacity before comparing group types during load balance
Robbie Ko (1):
nfsd: reject out-of-range useconds in NFSv2 SETATTR/CREATE
Robert Richter (1):
cxl/pci: Store the endpoint's Component Register mappings in struct cxl_dev_state
Robertus Diawan Chris (1):
modpost: prevent leak when early return no suffix .o in read_symbols()
Ronald Claveau (1):
PCI: meson: Fix GPIO state while requesting PERST#
Rosen Penev (4):
ARM: dts: helios4: add vcc-supply to EEPROM
ARM: dts: helios4: add vcc-supply to GPIO expander
ARM: dts: helios4: add SATA regulator supplies
ASoC: xilinx: formatter_pcm: fix stream_data leak on open error
Runyu Xiao (9):
fbdev: omapfb: panel-dsi-cm: initialize lock before registering display
ALSA: pcxhr: initialize mutexes before requesting threaded IRQ
wifi: mt76: mt7615: avoid waiting for mac work under the mt76 mutex
scsi: pm8001: Use rollback index when freeing MSI-X vectors
ALSA: harmony: initialize locks before requesting IRQ
ASoC: cs35l33: drain threaded IRQ before runtime suspend
ASoC: cs35l34: drain threaded IRQ before runtime suspend
ASoC: rt700-sdw: always drain jack work on remove
sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE
Ruoyu Wang (15):
net: openvswitch: fix nf_connlabels leak in ovs_ct_init
media: cec: Serialize exclusive follower delivery
media: cobalt: Avoid freeing ALSA private data twice
media: zoran: Avoid freeing a registered video_device twice
bus: qcom-ebi2: Fix clock leak on probe failure
usb: gadget: aspeed_udc: check endpoint DMA allocation
mtd: mtdswap: Avoid freeing registered blktrans device twice
NFSv4: remove callback IDR entry on client allocation failure
net: dsa: mv88e6xxx: Fix PCS link check on CMODE read error
bonding: initialize err for empty target lists
i2c: ocores: Disable clock on failed resume
net: bridge: Reject descending VLAN tunnel ranges
net: ipa: balance runtime PM reference on remove error
tpm: st33zp24: Return zero on status read failure
tpm: st33zp24: Validate locality read result
Rupesh Majhi (1):
iio: pressure: dps310: fix NULL pointer dereference on ACPI probe
Russell King (Oracle) (1):
phy: qcom-sgmii-eth: relax order of .power_on() vs .set_mode*()
Ryan Roberts (5):
selftests/mm/kugepaged: restore thp settings at exit
selftests/mm: factor out thp settings management
selftests/mm: support multi-size THP interface in thp_settings
selftests/mm/khugepaged: enlighten for multi-size THP
selftests/mm/cow: generalize do_run_with_thp() helper
Ryusuke Konishi (2):
nilfs2: fix slab-out-of-bounds in nilfs_direct_propagate after truncation
nilfs2: fix BUG in nilfs_copy_dirty_pages() on dirty state mismatch
SJ Park (12):
mm/damon/sysfs-schemes: kobject_del() scheme dirs
mm/damon/sysfs-schemes: kobject_del() scheme filter dirs
mm/damon/sysfs-schemes: kobject_del() scheme region dirs
mm/damon/sysfs: kobject_del() region and target (error) dirs
mm/damon/sysfs: kobject_del() target (normal), context and kdamond dirs
mm/damon/core-kunit: check region count before testing in split_at()
mm/damon/vaddr: drop last same folio access check optimization
mm/damon/paddr: drop last same folio access check reuse optimization
mm/damon/vaddr-kunit: check region count in three_regions test
mm/damon/core-kunit: handle region split failure in filter_out()
mm/damon/tests/core-kunit: catch test failure in test_merge_regions_of()
mm/damon/core: skip aging from repeated aggressive merging
Sajal Gupta (1):
md/raid5-ppl: fix use-after-free in ppl_do_flush()
Saket Kumar Bhaskar (1):
powerpc/irq: Fix missing r2 clobber in PCREL inline assembly
Samuel Page (1):
fs/ntfs3: fix info-leak on partial LZNT decompress in ni_read_frame()
Sandipan Das (3):
perf/x86/amd/uncore: Refactor uncore management
perf/x86/amd/uncore: Add group validation
perf/x86/amd/uncore: Fix the return value of amd_uncore_df_event_init() on error
Sang-Heon Jeon (2):
iio: light: isl29028: return zero in write_raw() on success
iio: light: tsl2583: return zero in write_raw() on success
Sanghyun Park (2):
bpf: Fix use-after-free on mm_struct in bpf_find_vma()
xfrm: Fix skb double-free in xfrm_dev_direct_output()
Sanjay Chitroda (2):
iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register()
iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure
Sanman Pradhan (1):
hwtracing: hisi_ptt: Propagate DMA reset timeout in trace_start()
Sayali Patil (2):
selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes
selftests/mm: fix ternary operator precedence in ksm_tests
Sean Rhodes (1):
misc: rtsx_usb: avoid USB I/O in runtime autosuspend
Sean Young (2):
media: keymaps: Remove obsolete RC_MAP_RC5_TV keymap define
media: keymaps: Remove obsolete RC_MAP_HAUPPAUGE_NEW keymap define
Sebastian Andrzej Siewior (1):
net: hsr: Use the seqnr lock for frames received via interlink port.
Sebastian Reichel (1):
media: v4l: async: Drop useless list move operation
Sechang Lim (1):
tcp: clear sock_ops cb flags before force-closing a child socket
Seiji Nishikawa (1):
ipmi: si: Fix NULL pointer dereference after failed registration
Selvin Xavier (1):
RDMA/bnxt_re: Clear VM_MAYWRITE on DBR/toggle page mmap
Sergei Litvin (1):
scripts/tags.sh: Prevent binary files appearing in cscope.files
Serhat Kumral (1):
RDMA/ucma: Allow path records to exactly fit the output buffer
Shin'ichiro Kawasaki (3):
nvmet-rdma: factor out response resource cleanup
nvmet-rdma: fix response resource leak on queue teardown
null_blk: add configfs variable shared_tags
Shivam Kumar (2):
nvmet-tcp: fix out-of-bounds write when receiving an over-long PDU
nvmet-tcp: reject unsolicited H2CData PDUs
Shivam Rawat (1):
drm/msm/a6xx: Fix stale rpmh votes after suspend
Shuangpeng Bai (3):
media: usbtv: keep device alive while ALSA card exists
media: go7007: defer the ALSA v4l2 put until card release
RDMA/siw: Fix use-after-free in siw_accept()
Simon Glass (1):
pinctrl: rockchip: Reset the pin count when recalculating SoC data
Slawomir Stepien (1):
wifi: zd1211rw: reject secondary interfaces to prevent conflicts
Sonali Pradhan (1):
usb: gadget: u_audio: Fix use-after-free on sound card disconnect
Sourabh Jain (3):
powerpc/pseries: Handle and log pseries-wdt registration failures
powerpc/pseries: Move H_WATCHDOG definitions to a common header
powerpc/crash: stop watchdogs before booting kdump kernel
Srinivas Kandagatla (2):
soundwire: qcom: Fix port exhaustion check in stream_alloc_ports
rpmsg: glink: remove duplicate code for rpmsg device remove
Srinivas Pandruvada (2):
platform/x86: ISST: Just allow 2 bits for SST feature enable
platform/x86: ISST: Validate logical CPU id and clos id
Stanislav Kinsburskii (1):
lib/test_hmm: fail dmirror_fault() when the mirrored mm is gone
Stanislaw Gruszka (1):
wifi: rtl818x: initialize eeprom_93cx6 struct to zero
StanleyYP Wang (1):
wifi: mt76: mt7996: fix reg addr remap when addr is 0
Stefan Haberland (3):
s390/dasd: Do not complete a failed ESE read as successful
s390/dasd: Guard sysfs discipline callbacks against unallocated private data
s390/dasd: Propagate partial completion length across ERP recovery
Stephan Gerhold (8):
clk: qcom: gcc-mdm9607: Increase delay for USB PHY reset
clk: qcom: gcc-msm8916: Fix enable_reg for gcc_blsp1_sleep_clk
clk: qcom: gcc-msm8939: Fix enable_reg for gcc_blsp1_sleep_clk
clk: qcom: gcc-mdm9607: Drop incorrect apss_tcu_clk_src
clk: qcom: gcc-mdm9607: Drop incorrect system_noc_bfdcd_clk_src
clk: qcom: gcc-mdm9607: Fix enable_reg for gcc_blsp1_sleep_clk
clk: qcom: gcc-mdm9607: Fix halt_reg for gcc_apss_axi_clk
clk: qcom: gcc-mdm9607: Drop incorrect BIMC PLL and related clocks
Steven Rostedt (2):
tracing/mmiotrace: Remove reference to unused per CPU data pointer
tracing: Have trace_event_update_all() only handle module that is loading
Su Yue (1):
md/md-bitmap: fix wrong bitmap_limit for clustermd when write sb
Sudeep Holla (12):
firmware: arm_scmi: Unregister device notifier before IDR teardown
firmware: arm_scmi: Free transport channel on IDR failure
firmware: arm_scmi: Avoid IDR updates while cleaning channels
firmware: arm_scmi: Reject out of range DT protocol IDs
firmware: arm_scmi: Use channel ID for transport teardown
firmware: arm_scmi: Protect device request lookup with RCU
firmware: arm_scmi: Drop handle on protocol bind failures
firmware: arm_scmi: Fix requested device removal race
firmware: arm_scmi: Unrequest devices if driver registration fails
mailbox: pcc: Always map the shared memory communication address
Revert "mailbox/pcc: support mailbox management of the shared buffer"
firmware: arm_scmi: Roll back partial protocol table registration
Sujal Tuladhar (1):
scsi: target: iscsi: Reserve a terminator byte for the login payload
Sunday Clement (1):
drm/amdkfd: Reject zero-sized AQL queue allocations after size halving
Sunil Khatri (1):
drm/amdgpu: use AMDGPU_GPU_PAGE_SHIFT instead of PAGE_SHIFT
Suraj Gupta (1):
dmaengine: xilinx_dma: Fix channel idle state management in AXIDMA and MCDMA interrupt handlers
Sven Eckelmann (5):
batman-adv: dat: avoid unaligned fault in IP extraction
batman-adv: bla: fix freeing of claims on meshif deletion
batman-adv: bla: prevent CRC corruptions after claim flush
batman-adv: dat: atomically update mac addresses
batman-adv: bla: avoid CRC corruption due to parallel claim add
Sven Peter (4):
nvme-apple: Destroy the admin queue on removal
nvme-apple: Don't set a DMA direction for commands without a data transfer
nvme-apple: Never set the opcode in the NVMMU TCB
nvme-apple: Drop the PRP null check chicken bit
Takashi Iwai (12):
ALSA: aloop: Fix racy access at PCM trigger
ALSA: aloop: Check card index validity at probe
ALSA: mpu401: Check card index validity at probe
ALSA: mts64: Check card index validity at probe
ALSA: portman2x4: Check card index validity at probe
ALSA: serial-u16550: Check card index validity at probe
ALSA: virmidi: Check card index validity at probe
ALSA: pcm: Fix race between non-atomic ops and trigger-start
ALSA: seq: midi: Optimize event_input locking with RCU
ALSA: control: Use automatic cleanup of kfree()
ALSA: control: Use guard() for locking
ALSA: control: Fix unannotated kfree() cleanup
Takuma Fujiwara (1):
PCI: j721e: Fix incorrect max_lanes for J7200
TanZheng (1):
RDMA/srpt: Fix srpt_alloc_rw_ctxs() unwind counters
Tanushree Shah (3):
perf data convert json: Fix trace_seq memory leak in process_sample_event()
perf trace-event: Fix buffer overflow in read_string()
perf trace-event: Fix integer truncation in do_read() and skip()
Tao Cui (3):
block/blk-stat: drain per-cpu callback stats over possible CPUs
block/blk-iocost: collect per-cpu latency stats over possible CPUs
block/kyber-iosched: flush per-cpu latency buckets over possible CPUs
Tao Yu (1):
drm/gud: validate TV mode names before creating enum property
Tengda Wu (1):
perf test: Use sqrtloop workload to test bperf event
Tetsuo Handa (1):
net: add missing ref_tracker_dir_exit() to net_passive_dec()
Thadeu Lima de Souza Cascardo (1):
drm: Fix drm_crtc_commit leak if signaled when PAGE_FLIP_EVENT is used
Thomas Bogendoerfer (1):
MIPS: ptrace: Fix syscall skipping via PTRACE_SYSCALL
Thomas Gleixner (2):
timer: Keep debugobjects state consistent in migrate_timer_list()
irqdomain: Make build work for CONFIG_GENERIC_IRQ_CHIP=n
Thomas Richard (1):
regulator: tps6594-regulator: Fix the number of irqs for TPS65224 and TPS6594
Thomas Richter (2):
s390/cpum_cf: Handle CPU hotplug via prepare/dead callbacks
perf test: raise limit to 20 percent for perf_stat_--bpf-counters_test
Thomas Weißschuh (2):
y2038: uapi: Use 64-bit __kernel_old_timespec::tv_nsec on x32
tools/nolibc/powerpc: mark ctr and xer as clobbered by system call
Thomas Zimmermann (2):
drm/hibmc: Fix list of formats on the primary plane
drm/hibmc: Use drm_atomic_helper_check_plane_state()
Thorsten Blum (5):
crypto: atmel-ecc - replace min_t with min
crypto: atmel-ecc - clean up and improve ECDH comments
crypto: atmel-ecc - reject hardware ECDH without a public key
crypto: sl3516 - drop invalid sg_dma_len checks before DMA mapping
apparmor: Replace sprintf/strcpy with scnprintf/strscpy in aa_policy_init
Tien Sung Ang (1):
fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
Tim Harvey (1):
PCI: Add ACS quirk for Pericom PI7C9X2G608 switches [12d8:2608]
Timur Kristóf (3):
drm/amdgpu/gfx6: Fixup emit_cntxcntl()
drm/amdgpu/gfx6: Fixup emitting SWITCH_BUFFER packets
drm/amdgpu/gfx6: Use PFP on the compute queues too
Titouan Ameline de Cadeville (1):
firmware: google: Add bounds checks in coreboot_table_populate()
Tomas Glozar (2):
tools/build: Add bpftool-skeletons feature test
tools/build: Use SYSTEM_BPFTOOL for system bpftool
Tomáš Macholda (1):
arm64: dts: turris-mox: fix usb3 phys
Tony W Wang-oc (1):
ACPI: processor: Add cpuidle driver check in acpi_processor_register_idle_driver()
Tzung-Bi Shih (2):
platform/chrome: sensorhub: Fix dropped timestamp events and log spam
platform/chrome: sensorhub: Fix memory overread in ring handler
Uday Khare (3):
media: video-i2c: fix kthread error pointer left in kthread_vid_cap on failure
regulator: tps6594: Fix device node reference leaks in multiphase loop
remoteproc: qcom: Fix glink->node reference leak in qcom_add_glink_subdev
Uwe Kleine-König (3):
usb: gadget: aspeed_udc: Convert to platform remove callback returning void
power: supply: sc2731_charger: Convert to platform remove callback returning void
net: ipa: Convert to platform remove callback returning void
Vaibhav Nagare (1):
qede: Fix NULL pointer dereference in TPA fragment processing
Valentin Kindschi (2):
Bluetooth: hci_conn: re-enable advertising only for peripheral role
Bluetooth: hci_event: clear HCI_LE_ADV only on a created connection
Valentin Schneider (1):
rcu: Rename rcu_momentary_dyntick_idle() into rcu_momentary_eqs()
Valery Borovsky (3):
media: airspy: use vb2_video_unregister_device() on disconnect to fix NULL deref
media: rtl2832_sdr: use vb2_video_unregister_device() on remove to fix DMA leak
media: rtl2832_sdr: release URBs and stream buffers on start_streaming() failure
Vasileios Almpanis (1):
i2c: core: fix debugfs UAF on adapter removal
Veronika Molnarova (1):
perf test stat_bpf_counter.sh: Stabilize the test results
Viacheslav Dubeyko (1):
ceph: fix BUG_ON in __ceph_build_xattrs_blob() due to stale blob size
Victor Nogueira (2):
net/sched: cls_api: fix teardown of an adopted proto on insert-race loss
net/sched: sch_teql: restore skb->dev on the slave failure path
Viktor Malik (1):
selftests/bpf: Silence array bounds warning in global_map_resize
Vincent Mailhol (1):
lib/ucs2_string.c: fix out-of-bounds read in ucs2_strnlen()
Vineet Gupta (1):
selftests/bpf: vmtest.sh: Preserve command quoting when running in the VM
Vishnu Santhosh (1):
rpmsg: glink: fix deadlock in endpoint destroy during driver detach
Vladimir Murzin (1):
arm64: hibernate: Restore DAIF state on error
Vladimir Oltean (1):
net: dsa: b53: fix error propagation from b53_fdb_dump()
Vladimir Zapolskiy (4):
dmaengine: mediatek: mtk-uart-apdma: Return -ENOMEM on memory allocation failure
dmaengine: hisilicon: Return -ENOMEM on dynamic memory allocation in probe
iommu/msm: Return -ENOMEM on memory allocation failure in probe
clk: qcom: Return expected ENOMEM error on dynamic allocation failure
Wei Jie Law (1):
HID: rmi: fix OOB access with undersized RMI reports
Wei Wang (2):
iommu/amd: Prevent SB IOAPIC from overriding IVRS validation errors
iommu/amd: Fix false positive in SB IOAPIC IVRS validation
Wei Yang (1):
mm/mm_init: fix incorrect node_spanned_pages
Weigang He (4):
media: cec: stm32: prevent out-of-bounds write on RX overflow
soc: samsung: exynos-pmu: fix of_node refcount leak in exynos_get_pmu_regmap()
ARM: imx: fix device_node refcount leak in imx_src_init()
ARM: imx: fix device_node refcount leaks in imx7_src_init()
Weilin Wang (1):
perf test: Simplify metric value validation test final report
Weiming Shi (5):
sctp: fix NULL deref on untransmitted RECONF completion
xdp: fix zero-copy frame layout
fs/ntfs3: reject restart table growth beyond U16_MAX entries
SUNRPC: check rpc_sockaddr2uaddr() return value in rpcb_register_inet4/6
fs/ntfs3: validate ef->size covers the record's name and value
Weiming Wu (1):
fs/ntfs3: fix out-of-bounds read of INDEX_ROOT in reparse/objid init
WenTao Liang (3):
regulator: as3722_get_regulator_dt_data: fix premature of_node_put leaving dangling of_node pointer
regulator: max8998_pmic_dt_parse_pdata: of_node_put on reg_np after ownership transferred to rdata
ASoC: samsung: aries_audio_probe: double of_node_put due to direct assignment without of_node_get
Wenjie Qi (2):
f2fs: return symlink writeback errors
f2fs: return writeback error from collapse range
Wentao Guan (1):
LoongArch: Do not save/restore percpu base register in rethook trampoline
Wentao Liang (2):
drm/sun4i: fix refcount leak in sun4i_backend_init_sat()
nvme: target: rdma: fix ndev refcount leak on queue connect
Wolfram Sang (1):
hwspinlock: propagate errno when registering single lock
Wupeng Ma (1):
mm/hugetlb: fix missing migratable flag on same-node hugetlb migration
Xiang Mei (9):
fs/ntfs3: validate dirty page table on log replay
bridge: mrp: reject zero test interval to avoid OOM panic
xfrm6: fix out-of-bounds write in xfrm6_input_addr() when secpath is full
isofs: fix out-of-bounds page array access on empty zisofs block
vfio/pci: clear vdev->msi_perm after freeing it on init failure
ext4: fix out-of-bounds read in ext4_read_inline_dir()
ext4: check dir entry fits before reading the hash trailer in ext4_search_dir()
vxlan: vnifilter: enforce exact length of GROUP/GROUP6 attributes
net: qualcomm: rmnet: restore skb->dev on deaggregated frames
Xie Yuanbin (1):
ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faults
Xin Chen (1):
Bluetooth: hci_core: use skb_get() instead of skb_clone() for req_skb
Xin Xie (1):
net: hsr: free learned nodes on device setup failure
Xingrui Li (1):
HID: sensor-hub: Fix out-of-bounds write in sensor_hub_get_feature
Xiubo Li (2):
ceph: fix UAF in __kick_flushing_caps() on cf entry freed during unlock
ceph: revalidate ki_pos for O_APPEND writes after cap acquisition
Xixin Liu (1):
nvmet-rdma: fix queue leak when connect backlog is exceeded
Xu Rao (4):
HID: roccat: free buffered reports when destroying device
mtd: mtdoops: free page bitmap when the backing MTD is removed
media: v4l2-async: avoid deleting unlinked ASC entry on link error
ALSA: hda: Fix connection list comparison in proc output
Xu Yang (2):
device property: fix infinite loop in fwnode_for_each_child_node()
usb: gadget: f_uac1_legacy: remove broken string configfs attributes
Xuanqiang Luo (1):
net: ravb: avoid dereferencing an invalid PTP clock
Yajun Deng (1):
USB: core: Use device_driver directly in struct usb_driver and usb_device_driver
Yang Shi (1):
selftests: arm64: add hugetlb mte tests
Yang Wang (1):
drm/amdgpu: check thunderbolt before switcheroo registration
Yang Xiuwei (2):
scsi: sd: Fix sd_done() sense handling condition
nvme/ioctl: check SUBMIT_IO with nvme_cmd_allowed()
Yao Kai (1):
futex: Prevent rcuwait use-after-free during requeue PI
Yehyeong Lee (10):
nvme: zero the discard fallback page
nvme-tcp: do not accept C2HData based on blk_rq_payload_bytes() alone
nvme-tcp: fix host memory disclosure on R2T for a read command
nvme-tcp: reject a read that transferred too few bytes
nvme-tcp: check the data direction of a C2HData PDU
IB/isert: reject PDUs declaring more data than was received
IB/isert: reject login PDUs declaring more data than was received
RDMA/srp: fix heap information leak on a truncated SRP_CRED_REQ
IB/isert: delay the final Login Response until the session is registered
IB/isert: post the full-feature receive buffers after session registration
Yemu Lu (1):
fat: restore original value when fat_ent_write failed
Yeoreum Yun (3):
coresight: etm4x: fix wrong check of etm4x_sspcicrn_present()
coresight: etm4x: fix underflow for usage of (nrseqstate - 1)
coresight: etm4x: fix leaked trace id
Yi Ding (2):
media: cec: meson: ao-cec-g12a: name the CEC core regmap to avoid debugfs clash
rtc: pcf8563: fix clock provider leak on unbind
Yichong Chen (13):
ecryptfs: fix tag 11 packet exact-fit size check
ecryptfs: hold msg ctx list lock when cleaning daemon queue
ecryptfs: pass packet set buffer size to parser
ecryptfs: reject too-small tag 70 packets
ecryptfs: release message context on send failure
ecryptfs: show filename encryption options
tools/bpf/bpftool: Reset vmlinux BTF after map commands
tools/bpf/bpftool: Reset vmlinux BTF after struct_ops commands
isofs: release zisofs block pointer buffer head
fanotify: report full event length for FIONREAD
fat: release buffer head after rebuilding parent
hugetlbfs: release subpool on fill_super failure
btrfs: retry verity reads for not-uptodate Merkle folios
Yicong Hui (1):
drm: Remove unused header in drm_dumb_buffers.c
Yifei Gao (1):
orangefs: fix double-free of trailer_buf on readdir copy failure
Yinhao Hu (1):
nfc: pn533: hold a reference to the request skb during send_frame
Yonatan Nachum (1):
RDMA/efa: Fix PBL chunk length computation
Yosry Ahmed (2):
KVM: nVMX: Always flush vpid02 on first use
KVM: nVMX: Service local TLB flushes on failed nested VM-Enter
Youngjae Kwon (1):
fsnotify: Fix stale object mask after concurrent mark updates
Yousef Alhouseen (2):
ipmi: ipmb: validate write message length
HID: synchronize input before cleaning up a failed probe
Yu Kuai (2):
md/raid5: round bitmap stripes with sector division
md: avoid stale clone I/O accounting timestamps
Yu Peng (1):
perf metricgroup: Fix metric expression copy leaks
Yuan Chen (1):
bpftool: Fix double close in map dump
Yuanshen Cao (1):
iio: light: tsl2772: fix ALS calibscale readback
Yue Haibing (1):
rcu: Remove unused function declaration rcu_eqs_special_set()
Yuho Choi (12):
bus: ti-sysc: Fix /chosen node reference leak
clocksource: Unregister subsystem on device registration failure
bus: mhi: ep: Fix device refcount leak in the error path of MHI device creation
serial: ma35d1: Fix OF node reference leaks in console init
hwrng: ks-sa - Fix runtime PM cleanup on registration failure
PCI: xgene: Drop unnecessary OF node reference
uio: Fix stale info pointer in failed registration path
driver core: soc: Unregister bus on early device registration failure
ACPI: video: Release PCI device reference after lookup
ubi: Fix rollback for explicit UBI device numbers
xenbus: Unregister reboot notifier on init failure
clocksource/drivers/armada: Unwind timer clock on init failure
Yun Zhou (4):
usb: gadget: f_tcm: fix deadlock in usbg_make_tpg()
ext4: fix circular lock dependency in ext4_ext_migrate
ext4: skip extra isize expansion during mount to prevent deadlock
nfc: nci: fix use of uninitialized memory in CORE_INIT_RSP parsing
Yunseong Kim (1):
ksmbd: validate ipc response length before dereferencing its fields
Yuqi Xu (1):
openvswitch: Fix CT limit teardown use-after-free
Yuvraj Singh Chauhan (1):
staging: octeon: fix free_irq dev_id mismatch in cvm_oct_rx_shutdown
Yuyang Huang (1):
ipv6: use RCU iterator to dump route exceptions
Zhan Xusheng (6):
ocfs2: fix readdir position truncation on 32-bit kernels
udf: Fix i_lenExtents truncation on 32-bit kernels
f2fs: use the mount idmap for the owner check in f2fs_xattr_advise_set()
f2fs: fix i_size when pinned fallocate partially fails
fs/ntfs3: fix integer overflow in MFT cluster validation
fs/ntfs3: reject out-of-range evcn in mi_enum_attr()
Zhang Tianci (1):
fuse: check attributes staleness on fuse_iget()
ZhangGuoDong (2):
smb/server: fix invalid pointer dereference in ksmbd_stop_durable_scavenger()
smb/server: preserve error status in smb2_handle_negotiate()
Zhao Li (2):
wifi: mac80211: send TWT teardown to peer after setup TX failure
wifi: mac80211: skip unused probe response countdown offsets
Zheng Qixing (1):
blk-cgroup: skip dying blkg in blkcg_activate_policy()
Zhenghang Xiao (1):
nfc: nci: fix double completion race in nci_data_exchange_complete
Zhenhao Wan (2):
drm/nouveau/uvmm: fix premature region free on failed OP_UNMAP_SPARSE
drm/nouveau/uvmm: clear the dirty flag when unwinding an OP_UNMAP_SPARSE
Zhi-Jun You (1):
wifi: mt76: mt7915: fix net_fill_forward_path for non-DBDC mt7986
Zhihao Cheng (9):
ubi: fastmap: Fix missed ec updating after erasing old fastmap data block
ubi: fastmap: erase_block: Get erase counter from wl_entry rather than flash
ubi: fastmap: Allocate memory with GFP_NOFS in ubi_update_fastmap
ubi: Replace erase_block() with sync_erase()
UBI: Preserve torture flag when rescheduling failed erasures
UBI: fastmap: Pass to_be_tortured when reusing old fastmap PEBs
ubi: fastmap: Wait until there are enough free PEBs before filling pools
ubi: fastmap: Fix lapsed wear leveling for first 64 PEBs
ubi: fastmap: Add module parameter to control reserving filling pool PEBs
Zhiling Zou (6):
ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()
ip6_gre: fix hardware header length for NBMA tunnels
orangefs: skip leading spaces before parsing client debug masks
net: cap advertised IP tunnel headroom
seg6: reset IP6CB after IPv6 decapsulation
batman-adv: fix stale receive device on merged fragments
Zhongqiu Han (1):
cpufreq: schedutil: Fix self-contradictory comment in sugov_iowait_apply()
Zhu Ling (1):
ACPI: EC: Avoid _REG disconnect on GPIO IRQ defer
Zhu Yanjun (1):
RDMA/cma: Fix WARNING in res_to_rt
Zhushuai Yin (1):
crypto: hisilicon/sec2 - fix CCM algorithm long packet failure
Zi Yan (1):
xarray: honor XA_FLAGS_ACCOUNT in xas_split_alloc()
Zide Chen (2):
perf/x86/intel/uncore: Fix die ID init and look up bugs
perf/x86/intel/uncore: Keep PCI PMUs working when MMIO/MSR setup fails
Zijun Hu (5):
Bluetooth: btqca: Fix qca_set_bdaddr() waiting for wrong HCI event
Bluetooth: btusb: Fix BD_ADDR byte order in btusb_set_bdaddr_wcn6855()
Bluetooth: btusb: Record matched usb_device_id into btusb_data
Bluetooth: btusb: QCA: Fix populating devcoredump fields on unenabled devices
Bluetooth: btintel: Fix diagnostics event detection
Zizhi Wo (5):
null_blk: use DEFINE_MUTEX for the file-scope mutex
null_blk: register configfs subsystem after creating default devices
null_blk: free global tag_set on init error path
null_blk: reject per-device queue resize for shared tag set
null_blk: serialize configfs attribute stores with the lock
bui duc phuc (3):
ASoC: pxa: Use devm_clk_get_optional() for extclk clock
net: ethernet: sun4i-emac: Fix IRQ error handling
net: fec: only stop PTP if it was initialized
caina (1):
Revert "irqchip/mbigen: Fix mbigen node address layout"
e.kubanski (1):
xsk: Fix offset calculation in unaligned mode
liyouhong (1):
dm-era: fix shadowed superblock leak on take-snap failure
longlong yan (1):
clk/x86: pmc_atom: add kasprintf return value check
zhouminqiang (1):
mtd: part: reject MTDPART_OFS_RETAIN in mtd_add_partition()
胡连勤 (1):
block: set QUEUE_FLAG_DYING unconditionally in blk_mark_disk_dead()
谢致邦 (XIE Zhibang) (1):
HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Linux 6.6.157
2026-09-14 11:58 Linux 6.6.157 Greg Kroah-Hartman
@ 2026-09-14 11:58 ` Greg Kroah-Hartman
0 siblings, 0 replies; 2+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-14 11:58 UTC (permalink / raw)
To: linux-kernel, akpm, torvalds, stable; +Cc: lwn, jslaby, Greg Kroah-Hartman
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy b/Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy
index b2eaefd9bc49..6a681d219f43 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1_legacy
@@ -5,8 +5,5 @@ Description:
The attributes:
audio_buf_size - audio buffer size
- fn_cap - capture pcm device file name
- fn_cntl - control device file name
- fn_play - playback pcm device file name
req_buf_size - ISO OUT endpoint request buffer size
req_count - ISO OUT endpoint request count
diff --git a/Documentation/ABI/testing/sysfs-bus-nvdimm b/Documentation/ABI/testing/sysfs-bus-nvdimm
index 64eb8f4c6a41..46dafd8482b9 100644
--- a/Documentation/ABI/testing/sysfs-bus-nvdimm
+++ b/Documentation/ABI/testing/sysfs-bus-nvdimm
@@ -48,7 +48,8 @@ What: /sys/bus/nd/devices/nmemX/cxl/id
Date: November 2022
KernelVersion: 6.2
Contact: Dave Jiang <dave.jiang@intel.com>
-Description: (RO) Show the id (serial) of the device. This is CXL specific.
+Description: (RO) Show the id (serial) of the device, formatted as an
+ unsigned 64-bit decimal value. This is CXL specific.
What: /sys/bus/nd/devices/nmemX/cxl/provider
Date: November 2022
diff --git a/Documentation/hwmon/emc1403.rst b/Documentation/hwmon/emc1403.rst
index 0de9616b24ed..ebf2435a76a6 100644
--- a/Documentation/hwmon/emc1403.rst
+++ b/Documentation/hwmon/emc1403.rst
@@ -45,6 +45,17 @@ Supported chips:
- https://ww1.microchip.com/downloads/en/DeviceDoc/1423_1424.pdf
+ * SMSC / Microchip EMC1428, EMC1438
+
+ Addresses scanned: I2C 0x18, 0x4c, 0x4d
+
+ Prefix: 'emc1428', 'emc1438'
+
+ Datasheets:
+
+ - https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005275A.pdf
+ - https://ww1.microchip.com/downloads/en/DeviceDoc/EMC1438%20DS%20Rev.%201.0%20(04-29-10).pdf
+
Author:
Kalhan Trisal <kalhan.trisal@intel.com
@@ -53,17 +64,17 @@ Description
-----------
The Standard Microsystems Corporation (SMSC) / Microchip EMC14xx chips
-contain up to four temperature sensors. EMC14x2 support two sensors
+contain up to eight temperature sensors. EMC14x2 support two sensors
(one internal, one external). EMC14x3 support three sensors (one internal,
-two external), and EMC14x4 support four sensors (one internal, three
-external).
+two external), EMC14x4 support four sensors (one internal, three external),
+and EMC14x8 support eight sensors (one internal, seven external).
The chips implement three limits for each sensor: low (tempX_min), high
(tempX_max) and critical (tempX_crit.) The chips also implement an
-hysteresis mechanism which applies to all limits. The relative difference
-is stored in a single register on the chip, which means that the relative
-difference between the limit and its hysteresis is always the same for
-all three limits.
+hysteresis mechanism which applies to high and critical limits. The relative
+difference is stored in a single register on the chip, which means that the
+relative difference between the limit and its hysteresis is always the same
+for high and critical limits.
This implementation detail implies the following:
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index 394cd226bfae..4916d605c31f 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -702,9 +702,6 @@ The uac1 function provides these attributes in its function directory:
=============== ====================================
audio_buf_size audio buffer size
- fn_cap capture pcm device file name
- fn_cntl control device file name
- fn_play playback pcm device file name
req_buf_size ISO OUT endpoint request buffer size
req_count ISO OUT endpoint request count
=============== ====================================
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
index 1e7265155715..fb0fe51ad0f5 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
@@ -79,15 +79,25 @@ Colorimetry Control IDs
- ``white_point_x``
- Specifies the normalized x chromaticity coordinate of the white
point of the mastering display in increments of 0.00002.
+ When both ``white_point_x`` and ``white_point_y`` are zero,
+ the white point chromaticity is unknown. If either coordinate is
+ non-zero, both coordinates shall be within their valid ranges.
* - __u16
- ``white_point_y``
- Specifies the normalized y chromaticity coordinate of the white
point of the mastering display in increments of 0.00002.
+ When both ``white_point_x`` and ``white_point_y`` are zero,
+ the white point chromaticity is unknown. If either coordinate is
+ non-zero, both coordinates shall be within their valid ranges.
* - __u32
- ``max_luminance``
- Specifies the nominal maximum display luminance of the mastering
display in units of 0.0001 cd/m\ :sup:`2`.
+ A value of zero indicates that the nominal maximum display
+ luminance is unknown.
* - __u32
- ``min_luminance``
- - specifies the nominal minimum display luminance of the mastering
+ - Specifies the nominal minimum display luminance of the mastering
display in units of 0.0001 cd/m\ :sup:`2`.
+ A value of zero indicates that the nominal minimum display
+ luminance is unknown.
diff --git a/Makefile b/Makefile
index 5e6219652cd7..cda631fef608 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 6
-SUBLEVEL = 156
+SUBLEVEL = 157
EXTRAVERSION =
NAME = Pinguïn Aangedreven
diff --git a/arch/alpha/include/uapi/asm/fpu.h b/arch/alpha/include/uapi/asm/fpu.h
index cea9eafa056f..d28dc36786e2 100644
--- a/arch/alpha/include/uapi/asm/fpu.h
+++ b/arch/alpha/include/uapi/asm/fpu.h
@@ -101,7 +101,12 @@ ieee_swcr_to_fpcr(unsigned long sw)
| IEEE_TRAP_ENABLE_OVF)) << 48;
fp |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57;
fp |= (sw & IEEE_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0);
- fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41;
+ /*
+ * Disable denormal operand traps only when denormal inputs are to be
+ * flushed to zero. Otherwise they must keep trapping, so that /S
+ * instructions reach the kernel emulation handler.
+ */
+ fp |= (sw & IEEE_MAP_DMZ ? FPCR_DNOD : 0);
return fp;
}
@@ -116,7 +121,6 @@ ieee_fpcr_to_swcr(unsigned long fp)
| IEEE_TRAP_ENABLE_OVF);
sw |= (~fp >> 57) & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE);
sw |= (fp >> 47) & IEEE_MAP_UMZ;
- sw |= (~fp >> 41) & IEEE_TRAP_ENABLE_DNO;
return sw;
}
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 22187531abb9..67c60a69b6ef 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -364,17 +364,17 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
switch(size) {
case 1:
- outb(port, val);
+ outb(val, port);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- outw(port, val);
+ outw(val, port);
return 2;
case 4:
if (port & 3)
return -EINVAL;
- outl(port, val);
+ outl(val, port);
return 4;
}
return -EINVAL;
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c
index 1f99b03effc2..a37707e05e34 100644
--- a/arch/alpha/kernel/sys_marvel.c
+++ b/arch/alpha/kernel/sys_marvel.c
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
*/
printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid);
+ /* Set up the lsi irqs. */
+ for (i = 0; i < 128; ++i) {
+ irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
+ /* Set up the msi irqs. */
+ for (i = 128; i < (128 + 512); ++i) {
+ irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
raw_spin_lock(&io7->irq_lock);
/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
- /* Set up the lsi irqs. */
- for (i = 0; i < 128; ++i) {
- irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
/* Disable the implemented irqs in hardware. */
for (i = 0; i < 0x60; ++i)
init_one_io7_lsi(io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
init_one_io7_lsi(io7, 0x74, boot_cpuid);
init_one_io7_lsi(io7, 0x75, boot_cpuid);
-
- /* Set up the msi irqs. */
- for (i = 128; i < (128 + 512); ++i) {
- irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
for (i = 0; i < 16; ++i)
init_one_io7_msi(io7, i, boot_cpuid);
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index de72bd837c5a..175be96f5644 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -198,12 +198,12 @@ static long dummy_emul(void) { return 0; }
long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask)
= (void *)dummy_emul;
EXPORT_SYMBOL_GPL(alpha_fp_emul_imprecise);
-long (*alpha_fp_emul) (unsigned long pc)
+long (*alpha_fp_emul) (unsigned long pc, unsigned long summary)
= (void *)dummy_emul;
EXPORT_SYMBOL_GPL(alpha_fp_emul);
#else
long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask);
-long alpha_fp_emul (unsigned long pc);
+long alpha_fp_emul (unsigned long pc, unsigned long summary);
#endif
asmlinkage void
@@ -217,7 +217,7 @@ do_entArith(unsigned long summary, unsigned long write_mask,
emulate the instruction. If the processor supports
precise exceptions, we don't have to search. */
if (!amask(AMASK_PRECISE_TRAP))
- si_code = alpha_fp_emul(regs->pc - 4);
+ si_code = alpha_fp_emul(regs->pc - 4, summary);
else
si_code = alpha_fp_emul_imprecise(regs, write_mask);
if (si_code == 0)
diff --git a/arch/alpha/math-emu/math.c b/arch/alpha/math-emu/math.c
index 4212258f3cfd..14b0bc325914 100644
--- a/arch/alpha/math-emu/math.c
+++ b/arch/alpha/math-emu/math.c
@@ -57,13 +57,13 @@ MODULE_DESCRIPTION("FP Software completion module");
MODULE_LICENSE("GPL v2");
extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long);
-extern long (*alpha_fp_emul) (unsigned long pc);
+extern long (*alpha_fp_emul) (unsigned long pc, unsigned long summary);
static long (*save_emul_imprecise)(struct pt_regs *, unsigned long);
-static long (*save_emul) (unsigned long pc);
+static long (*save_emul) (unsigned long pc, unsigned long summary);
long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long);
-long do_alpha_fp_emul(unsigned long);
+long do_alpha_fp_emul(unsigned long, unsigned long);
static int alpha_fp_emul_init_module(void)
{
@@ -91,7 +91,22 @@ module_exit(alpha_fp_emul_cleanup_module);
/*
- * Emulate the floating point instruction at address PC. Returns -1 if the
+ * Exception bits of the exception summary register (EXC_SUM). Bit 0 is the
+ * software completion bit; bits 1 through 5 report the exceptions the
+ * hardware attributed to the trapping instruction, and lie at the same
+ * positions as the corresponding IEEE_TRAP_ENABLE_* bits.
+ */
+#define EXC_SUM_INV (1UL << 1)
+#define EXC_SUM_DZE (1UL << 2)
+#define EXC_SUM_OVF (1UL << 3)
+#define EXC_SUM_UNF (1UL << 4)
+#define EXC_SUM_INE (1UL << 5)
+#define EXC_SUM_MASK (EXC_SUM_INV | EXC_SUM_DZE | EXC_SUM_OVF \
+ | EXC_SUM_UNF | EXC_SUM_INE)
+
+/*
+ * Emulate the floating point instruction at address PC. SUMMARY is the
+ * exception summary register the trap was delivered with. Returns -1 if the
* instruction to be emulated is illegal (such as with the opDEC trap), else
* the SI_CODE for a SIGFPE signal, else 0 if everything's ok.
*
@@ -100,7 +115,7 @@ module_exit(alpha_fp_emul_cleanup_module);
* stick the result of the operation into the appropriate register.
*/
long
-alpha_fp_emul (unsigned long pc)
+alpha_fp_emul (unsigned long pc, unsigned long summary)
{
FP_DECL_EX;
FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
@@ -305,12 +320,56 @@ alpha_fp_emul (unsigned long pc)
swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
current_thread_info()->ieee_state
|= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
+ }
- /* Update hardware control register. */
- fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
- fpcr |= ieee_swcr_to_fpcr(swcr);
- wrfpcr(fpcr);
+ /*
+ * EV6 records exception status bits in the FPCR before delivering the
+ * software completion trap, and swcr_update_status() above merged them
+ * into SWCR. Some can be wrong for the instruction we just emulated:
+ * a CVTTS of a value exactly representable as a subnormal sets FPCR_UNF
+ * even though the result is exact. Clear the exceptions the trap
+ * reported but that soft-fp did not raise.
+ */
+ if (implver() == IMPLVER_EV6) {
+ unsigned long spurious = summary & EXC_SUM_MASK;
+
+ if (spurious & (EXC_SUM_UNF | EXC_SUM_OVF)) {
+ /*
+ * EXC_SUM reports only the underflow or overflow,
+ * but the hardware sets INE alongside it in the FPCR.
+ */
+ spurious |= EXC_SUM_INE;
+ } else if (!spurious) {
+ /*
+ * No exception reported, so this was a denormal
+ * operand trap, for which INE and UNF can be
+ * fabricated as well.
+ */
+ spurious = EXC_SUM_INE | EXC_SUM_UNF;
+ }
+ /*
+ * Never clear an exception software has confirmed. Every
+ * instruction that genuinely raises one traps for software
+ * completion and is recorded in ieee_state above, so a bit
+ * found there -- including one just set from _fex -- belongs
+ * to this or an earlier instruction and must survive.
+ */
+ spurious &= ~(current_thread_info()->ieee_state
+ >> IEEE_STATUS_TO_EXCSUM_SHIFT);
+
+ swcr &= ~(spurious << IEEE_STATUS_TO_EXCSUM_SHIFT);
+ }
+
+ /*
+ * Update hardware control register. This has to happen even when
+ * soft-fp raised nothing, to clear any fabricated bits.
+ */
+ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
+ fpcr |= ieee_swcr_to_fpcr(swcr);
+ wrfpcr(fpcr);
+
+ if (_fex) {
/* Do we generate a signal? */
_fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK;
si_code = 0;
@@ -392,9 +451,16 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
break;
}
if (!write_mask) {
- /* Re-execute insns in the trap-shadow. */
+ /*
+ * Re-execute insns in the trap-shadow. Pass no
+ * exception summary: it describes the trap, which
+ * was taken anywhere in the shadow, and so is not
+ * attribution for this instruction. Nothing is
+ * lost, since only EV6 -- which traps precisely and
+ * never comes this way -- needs it.
+ */
regs->pc = trigger_pc + 4;
- si_code = alpha_fp_emul(trigger_pc);
+ si_code = alpha_fp_emul(trigger_pc, 0);
goto egress;
}
trigger_pc -= 4;
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f254900f0ddd..6028c93f0e41 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -87,7 +87,7 @@ config ARM
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE
select HAVE_ARM_SMCCC if CPU_V7
- select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32
+ select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 && !CPU_32v3
select HAVE_CONTEXT_TRACKING_USER
select HAVE_C_RECORDMCOUNT
select HAVE_BUILDTIME_MCOUNT_SORT
diff --git a/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi b/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
index 51a6464aab9a..cabf619c2e21 100644
--- a/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
@@ -185,7 +185,7 @@ de: display-engine {
pmu {
compatible = "arm,cortex-a8-pmu";
- interrupts = <3>;
+ interrupts = <66>;
};
reserved-memory {
diff --git a/arch/arm/boot/dts/marvell/armada-388-helios4.dts b/arch/arm/boot/dts/marvell/armada-388-helios4.dts
index ec134e22bae3..003b0708e965 100644
--- a/arch/arm/boot/dts/marvell/armada-388-helios4.dts
+++ b/arch/arm/boot/dts/marvell/armada-388-helios4.dts
@@ -169,6 +169,7 @@ expander0: gpio-expander@20 {
gpio-controller;
#gpio-cells = <2>;
reg = <0x20>;
+ vcc-supply = <®_3p3v>;
pinctrl-names = "default";
pinctrl-0 = <&pca0_pins>;
interrupt-parent = <&gpio0>;
@@ -201,6 +202,10 @@ temp_sensor: temp@4c {
reg = <0x4c>;
vcc-supply = <®_3p3v>;
};
+
+ eeprom@53 {
+ vcc-supply = <®_3p3v>;
+ };
};
i2c@11100 {
@@ -217,13 +222,17 @@ sata@a8000 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
+ ahci-supply = <®_3p3v>;
+ phy-supply = <®_3p3v>;
sata0: sata-port@0 {
reg = <0>;
+ target-supply = <®_5p0v_hdd>;
};
sata1: sata-port@1 {
reg = <1>;
+ target-supply = <®_5p0v_hdd>;
};
};
@@ -231,13 +240,17 @@ sata@e0000 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
+ ahci-supply = <®_3p3v>;
+ phy-supply = <®_3p3v>;
sata2: sata-port@0 {
reg = <0>;
+ target-supply = <®_5p0v_hdd>;
};
sata3: sata-port@1 {
reg = <1>;
+ target-supply = <®_5p0v_hdd>;
};
};
diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c
index 59a8e8cc4469..c3c80b4c3d53 100644
--- a/arch/arm/mach-imx/src.c
+++ b/arch/arm/mach-imx/src.c
@@ -171,6 +171,7 @@ void __init imx_src_init(void)
if (!np)
return;
src_base = of_iomap(np, 0);
+ of_node_put(np);
WARN_ON(!src_base);
/*
@@ -195,6 +196,7 @@ void __init imx7_src_init(void)
return;
src_base = of_iomap(np, 0);
+ of_node_put(np);
if (!src_base)
return;
@@ -203,6 +205,7 @@ void __init imx7_src_init(void)
return;
gpc_base = of_iomap(np, 0);
+ of_node_put(np);
if (!gpc_base)
return;
}
diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c
index 304ea61a0716..35ed3569c5a3 100644
--- a/arch/arm/mach-lpc32xx/common.c
+++ b/arch/arm/mach-lpc32xx/common.c
@@ -106,7 +106,7 @@ void __init lpc32xx_map_io(void)
iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc));
}
-static int __init lpc32xx_check_uid(void)
+void __init lpc32xx_check_uid(void)
{
u32 uid[4];
@@ -119,7 +119,4 @@ static int __init lpc32xx_check_uid(void)
system_serial_low = uid[0];
system_serial_high = uid[1];
}
-
- return 1;
}
-arch_initcall(lpc32xx_check_uid);
diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h
index 32f0ad217807..06b20bea324e 100644
--- a/arch/arm/mach-lpc32xx/common.h
+++ b/arch/arm/mach-lpc32xx/common.h
@@ -16,6 +16,8 @@
* Other arch specific structures and functions
*/
extern void __init lpc32xx_map_io(void);
+extern void __init lpc32xx_check_uid(void);
+extern void __init lpc32xx_pm_init(void);
extern void __init lpc32xx_serial_init(void);
/*
diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c
index 66701bf43248..ddc6333ca55d 100644
--- a/arch/arm/mach-lpc32xx/phy3250.c
+++ b/arch/arm/mach-lpc32xx/phy3250.c
@@ -71,6 +71,8 @@ static const struct of_dev_auxdata lpc32xx_auxdata_lookup[] __initconst = {
static void __init lpc3250_machine_init(void)
{
+ lpc32xx_check_uid();
+ lpc32xx_pm_init();
lpc32xx_serial_init();
of_platform_default_populate(NULL, lpc32xx_auxdata_lookup, NULL);
diff --git a/arch/arm/mach-lpc32xx/pm.c b/arch/arm/mach-lpc32xx/pm.c
index 2572bd89a5e8..9b5c5e1462ed 100644
--- a/arch/arm/mach-lpc32xx/pm.c
+++ b/arch/arm/mach-lpc32xx/pm.c
@@ -120,7 +120,7 @@ static const struct platform_suspend_ops lpc32xx_pm_ops = {
#define EMC_DYN_MEM_CTRL_OFS 0x20
#define EMC_SRMMC (1 << 3)
#define EMC_CTRL_REG io_p2v(LPC32XX_EMC_BASE + EMC_DYN_MEM_CTRL_OFS)
-static int __init lpc32xx_pm_init(void)
+void __init lpc32xx_pm_init(void)
{
/*
* Setup SDRAM self-refresh clock to automatically disable o
@@ -129,7 +129,4 @@ static int __init lpc32xx_pm_init(void)
__raw_writel(__raw_readl(EMC_CTRL_REG) | EMC_SRMMC, EMC_CTRL_REG);
suspend_set_ops(&lpc32xx_pm_ops);
-
- return 0;
}
-arch_initcall(lpc32xx_pm_init);
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 87ed5da30e44..d6e3121c6a3f 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -205,7 +205,11 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
pr_err("8<--- cut here ---\n");
pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
tsk->comm, sig, addr, fsr);
- show_pte(KERN_ERR, tsk->mm, addr);
+ if (likely(addr < TASK_SIZE)) {
+ mmap_write_lock(tsk->mm);
+ show_pte(KERN_ERR, tsk->mm, addr);
+ mmap_write_unlock(tsk->mm);
+ }
show_regs(regs);
}
#endif
@@ -586,7 +590,15 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
pr_alert("8<--- cut here ---\n");
pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
- show_pte(KERN_ALERT, current->mm, addr);
+ if (likely(user_mode(regs))) {
+ if (addr < TASK_SIZE) {
+ mmap_write_lock(current->mm);
+ show_pte(KERN_ALERT, current->mm, addr);
+ mmap_write_unlock(current->mm);
+ }
+ } else {
+ show_pte(KERN_ALERT, current->mm, addr);
+ }
arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
fsr, 0);
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
index b0885a389951..75116ae701f8 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
@@ -202,8 +202,8 @@ accelerometer@68 {
interrupts = <7 5 IRQ_TYPE_EDGE_RISING>; /* PH5 */
vdd-supply = <®_dldo1>;
vddio-supply = <®_dldo1>;
- mount-matrix = "0", "1", "0",
- "-1", "0", "0",
+ mount-matrix = "0", "-1", "0",
+ "1", "0", "0",
"0", "0", "1";
};
};
diff --git a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
index df16eead2c80..50efa48e85a9 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
@@ -433,6 +433,10 @@ gpio_speaker: gpio-controller@1f {
};
};
+&mipi_pcie_analog_dphy {
+ status = "okay";
+};
+
&pdm {
pinctrl-0 = <&pdm_dclk_a14_pins>, <&pdm_din0_pins>,
<&pdm_din1_pins>, <&pdm_din2_pins>, <&pdm_din3_pins>;
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts b/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts
index 805ef2d79b40..1cbaae0310d6 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts
@@ -292,7 +292,8 @@ connector {
&usb3 {
status = "okay";
- phys = <&comphy2 0>;
+ phys = <&usb2_utmi_otg_phy>, <&comphy2 0>;
+ phy-names = "usb2-phy", "usb3-phy";
};
&mdio {
diff --git a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts
index dbad8f57f2fa..f145e8ee5f80 100644
--- a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts
+++ b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts
@@ -39,7 +39,7 @@ &blsp2_i2c3 {
haptics: drv2604@5a {
compatible = "ti,drv2604";
reg = <0x5a>;
- enable-gpio = <&tlmm 93 0x00>;
+ enable-gpios = <&tlmm 93 GPIO_ACTIVE_HIGH>;
mode = <DRV260X_LRA_MODE>;
library-sel = <DRV260X_LIB_LRA>;
pinctrl-names = "default","sleep";
diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi
index 876c6921ddf0..7604b4725de6 100644
--- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi
@@ -224,10 +224,6 @@ rmi4-f11@11 {
};
};
-&blsp1_i2c5_sleep {
- bias-disable;
-};
-
&blsp1_uart3 {
status = "okay";
diff --git a/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts b/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts
index 437b30cc8bdc..7eb8456aed1b 100644
--- a/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts
+++ b/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts
@@ -217,11 +217,6 @@ rmi4-f1a@1a {
};
};
-&blsp1_i2c5_sleep {
- /delete-property/ bias-pull-up;
- bias-disable;
-};
-
&blsp1_uart3 {
status = "okay";
diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi
index 7fcc15b6946a..c0dbc4f05b90 100644
--- a/arch/arm64/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi
@@ -1183,7 +1183,7 @@ blsp1_i2c1_sleep: blsp1-i2c1-sleep-state-state {
pins = "gpio2", "gpio3";
function = "blsp_i2c1";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_i2c2_default: blsp1-i2c2-default-state {
@@ -1197,7 +1197,7 @@ blsp1_i2c2_sleep: blsp1-i2c2-sleep-state-state {
pins = "gpio32", "gpio33";
function = "blsp_i2c2";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_i2c3_default: blsp1-i2c3-default-state {
@@ -1211,7 +1211,7 @@ blsp1_i2c3_sleep: blsp1-i2c3-sleep-state {
pins = "gpio47", "gpio48";
function = "blsp_i2c3";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_i2c4_default: blsp1-i2c4-default-state {
@@ -1225,7 +1225,7 @@ blsp1_i2c4_sleep: blsp1-i2c4-sleep-state {
pins = "gpio10", "gpio11";
function = "blsp_i2c4";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_i2c5_default: blsp1-i2c5-default-state {
@@ -1239,7 +1239,7 @@ blsp1_i2c5_sleep: blsp1-i2c5-sleep-state {
pins = "gpio87", "gpio88";
function = "blsp_i2c5";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_i2c6_default: blsp1-i2c6-default-state {
@@ -1253,7 +1253,7 @@ blsp1_i2c6_sleep: blsp1-i2c6-sleep-state {
pins = "gpio43", "gpio44";
function = "blsp_i2c6";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp1_spi_b_default: blsp1-spi-b-default-state {
@@ -1318,7 +1318,7 @@ blsp2_i2c1_sleep: blsp2-i2c1-sleep-state {
pins = "gpio55", "gpio56";
function = "blsp_i2c7";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_i2c2_default: blsp2-i2c2-default-state {
@@ -1332,7 +1332,7 @@ blsp2_i2c2_sleep: blsp2-i2c2-sleep-state {
pins = "gpio6", "gpio7";
function = "blsp_i2c8";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_i2c3_default: blsp2-i2c3-default-state {
@@ -1346,7 +1346,7 @@ blsp2_i2c3_sleep: blsp2-i2c3-sleep-state {
pins = "gpio51", "gpio52";
function = "blsp_i2c9";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_i2c4_default: blsp2-i2c4-default-state {
@@ -1360,7 +1360,7 @@ blsp2_i2c4_sleep: blsp2-i2c4-sleep-state {
pins = "gpio67", "gpio68";
function = "blsp_i2c10";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_i2c5_default: blsp2-i2c5-default-state {
@@ -1374,7 +1374,7 @@ blsp2_i2c5_sleep: blsp2-i2c5-sleep-state {
pins = "gpio60", "gpio61";
function = "blsp_i2c11";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_i2c6_default: blsp2-i2c6-default-state {
@@ -1388,7 +1388,7 @@ blsp2_i2c6_sleep: blsp2-i2c6-sleep-state {
pins = "gpio83", "gpio84";
function = "blsp_i2c12";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
blsp2_spi1_default: blsp2-spi1-default-state {
diff --git a/arch/arm64/boot/dts/qcom/sc8180x-lenovo-flex-5g.dts b/arch/arm64/boot/dts/qcom/sc8180x-lenovo-flex-5g.dts
index abc66613ccaa..33de3d434e3a 100644
--- a/arch/arm64/boot/dts/qcom/sc8180x-lenovo-flex-5g.dts
+++ b/arch/arm64/boot/dts/qcom/sc8180x-lenovo-flex-5g.dts
@@ -159,14 +159,7 @@ cdsp_mem: cdsp-region@98900000 {
};
};
- vph_pwr: vph-pwr-regulator {
- compatible = "regulator-fixed";
- regulator-name = "vph_pwr";
- regulator-min-microvolt = <3700000>;
- regulator-max-microvolt = <3700000>;
- };
-
- vreg_s4a_1p8: pm8150-s4-regulator {
+ vreg_s4a_1p8: regulator-pm8150-s4 {
compatible = "regulator-fixed";
regulator-name = "vreg_s4a_1p8";
@@ -179,6 +172,13 @@ vreg_s4a_1p8: pm8150-s4-regulator {
vin-supply = <&vph_pwr>;
};
+ vph_pwr: regulator-vph-pwr {
+ compatible = "regulator-fixed";
+ regulator-name = "vph_pwr";
+ regulator-min-microvolt = <3700000>;
+ regulator-max-microvolt = <3700000>;
+ };
+
usbprim-sbu-mux {
compatible = "pericom,pi3usb102", "gpio-sbu-mux";
@@ -356,7 +356,7 @@ zap-shader {
&i2c1 {
clock-frequency = <100000>;
- pinctrl-0 = <&i2c1_active>, <&i2c1_hid_active>;
+ pinctrl-0 = <&i2c1_active>;
pinctrl-names = "default";
status = "okay";
@@ -367,13 +367,16 @@ hid@10 {
hid-descr-addr = <0x1>;
interrupts-extended = <&tlmm 122 IRQ_TYPE_LEVEL_LOW>;
+
+ pinctrl-0 = <&ts_int_default>;
+ pinctrl-names = "default";
};
};
&i2c7 {
clock-frequency = <100000>;
- pinctrl-0 = <&i2c7_active>, <&i2c7_hid_active>;
+ pinctrl-0 = <&i2c7_active>;
pinctrl-names = "default";
status = "okay";
@@ -384,6 +387,9 @@ hid@5 {
hid-descr-addr = <0x20>;
interrupts-extended = <&tlmm 37 IRQ_TYPE_LEVEL_LOW>;
+
+ pinctrl-0 = <&kb_int_default>;
+ pinctrl-names = "default";
};
hid@2c {
@@ -392,6 +398,9 @@ hid@2c {
hid-descr-addr = <0x20>;
interrupts-extended = <&tlmm 24 IRQ_TYPE_LEVEL_LOW>;
+
+ pinctrl-0 = <&tp_int_default>;
+ pinctrl-names = "default";
};
};
@@ -466,6 +475,10 @@ &pcie3_phy {
status = "okay";
};
+&pmc8180_pwrkey {
+ status = "okay";
+};
+
&pmc8180c_lpg {
status = "okay";
};
@@ -668,14 +681,6 @@ i2c1_active: i2c1-active-state {
drive-strength = <2>;
};
- i2c1_hid_active: i2c1-hid-active-state {
- pins = "gpio122";
- function = "gpio";
-
- bias-pull-up;
- drive-strength = <2>;
- };
-
i2c7_active: i2c7-active-state {
pins = "gpio98", "gpio99";
function = "qup7";
@@ -684,8 +689,8 @@ i2c7_active: i2c7-active-state {
drive-strength = <2>;
};
- i2c7_hid_active: i2c7-hid-active-state {
- pins = "gpio37", "gpio24";
+ kb_int_default: kb-int-default-state {
+ pins = "gpio37";
function = "gpio";
bias-pull-up;
@@ -717,6 +722,22 @@ wake-n-pins {
};
};
+ tp_int_default: tp-int-default-state {
+ pins = "gpio24";
+ function = "gpio";
+
+ bias-pull-up;
+ drive-strength = <2>;
+ };
+
+ ts_int_default: ts-int-default-state {
+ pins = "gpio122";
+ function = "gpio";
+
+ bias-pull-up;
+ drive-strength = <2>;
+ };
+
usbprim_sbu_default: usbprim-sbu-state {
oe-n-pins {
pins = "gpio152";
diff --git a/arch/arm64/boot/dts/qcom/sc8180x-pmics.dtsi b/arch/arm64/boot/dts/qcom/sc8180x-pmics.dtsi
index ddc84282f142..68af5888b034 100644
--- a/arch/arm64/boot/dts/qcom/sc8180x-pmics.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8180x-pmics.dtsi
@@ -77,7 +77,7 @@ pmc8180_0: pmic@0 {
pon: pon@800 {
compatible = "qcom,pm8916-pon";
reg = <0x0800>;
- pwrkey {
+ pmc8180_pwrkey: pwrkey {
compatible = "qcom,pm8941-pwrkey";
interrupts = <0x0 0x8 0x0 IRQ_TYPE_EDGE_BOTH>;
debounce = <15625>;
diff --git a/arch/arm64/boot/dts/qcom/sc8180x-primus.dts b/arch/arm64/boot/dts/qcom/sc8180x-primus.dts
index ae008c3b0aed..0941fc6c4cf8 100644
--- a/arch/arm64/boot/dts/qcom/sc8180x-primus.dts
+++ b/arch/arm64/boot/dts/qcom/sc8180x-primus.dts
@@ -164,7 +164,7 @@ reserved-region@9a500000 {
};
};
- vreg_nvme_0p9: nvme-0p9-regulator {
+ vreg_nvme_0p9: regulator-nvme-0p9 {
compatible = "regulator-fixed";
regulator-name = "vreg_nvme_0p9";
@@ -174,7 +174,7 @@ vreg_nvme_0p9: nvme-0p9-regulator {
regulator-always-on;
};
- vreg_nvme_3p3: nvme-3p3-regulator {
+ vreg_nvme_3p3: regulator-nvme-3p3 {
compatible = "regulator-fixed";
regulator-name = "vreg_nvme_3p3";
@@ -187,7 +187,7 @@ vreg_nvme_3p3: nvme-3p3-regulator {
regulator-always-on;
};
- vdd_kb_tp_3v3: vdd-kb-tp-3v3-regulator {
+ vdd_kb_tp_3v3: regulator-vdd-kb-tp-3v3 {
compatible = "regulator-fixed";
regulator-name = "vdd_kb_tp_3v3";
regulator-min-microvolt = <3300000>;
@@ -202,7 +202,7 @@ vdd_kb_tp_3v3: vdd-kb-tp-3v3-regulator {
pinctrl-0 = <&kb_tp_3v3_en_active_state>;
};
- vph_pwr: vph-pwr-regulator {
+ vph_pwr: regulator-vph-pwr {
compatible = "regulator-fixed";
regulator-name = "vph_pwr";
regulator-min-microvolt = <3700000>;
@@ -545,6 +545,10 @@ &pcie1_phy {
status = "okay";
};
+&pmc8180_pwrkey {
+ status = "okay";
+};
+
&pmc8180c_lpg {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi
index dfeeada91b78..83a78c159027 100644
--- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi
@@ -1745,9 +1745,8 @@ pcie0: pci@1c00000 {
assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommus = <&apps_smmu 0x1d80 0x7f>;
- iommu-map = <0x0 &apps_smmu 0x1d80 0x1>,
- <0x100 &apps_smmu 0x1d81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1d80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1d81 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -1843,9 +1842,8 @@ pcie3: pci@1c08000 {
assigned-clocks = <&gcc GCC_PCIE_3_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommus = <&apps_smmu 0x1e00 0x7f>;
- iommu-map = <0x0 &apps_smmu 0x1e00 0x1>,
- <0x100 &apps_smmu 0x1e01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1e00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1e01 0x0 0x1>;
resets = <&gcc GCC_PCIE_3_BCR>;
reset-names = "pci";
@@ -1942,9 +1940,8 @@ pcie1: pci@1c10000 {
assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommus = <&apps_smmu 0x1c80 0x7f>;
- iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
- <0x100 &apps_smmu 0x1c81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c81 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
@@ -2041,9 +2038,8 @@ pcie2: pci@1c18000 {
assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommus = <&apps_smmu 0x1d00 0x7f>;
- iommu-map = <0x0 &apps_smmu 0x1d00 0x1>,
- <0x100 &apps_smmu 0x1d01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1d00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1d01 0x0 0x1>;
resets = <&gcc GCC_PCIE_2_BCR>;
reset-names = "pci";
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
index ffc4406422ae..b985d76a4cda 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
+++ b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
@@ -167,7 +167,7 @@ vreg_misc_3p3: regulator-misc-3p3 {
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
- gpio = <&pmc8280_1_gpios 2 GPIO_ACTIVE_HIGH>;
+ gpio = <&pmc8280_1_gpios 1 GPIO_ACTIVE_HIGH>;
enable-active-high;
pinctrl-names = "default";
@@ -757,7 +757,7 @@ edp_bl_reg_en: edp-bl-reg-en-state {
};
misc_3p3_reg_en: misc-3p3-reg-en-state {
- pins = "gpio2";
+ pins = "gpio1";
function = "normal";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-motorola-ocean.dts b/arch/arm64/boot/dts/qcom/sdm632-motorola-ocean.dts
index c82d6e628d2c..f5728cc5a453 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-motorola-ocean.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-motorola-ocean.dts
@@ -130,7 +130,7 @@ led-controller@36 {
led: led@1 {
reg = <1>;
- default-trigger = "backlight";
+ linux,default-trigger = "backlight";
function = LED_FUNCTION_BACKLIGHT;
led-sources = <0 1 2>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index fa91e2036dd5..0bcaeba8dafa 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2349,22 +2349,22 @@ pcie0: pci@1c00000 {
"slave_q2a",
"tbu";
- iommu-map = <0x0 &apps_smmu 0x1c10 0x1>,
- <0x100 &apps_smmu 0x1c11 0x1>,
- <0x200 &apps_smmu 0x1c12 0x1>,
- <0x300 &apps_smmu 0x1c13 0x1>,
- <0x400 &apps_smmu 0x1c14 0x1>,
- <0x500 &apps_smmu 0x1c15 0x1>,
- <0x600 &apps_smmu 0x1c16 0x1>,
- <0x700 &apps_smmu 0x1c17 0x1>,
- <0x800 &apps_smmu 0x1c18 0x1>,
- <0x900 &apps_smmu 0x1c19 0x1>,
- <0xa00 &apps_smmu 0x1c1a 0x1>,
- <0xb00 &apps_smmu 0x1c1b 0x1>,
- <0xc00 &apps_smmu 0x1c1c 0x1>,
- <0xd00 &apps_smmu 0x1c1d 0x1>,
- <0xe00 &apps_smmu 0x1c1e 0x1>,
- <0xf00 &apps_smmu 0x1c1f 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c10 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c11 0x0 0x1>,
+ <0x200 &apps_smmu 0x1c12 0x0 0x1>,
+ <0x300 &apps_smmu 0x1c13 0x0 0x1>,
+ <0x400 &apps_smmu 0x1c14 0x0 0x1>,
+ <0x500 &apps_smmu 0x1c15 0x0 0x1>,
+ <0x600 &apps_smmu 0x1c16 0x0 0x1>,
+ <0x700 &apps_smmu 0x1c17 0x0 0x1>,
+ <0x800 &apps_smmu 0x1c18 0x0 0x1>,
+ <0x900 &apps_smmu 0x1c19 0x0 0x1>,
+ <0xa00 &apps_smmu 0x1c1a 0x0 0x1>,
+ <0xb00 &apps_smmu 0x1c1b 0x0 0x1>,
+ <0xc00 &apps_smmu 0x1c1c 0x0 0x1>,
+ <0xd00 &apps_smmu 0x1c1d 0x0 0x1>,
+ <0xe00 &apps_smmu 0x1c1e 0x0 0x1>,
+ <0xf00 &apps_smmu 0x1c1f 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -2459,22 +2459,22 @@ pcie1: pci@1c08000 {
assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
- <0x100 &apps_smmu 0x1c01 0x1>,
- <0x200 &apps_smmu 0x1c02 0x1>,
- <0x300 &apps_smmu 0x1c03 0x1>,
- <0x400 &apps_smmu 0x1c04 0x1>,
- <0x500 &apps_smmu 0x1c05 0x1>,
- <0x600 &apps_smmu 0x1c06 0x1>,
- <0x700 &apps_smmu 0x1c07 0x1>,
- <0x800 &apps_smmu 0x1c08 0x1>,
- <0x900 &apps_smmu 0x1c09 0x1>,
- <0xa00 &apps_smmu 0x1c0a 0x1>,
- <0xb00 &apps_smmu 0x1c0b 0x1>,
- <0xc00 &apps_smmu 0x1c0c 0x1>,
- <0xd00 &apps_smmu 0x1c0d 0x1>,
- <0xe00 &apps_smmu 0x1c0e 0x1>,
- <0xf00 &apps_smmu 0x1c0f 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c01 0x0 0x1>,
+ <0x200 &apps_smmu 0x1c02 0x0 0x1>,
+ <0x300 &apps_smmu 0x1c03 0x0 0x1>,
+ <0x400 &apps_smmu 0x1c04 0x0 0x1>,
+ <0x500 &apps_smmu 0x1c05 0x0 0x1>,
+ <0x600 &apps_smmu 0x1c06 0x0 0x1>,
+ <0x700 &apps_smmu 0x1c07 0x0 0x1>,
+ <0x800 &apps_smmu 0x1c08 0x0 0x1>,
+ <0x900 &apps_smmu 0x1c09 0x0 0x1>,
+ <0xa00 &apps_smmu 0x1c0a 0x0 0x1>,
+ <0xb00 &apps_smmu 0x1c0b 0x0 0x1>,
+ <0xc00 &apps_smmu 0x1c0c 0x0 0x1>,
+ <0xd00 &apps_smmu 0x1c0d 0x0 0x1>,
+ <0xe00 &apps_smmu 0x1c0e 0x0 0x1>,
+ <0xf00 &apps_smmu 0x1c0f 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
index 606f7a6f4179..2c6f37864a5d 100644
--- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
+++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
@@ -41,7 +41,7 @@ chosen {
stdout-path = "serial0:115200n8";
- framebuffer0: framebuffer@a000000 {
+ framebuffer0: framebuffer@a0000000 {
compatible = "simple-framebuffer";
reg = <0 0xa0000000 0 (2340 * 1080 * 4)>;
width = <1080>;
@@ -710,8 +710,8 @@ &usb_1_hsphy {
};
&usb_1_qmpphy {
- vdda-phy-supply = <&vreg_l22a>;
- vdda-pll-supply = <&vreg_l16a>;
+ vdda-phy-supply = <&vreg_l16a>;
+ vdda-pll-supply = <&vreg_l22a>;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi
index 73ef228ff268..a506fcf4644f 100644
--- a/arch/arm64/boot/dts/qcom/sm8150.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi
@@ -1865,8 +1865,8 @@ pcie0: pci@1c00000 {
"slave_q2a",
"tbu";
- iommu-map = <0x0 &apps_smmu 0x1d80 0x1>,
- <0x100 &apps_smmu 0x1d81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1d80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1d81 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -1967,8 +1967,8 @@ pcie1: pci@1c08000 {
assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommu-map = <0x0 &apps_smmu 0x1e00 0x1>,
- <0x100 &apps_smmu 0x1e01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1e00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1e01 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
index 85e5cf3dc91e..5d49247d73a1 100644
--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi
@@ -27,7 +27,7 @@ / {
/* required for bootloader to select correct board */
qcom,msm-id = <QCOM_ID_SM8250 0x20001>; /* SM8250 v2.1 */
- qcom,board-id = <0x10008 0>;
+ qcom,board-id = <0x2f 0>;
chosen {
#address-cells = <2>;
diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
index d8086dae6e4f..dd537ab1c681 100644
--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
@@ -1895,8 +1895,8 @@ pcie0: pci@1c00000 {
"tbu",
"ddrss_sf_tbu";
- iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
- <0x100 &apps_smmu 0x1c01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c01 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -2002,8 +2002,8 @@ pcie1: pci@1c08000 {
assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
- <0x100 &apps_smmu 0x1c81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c81 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
@@ -2111,8 +2111,8 @@ pcie2: pci@1c10000 {
assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>;
assigned-clock-rates = <19200000>;
- iommu-map = <0x0 &apps_smmu 0x1d00 0x1>,
- <0x100 &apps_smmu 0x1d01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1d00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1d01 0x0 0x1>;
resets = <&gcc GCC_PCIE_2_BCR>;
reset-names = "pci";
@@ -3950,8 +3950,12 @@ venus: video-codec@aa00000 {
interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&videocc MVS0C_GDSC>,
<&videocc MVS0_GDSC>,
- <&rpmhpd RPMHPD_MX>;
- power-domain-names = "venus", "vcodec0", "mx";
+ <&rpmhpd RPMHPD_MX>,
+ <&rpmhpd RPMHPD_MMCX>;
+ power-domain-names = "venus",
+ "vcodec0",
+ "mx",
+ "mmcx";
operating-points-v2 = <&venus_opp_table>;
clocks = <&gcc GCC_VIDEO_AXI0_CLK>,
@@ -3983,24 +3987,28 @@ video-encoder {
venus_opp_table: opp-table {
compatible = "operating-points-v2";
- opp-720000000 {
- opp-hz = /bits/ 64 <720000000>;
- required-opps = <&rpmhpd_opp_low_svs>;
+ opp-240000000 {
+ opp-hz = /bits/ 64 <240000000>;
+ required-opps = <&rpmhpd_opp_svs>,
+ <&rpmhpd_opp_low_svs>;
};
- opp-1014000000 {
- opp-hz = /bits/ 64 <1014000000>;
- required-opps = <&rpmhpd_opp_svs>;
+ opp-338000000 {
+ opp-hz = /bits/ 64 <338000000>;
+ required-opps = <&rpmhpd_opp_svs>,
+ <&rpmhpd_opp_svs>;
};
- opp-1098000000 {
- opp-hz = /bits/ 64 <1098000000>;
- required-opps = <&rpmhpd_opp_svs_l1>;
+ opp-366000000 {
+ opp-hz = /bits/ 64 <366000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>,
+ <&rpmhpd_opp_svs_l1>;
};
- opp-1332000000 {
- opp-hz = /bits/ 64 <1332000000>;
- required-opps = <&rpmhpd_opp_nom>;
+ opp-444000000 {
+ opp-hz = /bits/ 64 <444000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>,
+ <&rpmhpd_opp_nom>;
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi
index 215782b1970d..8ce8e1c1f72d 100644
--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi
@@ -1552,8 +1552,8 @@ pcie0: pci@1c00000 {
"aggre1",
"aggre0";
- iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
- <0x100 &apps_smmu 0x1c01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c01 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -1635,8 +1635,8 @@ pcie1: pci@1c08000 {
"ddrss_sf_tbu",
"aggre1";
- iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
- <0x100 &apps_smmu 0x1c81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c81 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi
index 0dad7b0866ea..61c00d878ee4 100644
--- a/arch/arm64/boot/dts/qcom/sm8450.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi
@@ -1811,8 +1811,8 @@ pcie0: pci@1c00000 {
"aggre0",
"aggre1";
- iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
- <0x100 &apps_smmu 0x1c01 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c00 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c01 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -1919,8 +1919,8 @@ pcie1: pci@1c08000 {
"ddrss_sf_tbu",
"aggre1";
- iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
- <0x100 &apps_smmu 0x1c81 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
+ <0x100 &apps_smmu 0x1c81 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>;
reset-names = "pci";
diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi
index 5a79adbe8da1..7f56451b1563 100644
--- a/arch/arm64/boot/dts/qcom/sm8550.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi
@@ -1734,8 +1734,10 @@ pcie0: pci@1c00000 {
<&gem_noc MASTER_APPSS_PROC 0 &cnoc_main SLAVE_PCIE_0 0>;
interconnect-names = "pcie-mem", "cpu-pcie";
- iommu-map = <0x0 &apps_smmu 0x1400 0x1>,
- <0x100 &apps_smmu 0x1401 0x1>;
+ msi-map = <0x0 &gic_its 0x1400 0x1>,
+ <0x100 &gic_its 0x1401 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1400 0x0 0x1>,
+ <0x100 &apps_smmu 0x1401 0x0 0x1>;
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "pci";
@@ -1830,8 +1832,10 @@ pcie1: pci@1c08000 {
<&gem_noc MASTER_APPSS_PROC 0 &cnoc_main SLAVE_PCIE_1 0>;
interconnect-names = "pcie-mem", "cpu-pcie";
- iommu-map = <0x0 &apps_smmu 0x1480 0x1>,
- <0x100 &apps_smmu 0x1481 0x1>;
+ msi-map = <0x0 &gic_its 0x1480 0x1>,
+ <0x100 &gic_its 0x1481 0x1>;
+ iommu-map = <0x0 &apps_smmu 0x1480 0x0 0x1>,
+ <0x100 &apps_smmu 0x1481 0x0 0x1>;
resets = <&gcc GCC_PCIE_1_BCR>,
<&gcc GCC_PCIE_1_LINK_DOWN_BCR>;
diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
index 403a295bde6a..de5b7802fc50 100644
--- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
@@ -19,7 +19,7 @@ emmc_pwrseq: emmc-pwrseq {
compatible = "mmc-pwrseq-emmc";
pinctrl-0 = <&emmc_reset>;
pinctrl-names = "default";
- reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>;
};
leds {
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi
index cacbad35cfc8..cf9fdb7d0cfb 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi
@@ -510,8 +510,7 @@ &pci_rootport {
mvl_wifi: wifi@0,0 {
compatible = "pci1b4b,2b42";
reg = <0x0000 0x0 0x0 0x0 0x0>;
- interrupt-parent = <&gpio0>;
- interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+ interrupts-extended = <&gpio0 8 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&wlan_host_wake_l>;
wakeup-source;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
index 7ba1c28f70a9..d3a0669c5197 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
@@ -133,6 +133,18 @@ &i2s0_8ch_bus {
<3 RK_PD7 1 &pcfg_pull_none>;
};
+&i2s0_8ch_bus_bclk_off {
+ rockchip,pins =
+ <3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>,
+ <3 RK_PD1 1 &pcfg_pull_none>,
+ <3 RK_PD2 1 &pcfg_pull_none>,
+ <3 RK_PD3 1 &pcfg_pull_none>,
+ <3 RK_PD4 1 &pcfg_pull_none>,
+ <3 RK_PD5 1 &pcfg_pull_none>,
+ <3 RK_PD6 1 &pcfg_pull_none>,
+ <3 RK_PD7 1 &pcfg_pull_none>;
+};
+
&i2s1 {
pinctrl-names = "default";
pinctrl-0 = <&i2s_8ch_mclk_pin>, <&i2s1_2ch_bus>;
diff --git a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi
index 064eb062bb54..67a7336053d9 100644
--- a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi
@@ -1316,7 +1316,7 @@ tx_pru0_1: txpru@c000 {
icssg0_mdio: mdio@32400 {
compatible = "ti,davinci_mdio";
reg = <0x32400 0x100>;
- clocks = <&k3_clks 62 3>;
+ clocks = <&k3_clks 81 0>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/kernel/compat_alignment.c b/arch/arm64/kernel/compat_alignment.c
index b68e1d328d4c..9b58d0aa38d2 100644
--- a/arch/arm64/kernel/compat_alignment.c
+++ b/arch/arm64/kernel/compat_alignment.c
@@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
static int
do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
{
- unsigned int rd, rn, nr_regs, regbits;
- unsigned long eaddr, newaddr;
+ unsigned int rd, rn, regbits;
+ unsigned long eaddr, newaddr, nr_regs;
unsigned int val;
/* count the number of registers in the mask to be transferred */
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index 17c436387f1b..6a357dfd19ce 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -342,8 +342,10 @@ int swsusp_arch_suspend(void)
crash_prepare_suspend();
ret = swsusp_mte_save_tags();
- if (ret)
+ if (ret) {
+ local_daif_restore(flags);
return ret;
+ }
sleep_cpu = smp_processor_id();
ret = swsusp_save();
@@ -459,9 +461,21 @@ int __nocfi swsusp_arch_resume(void)
if (el2_reset_needed())
__hyp_set_vectors(el2_vectors);
+ /*
+ * It is necessary to mask all DAIF exceptions here as:
+ *
+ * - The copy of swsusp_arch_suspend_exit() in the hibernation
+ * text cannot handle taking any exceptions.
+ *
+ * - The suspended kernel masked all DAIF exceptions in
+ * swsusp_arch_resume(), and expects to be re-entered in the
+ * same state : with all DAIF exceptions masked.
+ */
+ local_daif_save();
hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1,
resume_hdr.reenter_kernel, restore_pblist,
resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page));
+ unreachable();
return 0;
}
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
index 29490be2546b..d1c2254e4fd1 100644
--- a/arch/arm64/lib/Makefile
+++ b/arch/arm64/lib/Makefile
@@ -1,4 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+
+# KCSAN uses udelay for introducing watchpoint delay; avoid recursion.
+KCSAN_SANITIZE_delay.o := n
+
lib-y := clear_user.o delay.o copy_from_user.o \
copy_to_user.o copy_page.o \
clear_page.o csum.o insn.o memchr.o memcpy.o \
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 586bcf2a3db1..99529886b270 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -15,6 +15,7 @@
#include <linux/mm.h>
#include <linux/hardirq.h>
#include <linux/init.h>
+#include <linux/irqflags.h>
#include <linux/kasan.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
@@ -156,6 +157,9 @@ static void show_pte(unsigned long addr)
pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n",
mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K,
vabits_actual, mm_to_pgd_phys(mm));
+
+ guard(irqsave)();
+
pgdp = pgd_offset(mm, addr);
pgd = READ_ONCE(*pgdp);
pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd));
@@ -169,25 +173,25 @@ static void show_pte(unsigned long addr)
if (pgd_none(pgd) || pgd_bad(pgd))
break;
- p4dp = p4d_offset(pgdp, addr);
+ p4dp = p4d_offset_lockless(pgdp, pgd, addr);
p4d = READ_ONCE(*p4dp);
pr_cont(", p4d=%016llx", p4d_val(p4d));
if (p4d_none(p4d) || p4d_bad(p4d))
break;
- pudp = pud_offset(p4dp, addr);
+ pudp = pud_offset_lockless(p4dp, p4d, addr);
pud = READ_ONCE(*pudp);
pr_cont(", pud=%016llx", pud_val(pud));
if (pud_none(pud) || pud_bad(pud))
break;
- pmdp = pmd_offset(pudp, addr);
+ pmdp = pmd_offset_lockless(pudp, pud, addr);
pmd = READ_ONCE(*pmdp);
pr_cont(", pmd=%016llx", pmd_val(pmd));
if (pmd_none(pmd) || pmd_bad(pmd))
break;
- ptep = pte_offset_map(pmdp, addr);
+ ptep = pte_offset_map(&pmd, addr);
if (!ptep)
break;
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index e075792d7225..c49cf9916188 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -106,12 +106,28 @@ EXPORT_SYMBOL(phys_mem_access_prot);
static phys_addr_t __init early_pgtable_alloc(int shift)
{
phys_addr_t phys;
+ void *ptr;
phys = memblock_phys_alloc_range(PAGE_SIZE, PAGE_SIZE, 0,
MEMBLOCK_ALLOC_NOLEAKTRACE);
if (!phys)
panic("Failed to allocate page table page\n");
+ /*
+ * The FIX_{PGD,PUD,PMD} slots may be in active use, but the FIX_PTE
+ * slot will be free, so we can (ab)use the FIX_PTE slot to initialise
+ * any level of table.
+ */
+ ptr = pte_set_fixmap(phys);
+
+ memset(ptr, 0, PAGE_SIZE);
+
+ /*
+ * Implicit barriers also ensure the zeroed page is visible to the page
+ * table walker
+ */
+ pte_clear_fixmap();
+
return phys;
}
@@ -153,14 +169,6 @@ bool pgattr_change_is_safe(u64 old, u64 new)
return ((old ^ new) & ~mask) == 0;
}
-static void init_clear_pgtable(void *table)
-{
- clear_page(table);
-
- /* Ensure the zeroing is observed by page table walks. */
- dsb(ishst);
-}
-
static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot)
{
@@ -203,15 +211,12 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
pmdval |= PMD_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pte_phys = pgtable_alloc(PAGE_SHIFT);
- ptep = pte_set_fixmap(pte_phys);
- init_clear_pgtable(ptep);
- ptep += pte_index(addr);
__pmd_populate(pmdp, pte_phys, pmdval);
- } else {
- BUG_ON(pmd_bad(pmd));
- ptep = pte_set_fixmap_offset(pmdp, addr);
+ pmd = READ_ONCE(*pmdp);
}
+ BUG_ON(pmd_bad(pmd));
+ ptep = pte_set_fixmap_offset(pmdp, addr);
do {
pgprot_t __prot = prot;
@@ -290,15 +295,12 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
pudval |= PUD_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pmd_phys = pgtable_alloc(PMD_SHIFT);
- pmdp = pmd_set_fixmap(pmd_phys);
- init_clear_pgtable(pmdp);
- pmdp += pmd_index(addr);
__pud_populate(pudp, pmd_phys, pudval);
- } else {
- BUG_ON(pud_bad(pud));
- pmdp = pmd_set_fixmap_offset(pudp, addr);
+ pud = READ_ONCE(*pudp);
}
+ BUG_ON(pud_bad(pud));
+ pmdp = pmd_set_fixmap_offset(pudp, addr);
do {
pgprot_t __prot = prot;
@@ -336,15 +338,12 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
p4dval |= P4D_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pud_phys = pgtable_alloc(PUD_SHIFT);
- pudp = pud_set_fixmap(pud_phys);
- init_clear_pgtable(pudp);
- pudp += pud_index(addr);
__p4d_populate(p4dp, pud_phys, p4dval);
- } else {
- BUG_ON(p4d_bad(p4d));
- pudp = pud_set_fixmap_offset(p4dp, addr);
+ p4d = READ_ONCE(*p4dp);
}
+ BUG_ON(p4d_bad(p4d));
+ pudp = pud_set_fixmap_offset(p4dp, addr);
do {
pud_t old_pud = READ_ONCE(*pudp);
@@ -426,10 +425,11 @@ void create_kpti_ng_temp_pgd(pgd_t *pgdir, phys_addr_t phys, unsigned long virt,
static phys_addr_t __pgd_pgtable_alloc(int shift)
{
- /* Page is zeroed by init_clear_pgtable() so don't duplicate effort. */
- void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL & ~__GFP_ZERO);
-
+ void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL);
BUG_ON(!ptr);
+
+ /* Ensure the zeroed page is visible to the page table walker */
+ dsb(ishst);
return __pa(ptr);
}
diff --git a/arch/csky/kernel/entry.S b/arch/csky/kernel/entry.S
index c68cdcc76d60..3261f46f2244 100644
--- a/arch/csky/kernel/entry.S
+++ b/arch/csky/kernel/entry.S
@@ -93,11 +93,11 @@ csky_syscall_trace:
ldw a2, (sp, LSAVE_A2)
ldw a3, (sp, LSAVE_A3)
#if defined(__CSKYABIV2__)
- subi sp, 8
ldw r9, (sp, LSAVE_A4)
+ ldw r10, (sp, LSAVE_A5)
+ subi sp, 8
stw r9, (sp, 0x0)
- ldw r9, (sp, LSAVE_A5)
- stw r9, (sp, 0x4)
+ stw r10, (sp, 0x4)
jsr syscallid /* Do system call */
addi sp, 8
#else
diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
index ea98b6a3e201..3e87bd03f3f7 100644
--- a/arch/loongarch/kernel/kprobes.c
+++ b/arch/loongarch/kernel/kprobes.c
@@ -273,6 +273,9 @@ bool kprobe_singlestep_handler(struct pt_regs *regs)
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
unsigned long addr = instruction_pointer(regs);
+ if (!cur)
+ return false;
+
if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
((unsigned long)&cur->ainsn.insn[1] == addr)) {
restore_local_irqflag(kcb, regs);
diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S
index bd5772c96338..4629b0ab0085 100644
--- a/arch/loongarch/kernel/rethook_trampoline.S
+++ b/arch/loongarch/kernel/rethook_trampoline.S
@@ -24,7 +24,6 @@
cfi_st t6, PT_R18
cfi_st t7, PT_R19
cfi_st t8, PT_R20
- cfi_st u0, PT_R21
cfi_st fp, PT_R22
cfi_st s0, PT_R23
cfi_st s1, PT_R24
@@ -59,7 +58,6 @@
cfi_ld t6, PT_R18
cfi_ld t7, PT_R19
cfi_ld t8, PT_R20
- cfi_ld u0, PT_R21
cfi_ld fp, PT_R22
cfi_ld s0, PT_R23
cfi_ld s1, PT_R24
diff --git a/arch/m68k/emu/nfcon.c b/arch/m68k/emu/nfcon.c
index 3a74d493eb3e..977209a28db6 100644
--- a/arch/m68k/emu/nfcon.c
+++ b/arch/m68k/emu/nfcon.c
@@ -49,7 +49,7 @@ static void nfcon_write(struct console *con, const char *str,
static struct tty_driver *nfcon_device(struct console *con, int *index)
{
*index = 0;
- return console_is_registered(con) ? nfcon_tty_driver : NULL;
+ return nfcon_tty_driver;
}
static struct console nf_console = {
diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c
index a700807c9b6d..aaea713210ed 100644
--- a/arch/m68k/kernel/traps.c
+++ b/arch/m68k/kernel/traps.c
@@ -937,8 +937,8 @@ void show_stack(struct task_struct *task, unsigned long *stack,
int i;
if (!stack) {
- if (task)
- stack = (unsigned long *)task->thread.esp0;
+ if (task && task != current)
+ stack = (unsigned long *)task->thread.ksp;
else
stack = (unsigned long *)&stack;
}
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 61503a36067e..2016dd5e8fae 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -1319,8 +1319,12 @@ long arch_ptrace(struct task_struct *child, long request,
*/
asmlinkage long syscall_trace_enter(struct pt_regs *regs)
{
+ long syscall;
+
user_exit();
+ syscall = current_thread_info()->syscall;
+
if (test_thread_flag(TIF_SYSCALL_TRACE)) {
if (ptrace_report_syscall_entry(regs))
return -1;
@@ -1356,7 +1360,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs)
* Negative syscall numbers are mistaken for rejected syscalls, but
* won't have had the return value set appropriately, so we do so now.
*/
- if (current_thread_info()->syscall < 0)
+ if (syscall < 0)
syscall_set_return_value(current, regs, -ENOSYS, 0);
return current_thread_info()->syscall;
}
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index c9f48e750b72..26b000fd0281 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -1230,15 +1230,50 @@ _no_syscall_trace:
*
*/
+/* Keep this literal; hi()/lo() can't use the UL-suffixed TASK_SIZE. */
+#define OR1K_ATOMIC_ADDR_LIMIT 0x7ffffffc
+
ENTRY(sys_or1k_atomic)
/* FIXME: This ignores r3 and always does an XCHG */
+
+ /* Check both user pointers before accessing them. */
+ l.movhi r13,hi(OR1K_ATOMIC_ADDR_LIMIT)
+ l.ori r13,r13,lo(OR1K_ATOMIC_ADDR_LIMIT)
+ l.sfgtu r4,r13
+ l.bf 9f
+ l.nop
+ l.sfgtu r5,r13
+ l.bf 9f
+ l.nop
+
DISABLE_INTERRUPTS(r17,r19)
- l.lwz r29,0(r4)
- l.lwz r27,0(r5)
- l.sw 0(r4),r27
- l.sw 0(r5),r29
+10: l.lwz r29,0(r4)
+11: l.lwz r27,0(r5)
+12: l.sw 0(r4),r27
+13: l.sw 0(r5),r29
ENABLE_INTERRUPTS(r17)
l.jr r9
l.or r11,r0,r0
+ /*
+ * Either pointer was outside user space, or turned out to be
+ * unmapped/inaccessible when we actually touched it.
+ */
+9: l.jr r9
+ l.addi r11,r0,-EFAULT
+
+ .section .fixup, "ax"
+14:
+ ENABLE_INTERRUPTS(r17)
+ l.j 9b
+ l.nop
+ .previous
+
+ .section __ex_table, "a"
+ .long 10b, 14b
+ .long 11b, 14b
+ .long 12b, 14b
+ .long 13b, 14b
+ .previous
+
/* ============================================================[ EOF ]=== */
diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S
index 9188c8d87437..e84d7d6e1eb9 100644
--- a/arch/parisc/kernel/head.S
+++ b/arch/parisc/kernel/head.S
@@ -105,6 +105,7 @@ $iodc_panic:
or %r10,%r10,%r10 /* qemu idle sleep */
msg1: .ascii "Can't boot kernel which was built for PA8x00 CPUs on this machine.\r\n"
msg1_end:
+ .align 4
$cpu_ok:
#endif
diff --git a/arch/powerpc/configs/85xx-hw.config b/arch/powerpc/configs/85xx-hw.config
index 8aff83217397..b655c36e56da 100644
--- a/arch/powerpc/configs/85xx-hw.config
+++ b/arch/powerpc/configs/85xx-hw.config
@@ -90,6 +90,7 @@ CONFIG_PPC_EPAPR_HV_BYTECHAN=y
CONFIG_QE_GPIO=y
CONFIG_QUICC_ENGINE=y
CONFIG_RAPIDIO=y
+CONFIG_RAS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_CMOS=y
CONFIG_RTC_DRV_DS1307=y
diff --git a/arch/powerpc/include/asm/papr-watchdog.h b/arch/powerpc/include/asm/papr-watchdog.h
new file mode 100644
index 000000000000..bf876fc2caae
--- /dev/null
+++ b/arch/powerpc/include/asm/papr-watchdog.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ASM_POWERPC_PAPR_WATCHDOG_H
+#define _ASM_POWERPC_PAPR_WATCHDOG_H
+
+/*
+ * H_WATCHDOG Input
+ *
+ * R4: "flags":
+ *
+ * Bits 48-55: "operation"
+ */
+#define PSERIES_WDTF_OP_START 0x100UL /* start timer */
+#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */
+#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */
+
+/*
+ * Bits 56-63: "timeoutAction" (for "Start Watchdog" only)
+ */
+#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */
+#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */
+#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */
+
+/*
+ * R5: "watchdogNumber":
+ * PAPR says use -1 (all ones) to stop all watchdogs.
+ */
+#define PSERIES_WDT_NUM_ALL ((unsigned long)-1)
+
+/*
+ * H_WATCHDOG Output
+ *
+ * R3: Return code
+ *
+ * H_SUCCESS The operation completed.
+ *
+ * H_BUSY The hypervisor is too busy; retry the operation.
+ *
+ * H_PARAMETER The given "flags" are somehow invalid. Either the
+ * "operation" or "timeoutAction" is invalid, or a
+ * reserved bit is set.
+ *
+ * H_P2 The given "watchdogNumber" is zero or exceeds the
+ * supported maximum value.
+ *
+ * H_P3 The given "timeoutInMs" is below the supported
+ * minimum value.
+ *
+ * H_NOOP The given "watchdogNumber" is already stopped.
+ *
+ * H_HARDWARE The operation failed for ineffable reasons.
+ *
+ * H_FUNCTION The H_WATCHDOG hypercall is not supported by this
+ * hypervisor.
+ *
+ * R4:
+ *
+ * - For the "Query Watchdog Capabilities" operation, a 64-bit
+ * structure:
+ */
+#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff)
+#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff)
+
+#endif /* _ASM_POWERPC_PAPR_WATCHDOG_H */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 6f7d4edaa0bc..64bbe355ea93 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -217,8 +217,12 @@ static __always_inline void call_do_softirq(const void *sp)
[sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE),
[callee] "i" (__do_softirq)
: // Clobbers
- "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6",
- "cr7", "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+ "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0",
+ /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */
+#ifdef CONFIG_PPC_KERNEL_PCREL
+ "r2",
+#endif
+ "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
"r11", "r12"
);
}
@@ -275,8 +279,12 @@ static __always_inline void call_do_irq(struct pt_regs *regs, void *sp)
[sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE),
[callee] "i" (__do_irq)
: // Clobbers
- "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6",
- "cr7", "r0", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+ "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0",
+ /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */
+#ifdef CONFIG_PPC_KERNEL_PCREL
+ "r2",
+#endif
+ "r4", "r5", "r6", "r7", "r8", "r9", "r10",
"r11", "r12"
);
}
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index ab99a3e7f997..0bcc6056b9e5 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -297,6 +297,9 @@ void smp_muxed_ipi_set_message(int cpu, int msg)
void smp_muxed_ipi_message_pass(int cpu, int msg)
{
+ if (!smp_ops->cause_ipi)
+ return;
+
smp_muxed_ipi_set_message(cpu, msg);
/*
diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c
index 7581e0ef3f6c..ff643d8c29b3 100644
--- a/arch/powerpc/mm/book3s64/radix_pgtable.c
+++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
@@ -1318,7 +1318,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
* covering out both edges.
*/
unsigned long addr;
- unsigned long addr_pfn = start_pfn;
unsigned long next;
pgd_t *pgd;
p4d_t *p4d;
@@ -1339,7 +1338,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
if (pmd_leaf(READ_ONCE(*pmd))) {
/* existing huge mapping. Skip the range */
- addr_pfn += (PMD_SIZE >> PAGE_SHIFT);
next = pmd_addr_end(addr, end);
continue;
}
@@ -1352,11 +1350,11 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
* page whose VMEMMAP_RESERVE_NR pages were mapped and
* this request fall in those pages.
*/
- addr_pfn += 1;
next = addr + PAGE_SIZE;
continue;
} else {
unsigned long nr_pages = pgmap_vmemmap_nr(pgmap);
+ unsigned long addr_pfn = page_to_pfn((struct page *)addr);
unsigned long pfn_offset = addr_pfn - ALIGN_DOWN(addr_pfn, nr_pages);
pte_t *tail_page_pte;
@@ -1380,7 +1378,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
if (!pte)
return -ENOMEM;
- addr_pfn += 2;
next = addr + 2 * PAGE_SIZE;
continue;
}
@@ -1396,7 +1393,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
return -ENOMEM;
vmemmap_verify(pte, node, addr, addr + PAGE_SIZE);
- addr_pfn += 1;
next = addr + PAGE_SIZE;
continue;
}
@@ -1406,7 +1402,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
return -ENOMEM;
vmemmap_verify(pte, node, addr, addr + PAGE_SIZE);
- addr_pfn += 1;
next = addr + PAGE_SIZE;
continue;
}
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index 40f3aa432fba..814e2b5fe559 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1502,7 +1502,7 @@ static int __init pmac_i2c_create_platform_devices(void)
if (bus->platform_dev == NULL)
return -ENOMEM;
bus->platform_dev->dev.platform_data = bus;
- bus->platform_dev->dev.of_node = bus->busnode;
+ bus->platform_dev->dev.of_node = of_node_get(bus->busnode);
platform_device_add(bus->platform_dev);
}
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index bf02f94a973d..54fd2a0d6604 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -743,18 +743,11 @@ static struct device_node *pci_dma_find(struct device_node *dn,
/* parse DMA window property. During normal system boot, only default
* DMA window is passed in OF. But, for kdump, a dedicated adapter might
- * have both default and DDW in FDT. In this scenario, DDW takes precedence
- * over default window.
+ * have both default and DDW in FDT. In this scenario, default window
+ * takes precedence over DDW. For a dedicated adapter, default window will
+ * potentially have more unused TCEs.
*/
- if (ddw_win) {
- struct dynamic_dma_window_prop *p;
-
- p = (struct dynamic_dma_window_prop *)ddw_prop;
- prop->liobn = p->liobn;
- prop->dma_base = p->dma_base;
- prop->tce_shift = p->tce_shift;
- prop->window_shift = p->window_shift;
- } else if (default_win) {
+ if (default_win) {
unsigned long offset, size, liobn;
of_parse_dma_window(rdn, default_prop, &liobn, &offset, &size);
@@ -763,6 +756,14 @@ static struct device_node *pci_dma_find(struct device_node *dn,
prop->dma_base = cpu_to_be64(offset);
prop->tce_shift = cpu_to_be32(IOMMU_PAGE_SHIFT_4K);
prop->window_shift = cpu_to_be32(order_base_2(size));
+ } else {
+ struct dynamic_dma_window_prop *p;
+
+ p = (struct dynamic_dma_window_prop *)ddw_prop;
+ prop->liobn = p->liobn;
+ prop->dma_base = p->dma_base;
+ prop->tce_shift = p->tce_shift;
+ prop->window_shift = p->window_shift;
}
return rdn;
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 1feb6b919bd9..f9204c96a6bc 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -76,6 +76,7 @@
#include <asm/dtl.h>
#include <asm/hvconsole.h>
#include <asm/setup.h>
+#include <asm/papr-watchdog.h>
#include "pseries.h"
@@ -187,14 +188,37 @@ static void __init fwnmi_init(void)
#endif
}
+static void pseries_crash_stop_watchdogs(void)
+{
+ long rc;
+
+ rc = plpar_hcall_norets_notrace(H_WATCHDOG, PSERIES_WDTF_OP_STOP,
+ PSERIES_WDT_NUM_ALL);
+ if (rc != H_SUCCESS && rc != H_NOOP)
+ pr_warn("Could not stop watchdogs before kdump rc=%ld\n", rc);
+}
+
/*
* Affix a device for the first timer to the platform bus if
* we have firmware support for the H_WATCHDOG hypercall.
*/
static __init int pseries_wdt_init(void)
{
- if (firmware_has_feature(FW_FEATURE_WATCHDOG))
- platform_device_register_simple("pseries-wdt", 0, NULL, 0);
+ struct platform_device *pdev;
+
+ if (!firmware_has_feature(FW_FEATURE_WATCHDOG))
+ return 0;
+
+ pdev = platform_device_register_simple("pseries-wdt", 0, NULL, 0);
+
+ if (IS_ERR(pdev)) {
+ pr_err("Failed to register pseries-wdt platform device\n");
+ return PTR_ERR(pdev);
+ }
+
+ if (crash_shutdown_register(pseries_crash_stop_watchdogs))
+ pr_warn("Could not register watchdog crash shutdown handler\n");
+
return 0;
}
machine_subsys_initcall(pseries, pseries_wdt_init);
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 3f0ad09e1650..5615b358df02 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -114,7 +114,8 @@ static bool riscv_isa_extension_check(int id)
/*
* The canonical order of ISA extension names in the ISA string is defined in
- * chapter 27 of the unprivileged specification.
+ * Chapter 27 of the RISC-V Instruction Set Manual Volume I Unprivileged ISA
+ * (Document Version 20191213).
*
* Ordinarily, for in-kernel data structures, this order is unimportant but
* isa_ext_arr defines the order of the ISA string in /proc/cpuinfo.
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index 893c35aeb746..dc940dad337f 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -747,8 +747,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
mutex_unlock(&debug_mutex);
}
-/* Remove debugfs entries and remove from internal list. */
-static void _debug_unregister(debug_info_t *id)
+/* Remove debugfs entries. */
+static void _debug_unregister_debugfs(debug_info_t *id)
{
int i;
@@ -758,6 +758,11 @@ static void _debug_unregister(debug_info_t *id)
debugfs_remove(id->debugfs_entries[i]);
}
debugfs_remove(id->debugfs_root_entry);
+}
+
+/* Remove from internal list. */
+static void _debug_unregister(debug_info_t *id)
+{
if (id == debug_area_first)
debug_area_first = id->next;
if (id == debug_area_last)
@@ -783,6 +788,7 @@ void debug_unregister(debug_info_t *id)
mutex_lock(&debug_mutex);
_debug_unregister(id);
mutex_unlock(&debug_mutex);
+ _debug_unregister_debugfs(id);
debug_info_put(id);
}
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index fbf4eb85cb0a..b62bf81fde34 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -108,6 +108,7 @@ struct cpu_cf_ptr {
static struct cpu_cf_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */
+ unsigned int tskctx; /* Users tracking all CPUs (cpu == -1) */
struct cpu_cf_ptr __percpu *cfptr;
} cpu_cf_root;
@@ -116,13 +117,15 @@ static struct cpu_cf_root { /* Anchor to per CPU data */
* user space in task context with perf_event_open() and close()
* system calls.
*
- * This mutex serializes functions cpum_cf_alloc_cpu() called at event
- * initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu()
- * called at event removal via call back function hw_perf_event_destroy()
- * when the event is deleted. They are serialized to enforce correct
- * bookkeeping of pointer and reference counts anchored by
- * struct cpu_cf_root and the access to cpu_cf_root::refcnt and the
- * per CPU pointers stored in cpu_cf_root::cfptr.
+ * This mutex serializes the allocation and removal of the per CPU counter
+ * data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with
+ * this mutex held at event initialization via cpumf_pmu_event_init(), at
+ * event removal via call back function hw_perf_event_destroy() when the
+ * event is deleted, and from the CPU hotplug prepare/dead callbacks. The
+ * mutex enforces correct bookkeeping of pointer and reference counts
+ * anchored by struct cpu_cf_root and protects the access to
+ * cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers
+ * stored in cpu_cf_root::cfptr.
*/
static DEFINE_MUTEX(pmc_reserve_mutex);
@@ -165,12 +168,14 @@ static void cpum_cf_reset_cpu(void *flags)
}
/* Free per CPU data when the last event is removed. */
-static void cpum_cf_free_root(void)
+static void cpum_cf_free_root(unsigned int num)
{
- if (!refcount_dec_and_test(&cpu_cf_root.refcnt))
+ struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr;
+
+ if (!refcount_sub_and_test(num, &cpu_cf_root.refcnt))
return;
- free_percpu(cpu_cf_root.cfptr);
cpu_cf_root.cfptr = NULL;
+ free_percpu(p);
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n",
@@ -184,17 +189,17 @@ static void cpum_cf_free_root(void)
* CPUs possible, which might be larger than the number of CPUs currently
* online.
*/
-static int cpum_cf_alloc_root(void)
+static int cpum_cf_alloc_root(unsigned int num)
{
int rc = 0;
- if (refcount_inc_not_zero(&cpu_cf_root.refcnt))
+ if (refcount_add_not_zero(num, &cpu_cf_root.refcnt))
return rc;
/* The memory is already zeroed. */
cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr);
if (cpu_cf_root.cfptr) {
- refcount_set(&cpu_cf_root.refcnt, 1);
+ refcount_set(&cpu_cf_root.refcnt, num);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
} else {
@@ -204,20 +209,23 @@ static int cpum_cf_alloc_root(void)
return rc;
}
-/* Free CPU counter data structure for a PMU */
-static void cpum_cf_free_cpu(int cpu)
+/*
+ * Remove num references to the CPU counter data structure of a PMU.
+ * Called with pmc_reserve_mutex held.
+ */
+static void cpum_cf_free_cpu(int cpu, unsigned int num)
{
struct cpu_cf_events *cpuhw;
struct cpu_cf_ptr *p;
- mutex_lock(&pmc_reserve_mutex);
+ lockdep_assert_held(&pmc_reserve_mutex);
/*
* When invoked via CPU hotplug handler, there might be no events
* installed or that particular CPU might not have an
* event installed. This anchor pointer can be NULL!
*/
if (!cpu_cf_root.cfptr)
- goto out;
+ return;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf;
/*
@@ -225,28 +233,29 @@ static void cpum_cf_free_cpu(int cpu)
* installed on that CPU, but on different CPUs.
*/
if (!cpuhw)
- goto out;
+ return;
- if (refcount_dec_and_test(&cpuhw->refcnt)) {
- kfree(cpuhw);
+ if (refcount_sub_and_test(num, &cpuhw->refcnt)) {
p->cpucf = NULL;
+ kfree(cpuhw);
}
- cpum_cf_free_root();
-out:
- mutex_unlock(&pmc_reserve_mutex);
+ cpum_cf_free_root(num);
}
-/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */
-static int cpum_cf_alloc_cpu(int cpu)
+/*
+ * Add num references to the CPU counter data structure of a PMU and
+ * allocate it when necessary. Called with pmc_reserve_mutex held.
+ */
+static int cpum_cf_alloc_cpu(int cpu, unsigned int num)
{
struct cpu_cf_events *cpuhw;
struct cpu_cf_ptr *p;
int rc;
- mutex_lock(&pmc_reserve_mutex);
- rc = cpum_cf_alloc_root();
+ lockdep_assert_held(&pmc_reserve_mutex);
+ rc = cpum_cf_alloc_root(num);
if (rc)
- goto unlock;
+ return rc;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf;
@@ -254,12 +263,12 @@ static int cpum_cf_alloc_cpu(int cpu)
cpuhw = kzalloc(sizeof(*cpuhw), GFP_KERNEL);
if (cpuhw) {
p->cpucf = cpuhw;
- refcount_set(&cpuhw->refcnt, 1);
+ refcount_set(&cpuhw->refcnt, num);
} else {
rc = -ENOMEM;
}
} else {
- refcount_inc(&cpuhw->refcnt);
+ refcount_add(num, &cpuhw->refcnt);
}
if (rc) {
/*
@@ -267,10 +276,8 @@ static int cpum_cf_alloc_cpu(int cpu)
* cpu_cf_event in not created, its destroy() function is not
* invoked. Adjust the reference counter for the anchor.
*/
- cpum_cf_free_root();
+ cpum_cf_free_root(num);
}
-unlock:
- mutex_unlock(&pmc_reserve_mutex);
return rc;
}
@@ -282,39 +289,70 @@ static int cpum_cf_alloc_cpu(int cpu)
* perf_event_open() with task context and /dev/hwctr interface.
* If cpu is non-zero install event on this CPU only. This setup handles
* perf_event_open() with CPU context.
+ * Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug
+ * prepare and dead callbacks use this count to install and remove the per
+ * CPU counter data on a new or dying CPU.
*/
-static int cpum_cf_alloc(int cpu)
+static int cpum_cf_alloc_cpuslocked(int cpu)
{
cpumask_var_t mask;
int rc;
+ lockdep_assert_cpus_held();
if (cpu == -1) {
if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
return -ENOMEM;
+ mutex_lock(&pmc_reserve_mutex);
for_each_online_cpu(cpu) {
- rc = cpum_cf_alloc_cpu(cpu);
+ rc = cpum_cf_alloc_cpu(cpu, 1);
if (rc) {
for_each_cpu(cpu, mask)
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
break;
}
cpumask_set_cpu(cpu, mask);
}
+ if (!rc)
+ cpu_cf_root.tskctx++;
+ mutex_unlock(&pmc_reserve_mutex);
free_cpumask_var(mask);
} else {
- rc = cpum_cf_alloc_cpu(cpu);
+ mutex_lock(&pmc_reserve_mutex);
+ rc = cpum_cf_alloc_cpu(cpu, 1);
+ mutex_unlock(&pmc_reserve_mutex);
}
return rc;
}
-static void cpum_cf_free(int cpu)
+static int cpum_cf_alloc(int cpu)
+{
+ int rc;
+
+ cpus_read_lock();
+ rc = cpum_cf_alloc_cpuslocked(cpu);
+ cpus_read_unlock();
+ return rc;
+}
+
+static void cpum_cf_free_cpuslocked(int cpu)
{
+ lockdep_assert_cpus_held();
+ mutex_lock(&pmc_reserve_mutex);
if (cpu == -1) {
+ cpu_cf_root.tskctx--;
for_each_online_cpu(cpu)
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
} else {
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
}
+ mutex_unlock(&pmc_reserve_mutex);
+}
+
+static void cpum_cf_free(int cpu)
+{
+ cpus_read_lock();
+ cpum_cf_free_cpuslocked(cpu);
+ cpus_read_unlock();
}
#define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */
@@ -1093,53 +1131,67 @@ static refcount_t cfset_opencnt = REFCOUNT_INIT(0); /* Access count */
static DEFINE_MUTEX(cfset_ctrset_mutex);
/*
- * CPU hotplug handles only /dev/hwctr device.
- * For perf_event_open() the CPU hotplug handling is done on kernel common
- * code:
+ * CPU hotplug handling:
+ *
+ * cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying
+ * CPU is offline. They create and remove the per CPU counter data for all
+ * users tracking every CPU (cpu == -1), that is perf_event_open() events
+ * with task context and /dev/hwctr device sessions. Each such user holds
+ * one reference to the per CPU counter data of each CPU. Therefore install
+ * and remove one reference per user, tracked in cpu_cf_root::tskctx. This
+ * guarantees the per CPU counter data exists before the new CPU executes
+ * its first task and is removed only after the dying CPU is gone.
+ *
+ * cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or
+ * dying CPU is online. They handle only the counter set state of open
+ * /dev/hwctr device sessions on that CPU. For perf_event_open() events
+ * nothing is done:
* - CPU add: Nothing is done since a file descriptor can not be created
* and returned to the user.
* - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and
- * pmu_delete(). The event itself is removed when the file descriptor is
- * closed.
+ * pmu_delete(). During task exit processing of grouped perf events
+ * triggered by CPU hotplug processing, pmu_disable() is called as part
+ * of perf context removal process. The event itself is removed when the
+ * event file descriptor is closed.
*/
+static int cpum_cf_prepare_cpu(unsigned int cpu)
+{
+ int rc = 0;
+
+ mutex_lock(&pmc_reserve_mutex);
+ if (cpu_cf_root.tskctx)
+ rc = cpum_cf_alloc_cpu(cpu, cpu_cf_root.tskctx);
+ mutex_unlock(&pmc_reserve_mutex);
+ return rc;
+}
+
+static int cpum_cf_dead_cpu(unsigned int cpu)
+{
+ mutex_lock(&pmc_reserve_mutex);
+ if (cpu_cf_root.tskctx)
+ cpum_cf_free_cpu(cpu, cpu_cf_root.tskctx);
+ mutex_unlock(&pmc_reserve_mutex);
+ return 0;
+}
+
static int cfset_online_cpu(unsigned int cpu);
static int cpum_cf_online_cpu(unsigned int cpu)
{
- int rc = 0;
-
- /*
- * Ignore notification for perf_event_open().
- * Handle only /dev/hwctr device sessions.
- */
mutex_lock(&cfset_ctrset_mutex);
- if (refcount_read(&cfset_opencnt)) {
- rc = cpum_cf_alloc_cpu(cpu);
- if (!rc)
- cfset_online_cpu(cpu);
- }
+ if (refcount_read(&cfset_opencnt))
+ cfset_online_cpu(cpu);
mutex_unlock(&cfset_ctrset_mutex);
- return rc;
+ return 0;
}
static int cfset_offline_cpu(unsigned int cpu);
static int cpum_cf_offline_cpu(unsigned int cpu)
{
- /*
- * During task exit processing of grouped perf events triggered by CPU
- * hotplug processing, pmu_disable() is called as part of perf context
- * removal process. Therefore do not trigger event removal now for
- * perf_event_open() created events. Perf common code triggers event
- * destruction when the event file descriptor is closed.
- *
- * Handle only /dev/hwctr device sessions.
- */
mutex_lock(&cfset_ctrset_mutex);
- if (refcount_read(&cfset_opencnt)) {
+ if (refcount_read(&cfset_opencnt))
cfset_offline_cpu(cpu);
- cpum_cf_free_cpu(cpu);
- }
mutex_unlock(&cfset_ctrset_mutex);
return 0;
}
@@ -1186,7 +1238,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
static int cfset_init(void);
static int __init cpumf_pmu_init(void)
{
- int rc;
+ int state, rc;
/* Extract counter measurement facility information */
if (!cpum_cf_avail() || qctri(&cpumf_ctr_info))
@@ -1228,11 +1280,24 @@ static int __init cpumf_pmu_init(void)
cfset_init();
}
+ rc = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN,
+ "perf/s390/cf:prepare",
+ cpum_cf_prepare_cpu, cpum_cf_dead_cpu);
+ if (rc < 0)
+ goto out3;
+ state = rc;
+
rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE,
"perf/s390/cf:online",
cpum_cf_online_cpu, cpum_cf_offline_cpu);
- return rc;
+ if (rc < 0)
+ goto out4;
+ return 0;
+out4:
+ cpuhp_remove_state(state);
+out3:
+ perf_pmu_unregister(&cpumf_pmu);
out2:
debug_unregister_view(cf_dbg, &debug_sprintf_view);
debug_unregister(cf_dbg);
@@ -1388,6 +1453,7 @@ static void cfset_all_stop(struct cfset_request *req)
*/
static int cfset_release(struct inode *inode, struct file *file)
{
+ cpus_read_lock();
mutex_lock(&cfset_ctrset_mutex);
/* Open followed by close/exit has no private_data */
if (file->private_data) {
@@ -1398,9 +1464,10 @@ static int cfset_release(struct inode *inode, struct file *file)
}
if (refcount_dec_and_test(&cfset_opencnt)) { /* Last close */
on_each_cpu(cfset_release_cpu, NULL, 1);
- cpum_cf_free(-1);
+ cpum_cf_free_cpuslocked(-1);
}
mutex_unlock(&cfset_ctrset_mutex);
+ cpus_read_unlock();
return 0;
}
@@ -1419,15 +1486,17 @@ static int cfset_open(struct inode *inode, struct file *file)
return -EPERM;
file->private_data = NULL;
+ cpus_read_lock();
mutex_lock(&cfset_ctrset_mutex);
if (!refcount_inc_not_zero(&cfset_opencnt)) { /* First open */
- rc = cpum_cf_alloc(-1);
+ rc = cpum_cf_alloc_cpuslocked(-1);
if (!rc) {
cfset_session_init();
refcount_set(&cfset_opencnt, 1);
}
}
mutex_unlock(&cfset_ctrset_mutex);
+ cpus_read_unlock();
/* nonseekable_open() never fails */
return rc ?: nonseekable_open(inode, file);
diff --git a/arch/s390/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c
index 3765c4223bf9..96f6707e80e6 100644
--- a/arch/s390/kvm/guestdbg.c
+++ b/arch/s390/kvm/guestdbg.c
@@ -184,7 +184,7 @@ static int __import_wp_info(struct kvm_vcpu *vcpu,
if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
return -EINVAL;
- wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT);
+ wp_info->old_data = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT);
if (!wp_info->old_data)
return -ENOMEM;
/* try to backup the original value */
@@ -256,7 +256,7 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
ret = __import_wp_info(vcpu, &bp_data[i],
&wp_info[nr_wp]);
if (ret)
- goto error;
+ goto error_wp;
nr_wp++;
break;
case KVM_HW_BP:
@@ -271,7 +271,12 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
vcpu->arch.guestdbg.hw_bp_info = bp_info;
vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
vcpu->arch.guestdbg.hw_wp_info = wp_info;
+ kfree(bp_data);
return 0;
+
+error_wp:
+ while (nr_wp--)
+ kfree(wp_info[nr_wp].old_data);
error:
kfree(bp_data);
kfree(wp_info);
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 3b14140ac865..4b3892584f5b 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -2863,7 +2863,7 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
struct mcck_volatile_info *mcck_info)
{
struct kvm_s390_interrupt_info inti;
- struct kvm_s390_irq irq;
+ struct kvm_s390_irq irq = {};
struct kvm_s390_mchk_info *mchk;
union mci mci;
__u64 cr14 = 0; /* upper bits are not used */
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index bccbb84eb1be..cf075a893422 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3437,6 +3437,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
kvm_s390_clear_local_irqs(vcpu);
kvm_clear_async_pf_completion_queue(vcpu);
+ kvm_s390_clear_bp_data(vcpu);
if (!kvm_is_ucontrol(vcpu->kvm))
sca_del_vcpu(vcpu);
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
@@ -4388,8 +4389,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
/* enforce guest PER */
kvm_s390_set_cpuflags(vcpu, CPUSTAT_P);
- if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
- rc = kvm_s390_import_bp_data(vcpu, dbg);
+ if (dbg->control & KVM_GUESTDBG_USE_HW_BP) {
+ scoped_guard(srcu, &vcpu->kvm->srcu)
+ rc = kvm_s390_import_bp_data(vcpu, dbg);
+ }
} else {
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
vcpu->arch.guestdbg.last_bp = 0;
@@ -5075,7 +5078,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
pr_err_ratelimited("can't run stopped vcpu %d\n",
vcpu->vcpu_id);
rc = -EINVAL;
- goto out;
+ goto out_sigset;
}
sync_regs(vcpu);
@@ -5102,9 +5105,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
disable_cpu_timer_accounting(vcpu);
store_regs(vcpu);
+ vcpu->stat.exit_userspace++;
+
+out_sigset:
kvm_sigset_deactivate(vcpu);
- vcpu->stat.exit_userspace++;
out:
vcpu_put(vcpu);
return rc;
@@ -5725,7 +5730,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd);
/* Always copy over UV rc / rrc data */
- if (copy_to_user((__u8 __user *)argp, &cmd.rc,
+ if (copy_to_user(argp + offsetof(struct kvm_pv_cmd, rc), &cmd.rc,
sizeof(cmd.rc) + sizeof(cmd.rrc)))
r = -EFAULT;
break;
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index 98d0ee9600eb..504923e2f012 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -580,8 +580,8 @@ static bool handle_in(struct pt_regs *regs, int size, int port)
.r13 = PORT_READ,
.r14 = port,
};
- u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
bool success;
+ u64 val;
/*
* Emulate the I/O read via hypercall. More info about ABI can be found
@@ -589,18 +589,16 @@ static bool handle_in(struct pt_regs *regs, int size, int port)
* "TDG.VP.VMCALL<Instruction.IO>".
*/
success = !__tdx_hypercall_ret(&args);
+ val = success ? args.r11 : 0;
- /* Update part of the register affected by the emulated instruction */
- regs->ax &= ~mask;
- if (success)
- regs->ax |= args.r11 & mask;
+ insn_assign_reg(®s->ax, val, size);
return success;
}
static bool handle_out(struct pt_regs *regs, int size, int port)
{
- u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
+ u64 mask = GENMASK(BITS_PER_BYTE * size - 1, 0);
/*
* Emulate the I/O write via hypercall. More info about ABI can be found
diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
index 83f15fe411b3..33ccd4232820 100644
--- a/arch/x86/events/amd/uncore.c
+++ b/arch/x86/events/amd/uncore.c
@@ -27,56 +27,41 @@
#define COUNTER_SHIFT 16
+#define NUM_UNCORES_MAX 2 /* DF (or NB) and L3 (or L2) */
+#define UNCORE_NAME_LEN 16
+
#undef pr_fmt
#define pr_fmt(fmt) "amd_uncore: " fmt
static int pmu_version;
-static int num_counters_llc;
-static int num_counters_nb;
-static bool l3_mask;
static HLIST_HEAD(uncore_unused_list);
-struct amd_uncore {
+struct amd_uncore_ctx {
int id;
int refcnt;
int cpu;
- int num_counters;
- int rdpmc_base;
- u32 msr_base;
- cpumask_t *active_mask;
- struct pmu *pmu;
struct perf_event **events;
struct hlist_node node;
};
-static struct amd_uncore * __percpu *amd_uncore_nb;
-static struct amd_uncore * __percpu *amd_uncore_llc;
-
-static struct pmu amd_nb_pmu;
-static struct pmu amd_llc_pmu;
-
-static cpumask_t amd_nb_active_mask;
-static cpumask_t amd_llc_active_mask;
-
-static bool is_nb_event(struct perf_event *event)
-{
- return event->pmu->type == amd_nb_pmu.type;
-}
+struct amd_uncore_pmu {
+ char name[UNCORE_NAME_LEN];
+ int num_counters;
+ int rdpmc_base;
+ u32 msr_base;
+ cpumask_t active_mask;
+ struct pmu pmu;
+ struct amd_uncore_ctx * __percpu *ctx;
+ int (*id)(unsigned int cpu);
+};
-static bool is_llc_event(struct perf_event *event)
-{
- return event->pmu->type == amd_llc_pmu.type;
-}
+static struct amd_uncore_pmu pmus[NUM_UNCORES_MAX];
+static int num_pmus __read_mostly;
-static struct amd_uncore *event_to_amd_uncore(struct perf_event *event)
+static struct amd_uncore_pmu *event_to_amd_uncore_pmu(struct perf_event *event)
{
- if (is_nb_event(event) && amd_uncore_nb)
- return *per_cpu_ptr(amd_uncore_nb, event->cpu);
- else if (is_llc_event(event) && amd_uncore_llc)
- return *per_cpu_ptr(amd_uncore_llc, event->cpu);
-
- return NULL;
+ return container_of(event->pmu, struct amd_uncore_pmu, pmu);
}
static void amd_uncore_read(struct perf_event *event)
@@ -118,7 +103,7 @@ static void amd_uncore_stop(struct perf_event *event, int flags)
hwc->state |= PERF_HES_STOPPED;
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
- amd_uncore_read(event);
+ event->pmu->read(event);
hwc->state |= PERF_HES_UPTODATE;
}
}
@@ -126,15 +111,16 @@ static void amd_uncore_stop(struct perf_event *event, int flags)
static int amd_uncore_add(struct perf_event *event, int flags)
{
int i;
- struct amd_uncore *uncore = event_to_amd_uncore(event);
+ struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
+ struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
struct hw_perf_event *hwc = &event->hw;
/* are we already assigned? */
- if (hwc->idx != -1 && uncore->events[hwc->idx] == event)
+ if (hwc->idx != -1 && ctx->events[hwc->idx] == event)
goto out;
- for (i = 0; i < uncore->num_counters; i++) {
- if (uncore->events[i] == event) {
+ for (i = 0; i < pmu->num_counters; i++) {
+ if (ctx->events[i] == event) {
hwc->idx = i;
goto out;
}
@@ -142,8 +128,8 @@ static int amd_uncore_add(struct perf_event *event, int flags)
/* if not, take the first available counter */
hwc->idx = -1;
- for (i = 0; i < uncore->num_counters; i++) {
- if (cmpxchg(&uncore->events[i], NULL, event) == NULL) {
+ for (i = 0; i < pmu->num_counters; i++) {
+ if (cmpxchg(&ctx->events[i], NULL, event) == NULL) {
hwc->idx = i;
break;
}
@@ -153,23 +139,13 @@ static int amd_uncore_add(struct perf_event *event, int flags)
if (hwc->idx == -1)
return -EBUSY;
- hwc->config_base = uncore->msr_base + (2 * hwc->idx);
- hwc->event_base = uncore->msr_base + 1 + (2 * hwc->idx);
- hwc->event_base_rdpmc = uncore->rdpmc_base + hwc->idx;
+ hwc->config_base = pmu->msr_base + (2 * hwc->idx);
+ hwc->event_base = pmu->msr_base + 1 + (2 * hwc->idx);
+ hwc->event_base_rdpmc = pmu->rdpmc_base + hwc->idx;
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
- /*
- * The first four DF counters are accessible via RDPMC index 6 to 9
- * followed by the L3 counters from index 10 to 15. For processors
- * with more than four DF counters, the DF RDPMC assignments become
- * discontiguous as the additional counters are accessible starting
- * from index 16.
- */
- if (is_nb_event(event) && hwc->idx >= NUM_COUNTERS_NB)
- hwc->event_base_rdpmc += NUM_COUNTERS_L3;
-
if (flags & PERF_EF_START)
- amd_uncore_start(event, PERF_EF_RELOAD);
+ event->pmu->start(event, PERF_EF_RELOAD);
return 0;
}
@@ -177,55 +153,67 @@ static int amd_uncore_add(struct perf_event *event, int flags)
static void amd_uncore_del(struct perf_event *event, int flags)
{
int i;
- struct amd_uncore *uncore = event_to_amd_uncore(event);
+ struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
+ struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
struct hw_perf_event *hwc = &event->hw;
- amd_uncore_stop(event, PERF_EF_UPDATE);
+ event->pmu->stop(event, PERF_EF_UPDATE);
- for (i = 0; i < uncore->num_counters; i++) {
- if (cmpxchg(&uncore->events[i], event, NULL) == event)
+ for (i = 0; i < pmu->num_counters; i++) {
+ if (cmpxchg(&ctx->events[i], event, NULL) == event)
break;
}
hwc->idx = -1;
}
-/*
- * Return a full thread and slice mask unless user
- * has provided them
- */
-static u64 l3_thread_slice_mask(u64 config)
+static bool amd_uncore_group_valid(struct perf_event *event)
{
- if (boot_cpu_data.x86 <= 0x18)
- return ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) |
- ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK);
+ struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
+ struct perf_event *leader = event->group_leader;
+ struct perf_event *sibling;
+ int counters = 0;
+
+ if (leader->pmu == event->pmu)
+ counters++;
+
+ for_each_sibling_event(sibling, leader) {
+ if (sibling->pmu == event->pmu &&
+ sibling->state > PERF_EVENT_STATE_OFF)
+ counters++;
+ }
/*
- * If the user doesn't specify a threadmask, they're not trying to
- * count core 0, so we enable all cores & threads.
- * We'll also assume that they want to count slice 0 if they specify
- * a threadmask and leave sliceid and enallslices unpopulated.
+ * When pmu->event_init() is called, the event is yet to be linked to
+ * its leader's sibling list, so it is counted separately
*/
- if (!(config & AMD64_L3_F19H_THREAD_MASK))
- return AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES |
- AMD64_L3_EN_ALL_CORES;
-
- return config & (AMD64_L3_F19H_THREAD_MASK | AMD64_L3_SLICEID_MASK |
- AMD64_L3_EN_ALL_CORES | AMD64_L3_EN_ALL_SLICES |
- AMD64_L3_COREID_MASK);
+ return (counters + 1) <= pmu->num_counters;
}
static int amd_uncore_event_init(struct perf_event *event)
{
- struct amd_uncore *uncore;
+ struct amd_uncore_pmu *pmu;
+ struct amd_uncore_ctx *ctx;
struct hw_perf_event *hwc = &event->hw;
- u64 event_mask = AMD64_RAW_EVENT_MASK_NB;
if (event->attr.type != event->pmu->type)
return -ENOENT;
- if (pmu_version >= 2 && is_nb_event(event))
- event_mask = AMD64_PERFMON_V2_RAW_EVENT_MASK_NB;
+ if (event->cpu < 0)
+ return -EINVAL;
+
+ pmu = event_to_amd_uncore_pmu(event);
+ ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ if (!ctx)
+ return -ENODEV;
+
+ /*
+ * Ensure that all events in a group can be scheduled together so that
+ * a failure can be reported at perf_event_open() time rather than
+ * silently at pmu->add() time when no free counter is found
+ */
+ if (event->group_leader != event && !amd_uncore_group_valid(event))
+ return -EINVAL;
/*
* NB and Last level cache counters (MSRs) are shared across all cores
@@ -235,28 +223,14 @@ static int amd_uncore_event_init(struct perf_event *event)
* out. So we do not support sampling and per-thread events via
* CAP_NO_INTERRUPT, and we do not enable counter overflow interrupts:
*/
- hwc->config = event->attr.config & event_mask;
+ hwc->config = event->attr.config;
hwc->idx = -1;
- if (event->cpu < 0)
- return -EINVAL;
-
- /*
- * SliceMask and ThreadMask need to be set for certain L3 events.
- * For other events, the two fields do not affect the count.
- */
- if (l3_mask && is_llc_event(event))
- hwc->config |= l3_thread_slice_mask(event->attr.config);
-
- uncore = event_to_amd_uncore(event);
- if (!uncore)
- return -ENODEV;
-
/*
* since request can come in to any of the shared cores, we will remap
* to a single common cpu.
*/
- event->cpu = uncore->cpu;
+ event->cpu = ctx->cpu;
return 0;
}
@@ -278,17 +252,10 @@ static ssize_t amd_uncore_attr_show_cpumask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- cpumask_t *active_mask;
- struct pmu *pmu = dev_get_drvdata(dev);
-
- if (pmu->type == amd_nb_pmu.type)
- active_mask = &amd_nb_active_mask;
- else if (pmu->type == amd_llc_pmu.type)
- active_mask = &amd_llc_active_mask;
- else
- return 0;
+ struct pmu *ptr = dev_get_drvdata(dev);
+ struct amd_uncore_pmu *pmu = container_of(ptr, struct amd_uncore_pmu, pmu);
- return cpumap_print_to_pagebuf(true, buf, active_mask);
+ return cpumap_print_to_pagebuf(true, buf, &pmu->active_mask);
}
static DEVICE_ATTR(cpumask, S_IRUGO, amd_uncore_attr_show_cpumask, NULL);
@@ -396,113 +363,57 @@ static const struct attribute_group *amd_uncore_l3_attr_update[] = {
NULL,
};
-static struct pmu amd_nb_pmu = {
- .task_ctx_nr = perf_invalid_context,
- .attr_groups = amd_uncore_df_attr_groups,
- .name = "amd_nb",
- .event_init = amd_uncore_event_init,
- .add = amd_uncore_add,
- .del = amd_uncore_del,
- .start = amd_uncore_start,
- .stop = amd_uncore_stop,
- .read = amd_uncore_read,
- .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
- .module = THIS_MODULE,
-};
-
-static struct pmu amd_llc_pmu = {
- .task_ctx_nr = perf_invalid_context,
- .attr_groups = amd_uncore_l3_attr_groups,
- .attr_update = amd_uncore_l3_attr_update,
- .name = "amd_l2",
- .event_init = amd_uncore_event_init,
- .add = amd_uncore_add,
- .del = amd_uncore_del,
- .start = amd_uncore_start,
- .stop = amd_uncore_stop,
- .read = amd_uncore_read,
- .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
- .module = THIS_MODULE,
-};
-
-static struct amd_uncore *amd_uncore_alloc(unsigned int cpu)
-{
- return kzalloc_node(sizeof(struct amd_uncore), GFP_KERNEL,
- cpu_to_node(cpu));
-}
-
-static inline struct perf_event **
-amd_uncore_events_alloc(unsigned int num, unsigned int cpu)
-{
- return kzalloc_node(sizeof(struct perf_event *) * num, GFP_KERNEL,
- cpu_to_node(cpu));
-}
-
static int amd_uncore_cpu_up_prepare(unsigned int cpu)
{
- struct amd_uncore *uncore_nb = NULL, *uncore_llc = NULL;
-
- if (amd_uncore_nb) {
- *per_cpu_ptr(amd_uncore_nb, cpu) = NULL;
- uncore_nb = amd_uncore_alloc(cpu);
- if (!uncore_nb)
- goto fail;
- uncore_nb->cpu = cpu;
- uncore_nb->num_counters = num_counters_nb;
- uncore_nb->rdpmc_base = RDPMC_BASE_NB;
- uncore_nb->msr_base = MSR_F15H_NB_PERF_CTL;
- uncore_nb->active_mask = &amd_nb_active_mask;
- uncore_nb->pmu = &amd_nb_pmu;
- uncore_nb->events = amd_uncore_events_alloc(num_counters_nb, cpu);
- if (!uncore_nb->events)
+ struct amd_uncore_pmu *pmu;
+ struct amd_uncore_ctx *ctx;
+ int node = cpu_to_node(cpu), i;
+
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ *per_cpu_ptr(pmu->ctx, cpu) = NULL;
+ ctx = kzalloc_node(sizeof(struct amd_uncore_ctx), GFP_KERNEL,
+ node);
+ if (!ctx)
goto fail;
- uncore_nb->id = -1;
- *per_cpu_ptr(amd_uncore_nb, cpu) = uncore_nb;
- }
- if (amd_uncore_llc) {
- *per_cpu_ptr(amd_uncore_llc, cpu) = NULL;
- uncore_llc = amd_uncore_alloc(cpu);
- if (!uncore_llc)
- goto fail;
- uncore_llc->cpu = cpu;
- uncore_llc->num_counters = num_counters_llc;
- uncore_llc->rdpmc_base = RDPMC_BASE_LLC;
- uncore_llc->msr_base = MSR_F16H_L2I_PERF_CTL;
- uncore_llc->active_mask = &amd_llc_active_mask;
- uncore_llc->pmu = &amd_llc_pmu;
- uncore_llc->events = amd_uncore_events_alloc(num_counters_llc, cpu);
- if (!uncore_llc->events)
+ ctx->cpu = cpu;
+ ctx->events = kzalloc_node(sizeof(struct perf_event *) *
+ pmu->num_counters, GFP_KERNEL,
+ node);
+ if (!ctx->events)
goto fail;
- uncore_llc->id = -1;
- *per_cpu_ptr(amd_uncore_llc, cpu) = uncore_llc;
+
+ ctx->id = -1;
+ *per_cpu_ptr(pmu->ctx, cpu) = ctx;
}
return 0;
fail:
- if (uncore_nb) {
- kfree(uncore_nb->events);
- kfree(uncore_nb);
- }
+ /* Rollback */
+ for (; i >= 0; i--) {
+ pmu = &pmus[i];
+ ctx = *per_cpu_ptr(pmu->ctx, cpu);
+ if (!ctx)
+ continue;
- if (uncore_llc) {
- kfree(uncore_llc->events);
- kfree(uncore_llc);
+ kfree(ctx->events);
+ kfree(ctx);
}
return -ENOMEM;
}
-static struct amd_uncore *
-amd_uncore_find_online_sibling(struct amd_uncore *this,
- struct amd_uncore * __percpu *uncores)
+static struct amd_uncore_ctx *
+amd_uncore_find_online_sibling(struct amd_uncore_ctx *this,
+ struct amd_uncore_pmu *pmu)
{
unsigned int cpu;
- struct amd_uncore *that;
+ struct amd_uncore_ctx *that;
for_each_online_cpu(cpu) {
- that = *per_cpu_ptr(uncores, cpu);
+ that = *per_cpu_ptr(pmu->ctx, cpu);
if (!that)
continue;
@@ -523,24 +434,16 @@ amd_uncore_find_online_sibling(struct amd_uncore *this,
static int amd_uncore_cpu_starting(unsigned int cpu)
{
- unsigned int eax, ebx, ecx, edx;
- struct amd_uncore *uncore;
-
- if (amd_uncore_nb) {
- uncore = *per_cpu_ptr(amd_uncore_nb, cpu);
- cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
- uncore->id = ecx & 0xff;
-
- uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_nb);
- *per_cpu_ptr(amd_uncore_nb, cpu) = uncore;
- }
-
- if (amd_uncore_llc) {
- uncore = *per_cpu_ptr(amd_uncore_llc, cpu);
- uncore->id = get_llc_id(cpu);
+ struct amd_uncore_pmu *pmu;
+ struct amd_uncore_ctx *ctx;
+ int i;
- uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_llc);
- *per_cpu_ptr(amd_uncore_llc, cpu) = uncore;
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ ctx = *per_cpu_ptr(pmu->ctx, cpu);
+ ctx->id = pmu->id(cpu);
+ ctx = amd_uncore_find_online_sibling(ctx, pmu);
+ *per_cpu_ptr(pmu->ctx, cpu) = ctx;
}
return 0;
@@ -548,195 +451,356 @@ static int amd_uncore_cpu_starting(unsigned int cpu)
static void uncore_clean_online(void)
{
- struct amd_uncore *uncore;
+ struct amd_uncore_ctx *ctx;
struct hlist_node *n;
- hlist_for_each_entry_safe(uncore, n, &uncore_unused_list, node) {
- hlist_del(&uncore->node);
- kfree(uncore->events);
- kfree(uncore);
+ hlist_for_each_entry_safe(ctx, n, &uncore_unused_list, node) {
+ hlist_del(&ctx->node);
+ kfree(ctx->events);
+ kfree(ctx);
}
}
-static void uncore_online(unsigned int cpu,
- struct amd_uncore * __percpu *uncores)
+static int amd_uncore_cpu_online(unsigned int cpu)
{
- struct amd_uncore *uncore = *per_cpu_ptr(uncores, cpu);
+ struct amd_uncore_pmu *pmu;
+ struct amd_uncore_ctx *ctx;
+ int i;
uncore_clean_online();
- if (cpu == uncore->cpu)
- cpumask_set_cpu(cpu, uncore->active_mask);
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ ctx = *per_cpu_ptr(pmu->ctx, cpu);
+ if (cpu == ctx->cpu)
+ cpumask_set_cpu(cpu, &pmu->active_mask);
+ }
+
+ return 0;
}
-static int amd_uncore_cpu_online(unsigned int cpu)
+static int amd_uncore_cpu_down_prepare(unsigned int cpu)
{
- if (amd_uncore_nb)
- uncore_online(cpu, amd_uncore_nb);
+ struct amd_uncore_ctx *this, *that;
+ struct amd_uncore_pmu *pmu;
+ int i, j;
+
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ this = *per_cpu_ptr(pmu->ctx, cpu);
+
+ /* this cpu is going down, migrate to a shared sibling if possible */
+ for_each_online_cpu(j) {
+ that = *per_cpu_ptr(pmu->ctx, j);
+
+ if (cpu == j)
+ continue;
+
+ if (this == that) {
+ perf_pmu_migrate_context(&pmu->pmu, cpu, j);
+ cpumask_clear_cpu(cpu, &pmu->active_mask);
+ cpumask_set_cpu(j, &pmu->active_mask);
+ that->cpu = j;
+ break;
+ }
+ }
+ }
- if (amd_uncore_llc)
- uncore_online(cpu, amd_uncore_llc);
+ return 0;
+}
+
+static int amd_uncore_cpu_dead(unsigned int cpu)
+{
+ struct amd_uncore_ctx *ctx;
+ struct amd_uncore_pmu *pmu;
+ int i;
+
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ ctx = *per_cpu_ptr(pmu->ctx, cpu);
+ if (cpu == ctx->cpu)
+ cpumask_clear_cpu(cpu, &pmu->active_mask);
+
+ if (!--ctx->refcnt) {
+ kfree(ctx->events);
+ kfree(ctx);
+ }
+
+ *per_cpu_ptr(pmu->ctx, cpu) = NULL;
+ }
return 0;
}
-static void uncore_down_prepare(unsigned int cpu,
- struct amd_uncore * __percpu *uncores)
+static int amd_uncore_df_id(unsigned int cpu)
{
- unsigned int i;
- struct amd_uncore *this = *per_cpu_ptr(uncores, cpu);
+ unsigned int eax, ebx, ecx, edx;
- if (this->cpu != cpu)
- return;
+ cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
- /* this cpu is going down, migrate to a shared sibling if possible */
- for_each_online_cpu(i) {
- struct amd_uncore *that = *per_cpu_ptr(uncores, i);
+ return ecx & 0xff;
+}
- if (cpu == i)
- continue;
+static int amd_uncore_df_event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int ret = amd_uncore_event_init(event);
- if (this == that) {
- perf_pmu_migrate_context(this->pmu, cpu, i);
- cpumask_clear_cpu(cpu, that->active_mask);
- cpumask_set_cpu(i, that->active_mask);
- that->cpu = i;
- break;
- }
- }
+ hwc->config = event->attr.config &
+ (pmu_version >= 2 ? AMD64_PERFMON_V2_RAW_EVENT_MASK_NB :
+ AMD64_RAW_EVENT_MASK_NB);
+
+ return ret;
}
-static int amd_uncore_cpu_down_prepare(unsigned int cpu)
+static int amd_uncore_df_add(struct perf_event *event, int flags)
{
- if (amd_uncore_nb)
- uncore_down_prepare(cpu, amd_uncore_nb);
+ int ret = amd_uncore_add(event, flags & ~PERF_EF_START);
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (ret)
+ return ret;
+
+ /*
+ * The first four DF counters are accessible via RDPMC index 6 to 9
+ * followed by the L3 counters from index 10 to 15. For processors
+ * with more than four DF counters, the DF RDPMC assignments become
+ * discontiguous as the additional counters are accessible starting
+ * from index 16.
+ */
+ if (hwc->idx >= NUM_COUNTERS_NB)
+ hwc->event_base_rdpmc += NUM_COUNTERS_L3;
- if (amd_uncore_llc)
- uncore_down_prepare(cpu, amd_uncore_llc);
+ /* Delayed start after rdpmc base update */
+ if (flags & PERF_EF_START)
+ amd_uncore_start(event, PERF_EF_RELOAD);
return 0;
}
-static void uncore_dead(unsigned int cpu, struct amd_uncore * __percpu *uncores)
+static int amd_uncore_df_init(void)
{
- struct amd_uncore *uncore = *per_cpu_ptr(uncores, cpu);
+ struct attribute **df_attr = amd_uncore_df_format_attr;
+ struct amd_uncore_pmu *pmu = &pmus[num_pmus];
+ union cpuid_0x80000022_ebx ebx;
+ int ret;
- if (cpu == uncore->cpu)
- cpumask_clear_cpu(cpu, uncore->active_mask);
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_NB))
+ return 0;
- if (!--uncore->refcnt) {
- kfree(uncore->events);
- kfree(uncore);
+ /*
+ * For Family 17h and above, the Northbridge counters are repurposed
+ * as Data Fabric counters. The PMUs are exported based on family as
+ * either NB or DF.
+ */
+ strscpy(pmu->name, boot_cpu_data.x86 >= 0x17 ? "amd_df" : "amd_nb",
+ sizeof(pmu->name));
+
+ pmu->num_counters = NUM_COUNTERS_NB;
+ pmu->msr_base = MSR_F15H_NB_PERF_CTL;
+ pmu->rdpmc_base = RDPMC_BASE_NB;
+ pmu->id = amd_uncore_df_id;
+
+ if (pmu_version >= 2) {
+ *df_attr++ = &format_attr_event14v2.attr;
+ *df_attr++ = &format_attr_umask12.attr;
+ ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES);
+ pmu->num_counters = ebx.split.num_df_pmc;
+ } else if (boot_cpu_data.x86 >= 0x17) {
+ *df_attr = &format_attr_event14.attr;
}
- *per_cpu_ptr(uncores, cpu) = NULL;
+ pmu->ctx = alloc_percpu(struct amd_uncore_ctx *);
+ if (!pmu->ctx)
+ return -ENOMEM;
+
+ pmu->pmu = (struct pmu) {
+ .task_ctx_nr = perf_invalid_context,
+ .attr_groups = amd_uncore_df_attr_groups,
+ .name = pmu->name,
+ .event_init = amd_uncore_df_event_init,
+ .add = amd_uncore_df_add,
+ .del = amd_uncore_del,
+ .start = amd_uncore_start,
+ .stop = amd_uncore_stop,
+ .read = amd_uncore_read,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
+ };
+
+ ret = perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1);
+ if (ret) {
+ free_percpu(pmu->ctx);
+ pmu->ctx = NULL;
+ return ret;
+ }
+
+ pr_info("%d %s %s counters detected\n", pmu->num_counters,
+ boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON" : "",
+ pmu->pmu.name);
+
+ num_pmus++;
+
+ return 0;
}
-static int amd_uncore_cpu_dead(unsigned int cpu)
+static int amd_uncore_l3_id(unsigned int cpu)
{
- if (amd_uncore_nb)
- uncore_dead(cpu, amd_uncore_nb);
+ return get_llc_id(cpu);
+}
- if (amd_uncore_llc)
- uncore_dead(cpu, amd_uncore_llc);
+static int amd_uncore_l3_event_init(struct perf_event *event)
+{
+ int ret = amd_uncore_event_init(event);
+ struct hw_perf_event *hwc = &event->hw;
+ u64 config = event->attr.config;
+ u64 mask;
+
+ hwc->config = config & AMD64_RAW_EVENT_MASK_NB;
+
+ /*
+ * SliceMask and ThreadMask need to be set for certain L3 events.
+ * For other events, the two fields do not affect the count.
+ */
+ if (ret || boot_cpu_data.x86 < 0x17)
+ return ret;
+
+ mask = config & (AMD64_L3_F19H_THREAD_MASK | AMD64_L3_SLICEID_MASK |
+ AMD64_L3_EN_ALL_CORES | AMD64_L3_EN_ALL_SLICES |
+ AMD64_L3_COREID_MASK);
+
+ if (boot_cpu_data.x86 <= 0x18)
+ mask = ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) |
+ ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK);
+
+ /*
+ * If the user doesn't specify a ThreadMask, they're not trying to
+ * count core 0, so we enable all cores & threads.
+ * We'll also assume that they want to count slice 0 if they specify
+ * a ThreadMask and leave SliceId and EnAllSlices unpopulated.
+ */
+ else if (!(config & AMD64_L3_F19H_THREAD_MASK))
+ mask = AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES |
+ AMD64_L3_EN_ALL_CORES;
+
+ hwc->config |= mask;
return 0;
}
-static int __init amd_uncore_init(void)
+static int amd_uncore_l3_init(void)
{
- struct attribute **df_attr = amd_uncore_df_format_attr;
struct attribute **l3_attr = amd_uncore_l3_format_attr;
- union cpuid_0x80000022_ebx ebx;
- int ret = -ENODEV;
+ struct amd_uncore_pmu *pmu = &pmus[num_pmus];
+ int ret;
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
- boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
- return -ENODEV;
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_LLC))
+ return 0;
- if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
- return -ENODEV;
+ /*
+ * For Family 17h and above, L3 cache counters are available instead
+ * of L2 cache counters. The PMUs are exported based on family as
+ * either L2 or L3.
+ */
+ strscpy(pmu->name, boot_cpu_data.x86 >= 0x17 ? "amd_l3" : "amd_l2",
+ sizeof(pmu->name));
- if (boot_cpu_has(X86_FEATURE_PERFMON_V2))
- pmu_version = 2;
+ pmu->num_counters = NUM_COUNTERS_L2;
+ pmu->msr_base = MSR_F16H_L2I_PERF_CTL;
+ pmu->rdpmc_base = RDPMC_BASE_LLC;
+ pmu->id = amd_uncore_l3_id;
- num_counters_nb = NUM_COUNTERS_NB;
- num_counters_llc = NUM_COUNTERS_L2;
if (boot_cpu_data.x86 >= 0x17) {
- /*
- * For F17h and above, the Northbridge counters are
- * repurposed as Data Fabric counters. Also, L3
- * counters are supported too. The PMUs are exported
- * based on family as either L2 or L3 and NB or DF.
- */
- num_counters_llc = NUM_COUNTERS_L3;
- amd_nb_pmu.name = "amd_df";
- amd_llc_pmu.name = "amd_l3";
- l3_mask = true;
+ *l3_attr++ = &format_attr_event8.attr;
+ *l3_attr++ = &format_attr_umask8.attr;
+ *l3_attr++ = boot_cpu_data.x86 >= 0x19 ?
+ &format_attr_threadmask2.attr :
+ &format_attr_threadmask8.attr;
+ pmu->num_counters = NUM_COUNTERS_L3;
}
- if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) {
- if (pmu_version >= 2) {
- *df_attr++ = &format_attr_event14v2.attr;
- *df_attr++ = &format_attr_umask12.attr;
- } else if (boot_cpu_data.x86 >= 0x17) {
- *df_attr = &format_attr_event14.attr;
- }
+ pmu->ctx = alloc_percpu(struct amd_uncore_ctx *);
+ if (!pmu->ctx)
+ return -ENOMEM;
+
+ pmu->pmu = (struct pmu) {
+ .task_ctx_nr = perf_invalid_context,
+ .attr_groups = amd_uncore_l3_attr_groups,
+ .attr_update = amd_uncore_l3_attr_update,
+ .name = pmu->name,
+ .event_init = amd_uncore_l3_event_init,
+ .add = amd_uncore_add,
+ .del = amd_uncore_del,
+ .start = amd_uncore_start,
+ .stop = amd_uncore_stop,
+ .read = amd_uncore_read,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
+ };
+
+ ret = perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1);
+ if (ret) {
+ free_percpu(pmu->ctx);
+ pmu->ctx = NULL;
+ return ret;
+ }
- amd_uncore_nb = alloc_percpu(struct amd_uncore *);
- if (!amd_uncore_nb) {
- ret = -ENOMEM;
- goto fail_nb;
- }
- ret = perf_pmu_register(&amd_nb_pmu, amd_nb_pmu.name, -1);
- if (ret)
- goto fail_nb;
+ pr_info("%d %s %s counters detected\n", pmu->num_counters,
+ boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON" : "",
+ pmu->pmu.name);
- if (pmu_version >= 2) {
- ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES);
- num_counters_nb = ebx.split.num_df_pmc;
- }
+ num_pmus++;
- pr_info("%d %s %s counters detected\n", num_counters_nb,
- boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON" : "",
- amd_nb_pmu.name);
+ return 0;
+}
- ret = 0;
- }
+static void uncore_free(void)
+{
+ struct amd_uncore_pmu *pmu;
+ int i;
- if (boot_cpu_has(X86_FEATURE_PERFCTR_LLC)) {
- if (boot_cpu_data.x86 >= 0x19) {
- *l3_attr++ = &format_attr_event8.attr;
- *l3_attr++ = &format_attr_umask8.attr;
- *l3_attr++ = &format_attr_threadmask2.attr;
- } else if (boot_cpu_data.x86 >= 0x17) {
- *l3_attr++ = &format_attr_event8.attr;
- *l3_attr++ = &format_attr_umask8.attr;
- *l3_attr++ = &format_attr_threadmask8.attr;
- }
+ for (i = 0; i < num_pmus; i++) {
+ pmu = &pmus[i];
+ if (!pmu->ctx)
+ continue;
- amd_uncore_llc = alloc_percpu(struct amd_uncore *);
- if (!amd_uncore_llc) {
- ret = -ENOMEM;
- goto fail_llc;
- }
- ret = perf_pmu_register(&amd_llc_pmu, amd_llc_pmu.name, -1);
- if (ret)
- goto fail_llc;
-
- pr_info("%d %s %s counters detected\n", num_counters_llc,
- boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON" : "",
- amd_llc_pmu.name);
- ret = 0;
+ perf_pmu_unregister(&pmu->pmu);
+ free_percpu(pmu->ctx);
+ pmu->ctx = NULL;
}
+ num_pmus = 0;
+}
+
+static int __init amd_uncore_init(void)
+{
+ int ret;
+
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
+ boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+ return -ENODEV;
+
+ if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
+ return -ENODEV;
+
+ if (boot_cpu_has(X86_FEATURE_PERFMON_V2))
+ pmu_version = 2;
+
+ ret = amd_uncore_df_init();
+ if (ret)
+ goto fail;
+
+ ret = amd_uncore_l3_init();
+ if (ret)
+ goto fail;
+
/*
* Install callbacks. Core will call them for each online cpu.
*/
if (cpuhp_setup_state(CPUHP_PERF_X86_AMD_UNCORE_PREP,
"perf/x86/amd/uncore:prepare",
amd_uncore_cpu_up_prepare, amd_uncore_cpu_dead))
- goto fail_llc;
+ goto fail;
if (cpuhp_setup_state(CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING,
"perf/x86/amd/uncore:starting",
@@ -753,12 +817,8 @@ static int __init amd_uncore_init(void)
cpuhp_remove_state(CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING);
fail_prep:
cpuhp_remove_state(CPUHP_PERF_X86_AMD_UNCORE_PREP);
-fail_llc:
- if (boot_cpu_has(X86_FEATURE_PERFCTR_NB))
- perf_pmu_unregister(&amd_nb_pmu);
- free_percpu(amd_uncore_llc);
-fail_nb:
- free_percpu(amd_uncore_nb);
+fail:
+ uncore_free();
return ret;
}
@@ -768,18 +828,7 @@ static void __exit amd_uncore_exit(void)
cpuhp_remove_state(CPUHP_AP_PERF_X86_AMD_UNCORE_ONLINE);
cpuhp_remove_state(CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING);
cpuhp_remove_state(CPUHP_PERF_X86_AMD_UNCORE_PREP);
-
- if (boot_cpu_has(X86_FEATURE_PERFCTR_LLC)) {
- perf_pmu_unregister(&amd_llc_pmu);
- free_percpu(amd_uncore_llc);
- amd_uncore_llc = NULL;
- }
-
- if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) {
- perf_pmu_unregister(&amd_nb_pmu);
- free_percpu(amd_uncore_nb);
- amd_uncore_nb = NULL;
- }
+ uncore_free();
}
module_init(amd_uncore_init);
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index c3b0d15a9841..3e904ba94278 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -1124,7 +1124,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
{
u64 from, to;
int br_sel = cpuc->br_sel;
- int i, j, type, to_plm;
+ int i, j, type, from_plm, to_plm;
bool compress = false;
/* if sampling all branches, then nothing to filter */
@@ -1156,8 +1156,14 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
type |= X86_BR_NO_TX;
}
- /* if type does not correspond, then discard */
- if (type == X86_BR_NONE || (br_sel & type) != type) {
+ from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
+ /*
+ * If type does not correspond, then discard.
+ * Specifically reject entries whose from address is in
+ * kernel space when only X86_BR_USER is requested.
+ */
+ if (type == X86_BR_NONE || (br_sel & type) != type ||
+ (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
cpuc->lbr_entries[i].from = 0;
compress = true;
}
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 69043e02e8a7..6769820a007b 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -65,6 +65,7 @@ int uncore_die_to_segment(int die)
return bus ? pci_domain_nr(bus) : -EINVAL;
}
+/* Note: This API can only be used when NUMA information is available. */
int uncore_device_to_die(struct pci_dev *dev)
{
int node = pcibus_to_node(dev->bus);
@@ -1606,8 +1607,6 @@ static int uncore_event_cpu_online(unsigned int cpu)
die = topology_logical_die_id(cpu);
msr_ret = uncore_box_ref(uncore_msr_uncores, die, cpu);
mmio_ret = uncore_box_ref(uncore_mmio_uncores, die, cpu);
- if (msr_ret && mmio_ret)
- return -ENOMEM;
/*
* Check if there is an online cpu in the package
diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c
index 091ed4651471..f6e42926b3f2 100644
--- a/arch/x86/events/intel/uncore_snbep.c
+++ b/arch/x86/events/intel/uncore_snbep.c
@@ -1465,13 +1465,7 @@ static int snbep_pci2phy_map_init(int devid, int nodeid_loc, int idmap_loc, bool
}
map->pbus_to_dieid[bus] = die_id = uncore_device_to_die(ubox_dev);
-
raw_spin_unlock(&pci2phy_map_lock);
-
- if (WARN_ON_ONCE(die_id == -1)) {
- err = -EINVAL;
- break;
- }
}
}
@@ -6445,7 +6439,7 @@ static void spr_update_device_location(int type_id)
if (devfn != dev->devfn)
continue;
- die = uncore_device_to_die(dev);
+ die = uncore_pcibus_to_dieid(dev->bus);
if (die < 0)
continue;
@@ -6460,6 +6454,11 @@ static void spr_update_device_location(int type_id)
int spr_uncore_pci_init(void)
{
+ int ret = snbep_pci2phy_map_init(0x3250, SKX_CPUNODEID, SKX_GIDNIDMAP, true);
+
+ if (ret)
+ return ret;
+
/*
* The discovery table of UPI on some SPR variant is broken,
* which impacts the detection of both UPI and M3UPI uncore PMON.
diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index 54368a43abf6..b1e6c47113aa 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -9,6 +9,7 @@
#include <linux/compiler.h>
#include <linux/bug.h>
#include <linux/err.h>
+#include <asm/insn.h>
#include <asm/ptrace.h>
#define INSN_CODE_SEG_ADDR_SZ(params) ((params >> 4) & 0xf)
@@ -44,4 +45,39 @@ enum insn_mmio_type {
enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
+/*
+ * Write @val into *@reg following the x86 rules for writes to
+ * general-purpose registers (Intel SDM Vol. 1, "General-Purpose
+ * Registers in 64-Bit Mode"): an 8- or 16-bit write leaves the rest of
+ * the register untouched, a 32-bit write zero-extends the result into
+ * the upper 32 bits, and a 64-bit write replaces the whole register.
+ *
+ * @bytes is the width of the write, not a property of the instruction:
+ * an instruction that, say, sign-extends a 32-bit immediate into a
+ * 64-bit register does a 64-bit write here.
+ *
+ * @reg need not be 8-byte aligned: KVM's instruction emulator offsets
+ * the pointer by one byte to address the high-byte registers (AH, CH,
+ * DH, BH). Use narrow stores for the sub-word cases so the access
+ * width matches @bytes and the adjacent bytes are left alone.
+ */
+static inline void insn_assign_reg(unsigned long *reg, u64 val, int bytes)
+{
+ switch (bytes) {
+ case 1:
+ *(u8 *)reg = (u8)val;
+ break;
+ case 2:
+ *(u16 *)reg = (u16)val;
+ break;
+ case 4:
+ /* A 32-bit write zero-extends into the upper 32 bits. */
+ *reg = (u32)val;
+ break;
+ case 8:
+ *reg = val;
+ break;
+ }
+}
+
#endif /* _ASM_X86_INSN_EVAL_H */
diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h
index 571fe4d2d232..6cea7b15785a 100644
--- a/arch/x86/include/asm/linkage.h
+++ b/arch/x86/include/asm/linkage.h
@@ -103,7 +103,7 @@
.byte 0xb8 ASM_NL \
.long __kcfi_typeid_##name ASM_NL \
CFI_POST_PADDING \
- SYM_FUNC_END(__cfi_##name)
+ SYM_END(__cfi_##name, SYM_T_FUNC)
/* UML needs to be able to override memcpy() and friends for KASAN. */
#ifdef CONFIG_UML
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index 2e6c04d8a45b..32d327609cc4 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -89,6 +89,9 @@ int mm_pkey_alloc(struct mm_struct *mm)
u16 all_pkeys_mask = ((1U << arch_max_pkey()) - 1);
int ret;
+ if (!arch_pkeys_enabled())
+ return -1;
+
/*
* Are we out of pkeys? We must handle this specially
* because ffz() behavior is undefined if there are no
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 313cb2ffd6aa..e66bdb2f27a3 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -24,6 +24,7 @@
#include "kvm_emulate.h"
#include <linux/stringify.h>
#include <asm/debugreg.h>
+#include <asm/insn-eval.h>
#include <asm/nospec-branch.h>
#include <asm/ibt.h>
@@ -489,25 +490,6 @@ static void assign_masked(ulong *dest, ulong src, ulong mask)
*dest = (*dest & ~mask) | (src & mask);
}
-static void assign_register(unsigned long *reg, u64 val, int bytes)
-{
- /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
- switch (bytes) {
- case 1:
- *(u8 *)reg = (u8)val;
- break;
- case 2:
- *(u16 *)reg = (u16)val;
- break;
- case 4:
- *reg = (u32)val;
- break; /* 64b: zero-extend */
- case 8:
- *reg = val;
- break;
- }
-}
-
static inline unsigned long ad_mask(struct x86_emulate_ctxt *ctxt)
{
return (1UL << (ctxt->ad_bytes << 3)) - 1;
@@ -555,7 +537,7 @@ register_address_increment(struct x86_emulate_ctxt *ctxt, int reg, int inc)
{
ulong *preg = reg_rmw(ctxt, reg);
- assign_register(preg, *preg + inc, ctxt->ad_bytes);
+ insn_assign_reg(preg, *preg + inc, ctxt->ad_bytes);
}
static void rsp_increment(struct x86_emulate_ctxt *ctxt, int inc)
@@ -1778,7 +1760,7 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
static void write_register_operand(struct operand *op)
{
- return assign_register(op->addr.reg, op->val, op->bytes);
+ return insn_assign_reg(op->addr.reg, op->val, op->bytes);
}
static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op)
@@ -2010,7 +1992,7 @@ static int em_popa(struct x86_emulate_ctxt *ctxt)
rc = emulate_pop(ctxt, &val, ctxt->op_bytes);
if (rc != X86EMUL_CONTINUE)
break;
- assign_register(reg_rmw(ctxt, reg), val, ctxt->op_bytes);
+ insn_assign_reg(reg_rmw(ctxt, reg), val, ctxt->op_bytes);
--reg;
}
return rc;
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 667245e34fc5..d29cb592e703 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -622,6 +622,18 @@ static enum hrtimer_restart stimer_timer_callback(struct hrtimer *timer)
return HRTIMER_NORESTART;
}
+/*
+ * Translate a stimer expiry given in 100ns reference ticks into an
+ * an absolute deadline. Saturates on overflow.
+ */
+static ktime_t stimer_add_delta(ktime_t now, u64 delta_100ns)
+{
+ if (delta_100ns >= KTIME_MAX / 100)
+ return KTIME_MAX;
+
+ return ktime_add_safe(now, 100 * delta_100ns);
+}
+
/*
* stimer_start() assumptions:
* a) stimer->count is not equal to 0
@@ -631,6 +643,7 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer)
{
u64 time_now;
ktime_t ktime_now;
+ ktime_t deadline;
time_now = get_time_ref_counter(hv_stimer_to_vcpu(stimer)->kvm);
ktime_now = ktime_get();
@@ -653,10 +666,8 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer)
stimer->index,
time_now, stimer->exp_time);
- hrtimer_start(&stimer->timer,
- ktime_add_ns(ktime_now,
- 100 * (stimer->exp_time - time_now)),
- HRTIMER_MODE_ABS);
+ deadline = stimer_add_delta(ktime_now, stimer->exp_time - time_now);
+ hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS);
return 0;
}
stimer->exp_time = stimer->count;
@@ -675,9 +686,9 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer)
stimer->index,
time_now, stimer->count);
- hrtimer_start(&stimer->timer,
- ktime_add_ns(ktime_now, 100 * (stimer->count - time_now)),
- HRTIMER_MODE_ABS);
+ deadline = stimer_add_delta(ktime_now, stimer->count - time_now);
+ hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS);
+
return 0;
}
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index fb473e36438e..a130e2d70c48 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -1187,6 +1187,9 @@ static void nested_vmx_transition_tlb_flush(struct kvm_vcpu *vcpu,
* is the VPID incorporated into the MMU context. I.e. KVM must assume
* that the new vpid12 has never been used and thus represents a new
* guest ASID that cannot have entries in the TLB.
+ *
+ * Note, last_vpid is initialized as 0, so the first nested VM-Enter
+ * after VMXON will always flush the TLB to avoid using stale entries.
*/
if (is_vmenter && vmcs12->virtual_processor_id != vmx->nested.last_vpid) {
vmx->nested.last_vpid = vmcs12->virtual_processor_id;
@@ -3552,6 +3555,14 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
vmentry_fail_vmexit_guest_mode:
if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETTING)
vcpu->arch.tsc_offset -= vmcs12->tsc_offset;
+
+ /*
+ * Handle any TLB flush requests that were queued for L2 if KVM made it
+ * far enough along to switch to L2 context. Note, loading host state
+ * will generate any flushes for L1 required by VM-Exit.
+ */
+ kvm_service_local_tlb_flush_requests(vcpu);
+
leave_guest_mode(vcpu);
vmentry_fail_vmexit:
@@ -5190,6 +5201,13 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu)
vmx->nested.vpid02 = allocate_vpid();
+ /*
+ * Clear last_vpid to ensure that the VPID is flushed on the first
+ * nested VM-Enter. Otherwise, stale TLB entries from a previous life of
+ * the VPID (e.g. different vCPU or even different VM) could be used.
+ */
+ vmx->nested.last_vpid = 0;
+
vmx->nested.vmcs02_initialized = false;
vmx->nested.vmxon = true;
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b8d8b6b58a6c..b108e934a142 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1586,6 +1586,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
if (blkg->pd[pol->plid])
continue;
+ if (hlist_unhashed(&blkg->blkcg_node))
+ continue;
/* If prealloc matches, use it; otherwise try GFP_NOWAIT */
if (blkg == pinned_blkg) {
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 129732a8d0dd..7291f59986b8 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1603,7 +1603,7 @@ static void ioc_lat_stat(struct ioc *ioc, u32 *missed_ppm_ar, u32 *rq_wait_pct_p
u64 rq_wait_ns = 0;
int cpu, rw;
- for_each_online_cpu(cpu) {
+ for_each_possible_cpu(cpu) {
struct ioc_pcpu_stat *stat = per_cpu_ptr(ioc->pcpu_stat, cpu);
u64 this_rq_wait_ns;
diff --git a/block/blk-stat.c b/block/blk-stat.c
index e42c263e53fb..67ccba824ea9 100644
--- a/block/blk-stat.c
+++ b/block/blk-stat.c
@@ -86,7 +86,7 @@ static void blk_stat_timer_fn(struct timer_list *t)
for (bucket = 0; bucket < cb->buckets; bucket++)
blk_rq_stat_init(&cb->stat[bucket]);
- for_each_online_cpu(cpu) {
+ for_each_possible_cpu(cpu) {
struct blk_rq_stat *cpu_stat;
cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu);
diff --git a/block/genhd.c b/block/genhd.c
index 3e88fe53f269..596abf94cee5 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -466,6 +466,13 @@ int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
if (ret)
goto out_device_del;
+ /*
+ * We do not support partitions with zoned block devices, so do not try
+ * to scan the partitions table.
+ */
+ if (blk_queue_is_zoned(disk->queue))
+ disk->flags |= GENHD_FL_NO_PART;
+
/*
* avoid probable deadlock caused by allocating memory with
* GFP_KERNEL in runtime_resume callback of its all ancestor
@@ -604,6 +611,7 @@ static void __blk_mark_disk_dead(struct gendisk *disk)
*/
void blk_mark_disk_dead(struct gendisk *disk)
{
+ blk_queue_flag_set(QUEUE_FLAG_DYING, disk->queue);
__blk_mark_disk_dead(disk);
blk_report_disk_dead(disk, true);
}
diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c
index 4155594aefc6..ec6ff862dc9f 100644
--- a/block/kyber-iosched.c
+++ b/block/kyber-iosched.c
@@ -282,7 +282,7 @@ static void kyber_timer_fn(struct timer_list *t)
bool bad = false;
/* Sum all of the per-cpu latency histograms. */
- for_each_online_cpu(cpu) {
+ for_each_possible_cpu(cpu) {
struct kyber_cpu_latency *cpu_latency;
cpu_latency = per_cpu_ptr(kqd->cpu_latency, cpu);
diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c
index 822ceac83068..e632c53d6246 100644
--- a/drivers/accessibility/speakup/keyhelp.c
+++ b/drivers/accessibility/speakup/keyhelp.c
@@ -8,6 +8,7 @@
*/
#include <linux/keyboard.h>
+#include <linux/ctype.h>
#include "spk_priv.h"
#include "speakup.h"
@@ -111,7 +112,7 @@ static void say_key(int key)
spk_msg_get(MSG_KEYNAMES_START + (key - 1)));
}
-static int help_init(void)
+static void help_init(void)
{
char start = SPACE;
int i;
@@ -120,13 +121,19 @@ static int help_init(void)
state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2;
for (i = 0; i < num_funcs; i++) {
char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i);
+ char first_letter;
- if (start == *cur_funcname)
+ first_letter = tolower(*cur_funcname);
+
+ /* Accept only 'a'..'z' to index letter_offsets[] safely */
+ if (first_letter < 'a' || first_letter > 'z')
+ continue;
+
+ if (start == first_letter)
continue;
- start = *cur_funcname;
+ start = first_letter;
letter_offsets[(start & 31) - 1] = i;
}
- return 0;
}
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
@@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP));
return 1;
}
- ch |= 32; /* lower case */
+ ch = tolower(ch);
if (ch < 'a' || ch > 'z')
return -1;
if (letter_offsets[ch - 'a'] == -1) {
diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c
index a7522d409802..1eea7c492875 100644
--- a/drivers/accessibility/speakup/kobjects.c
+++ b/drivers/accessibility/speakup/kobjects.c
@@ -92,9 +92,9 @@ static void report_char_chartab_status(int reset, int received, int used,
if (reset) {
pr_info("%s reset to defaults\n", object_type[do_characters]);
} else if (received) {
- len = snprintf(buf, sizeof(buf),
- " updated %d of %d %s\n",
- used, received, object_type[do_characters]);
+ len = scnprintf(buf, sizeof(buf),
+ " updated %d of %d %s\n",
+ used, received, object_type[do_characters]);
if (rejected)
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
" with %d reject%s\n",
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index f94cddbbb55a..1515e79d0a2e 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1510,6 +1510,24 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
"ACPI EC", ec) >= 0;
}
+static int ec_prepare_gpio_irq(struct acpi_ec *ec, struct acpi_device *device)
+{
+ int irq;
+
+ if (!device || ec->gpe >= 0 || ec->irq >= 0)
+ return 0;
+
+ /* ACPI reduced hardware platforms use a GpioInt from _CRS. */
+ irq = acpi_dev_gpio_irq_get(device, 0);
+ if (irq == -EPROBE_DEFER)
+ return irq;
+
+ if (irq >= 0)
+ ec->irq = irq;
+
+ return 0;
+}
+
/**
* ec_install_handlers - Install service callbacks and register query methods.
* @ec: Target EC.
@@ -1524,7 +1542,6 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
* Return:
* -ENODEV if the address space handler cannot be installed, which means
* "unable to handle transactions",
- * -EPROBE_DEFER if GPIO IRQ acquisition needs to be deferred,
* or 0 (success) otherwise.
*/
static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
@@ -1557,19 +1574,6 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
if (!device)
return 0;
- if (ec->gpe < 0) {
- /* ACPI reduced hardware platforms use a GpioInt from _CRS. */
- int irq = acpi_dev_gpio_irq_get(device, 0);
- /*
- * Bail out right away for deferred probing or complete the
- * initialization regardless of any other errors.
- */
- if (irq == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- else if (irq >= 0)
- ec->irq = irq;
- }
-
if (!test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) {
/* Find and register all query methods */
acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
@@ -1647,6 +1651,14 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca
{
int ret;
+ /*
+ * GPIO IRQ lookup can defer. Do it before publishing the EC
+ * OpRegion to AML to avoid a spurious _REG(disconnect).
+ */
+ ret = ec_prepare_gpio_irq(ec, device);
+ if (ret)
+ return ret;
+
/* First EC capable of handling transactions */
if (!first_ec)
first_ec = ec;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 84030804a763..e9ec4eb16051 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -735,7 +735,6 @@ static int acpi_pci_root_add(struct acpi_device *device,
dev_err(&device->dev,
"Bus %04x:%02x not present in PCI namespace\n",
root->segment, (unsigned int)root->secondary.start);
- device->driver_data = NULL;
result = -ENODEV;
goto remove_dmar;
}
@@ -771,6 +770,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
if (hotadd)
dmar_device_remove(handle);
end:
+ device->driver_data = NULL;
kfree(root);
return result;
}
@@ -794,6 +794,7 @@ static void acpi_pci_root_remove(struct acpi_device *device)
pci_unlock_rescan_remove();
+ device->driver_data = NULL;
kfree(root);
}
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index fb93d5401d89..b89910a30bb9 100644
--- a/drivers/acpi/pfr_update.c
+++ b/drivers/acpi/pfr_update.c
@@ -120,7 +120,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
struct pfru_device *pfru_dev)
{
acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev);
- union acpi_object *out_obj;
+ union acpi_object *out_obj, *elem;
int ret = -EINVAL;
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
@@ -144,36 +144,39 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER)
goto free_acpi_buffer;
- cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value;
+ elem = out_obj->package.elements;
+
+ cap_hdr->status = elem[CAP_STATUS_IDX].integer.value;
if (cap_hdr->status != DSM_SUCCEED) {
ret = -EBUSY;
dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status);
goto free_acpi_buffer;
}
- cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value;
+ if (elem[CAP_CODE_TYPE_IDX].buffer.length > sizeof(cap_hdr->code_type) ||
+ elem[CAP_DRV_TYPE_IDX].buffer.length > sizeof(cap_hdr->drv_type) ||
+ elem[CAP_PLAT_ID_IDX].buffer.length > sizeof(cap_hdr->platform_id) ||
+ elem[CAP_OEM_ID_IDX].buffer.length > sizeof(cap_hdr->oem_id))
+ goto free_acpi_buffer;
+
+ cap_hdr->update_cap = elem[CAP_UPDATE_IDX].integer.value;
memcpy(&cap_hdr->code_type,
- out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer,
- out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length);
- cap_hdr->fw_version =
- out_obj->package.elements[CAP_FW_VER_IDX].integer.value;
- cap_hdr->code_rt_version =
- out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value;
+ elem[CAP_CODE_TYPE_IDX].buffer.pointer,
+ elem[CAP_CODE_TYPE_IDX].buffer.length);
+ cap_hdr->fw_version = elem[CAP_FW_VER_IDX].integer.value;
+ cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX].integer.value;
memcpy(&cap_hdr->drv_type,
- out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer,
- out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length);
- cap_hdr->drv_rt_version =
- out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value;
- cap_hdr->drv_svn =
- out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value;
+ elem[CAP_DRV_TYPE_IDX].buffer.pointer,
+ elem[CAP_DRV_TYPE_IDX].buffer.length);
+ cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX].integer.value;
+ cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX].integer.value;
memcpy(&cap_hdr->platform_id,
- out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer,
- out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length);
+ elem[CAP_PLAT_ID_IDX].buffer.pointer,
+ elem[CAP_PLAT_ID_IDX].buffer.length);
memcpy(&cap_hdr->oem_id,
- out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer,
- out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length);
- cap_hdr->oem_info_len =
- out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length;
+ elem[CAP_OEM_ID_IDX].buffer.pointer,
+ elem[CAP_OEM_ID_IDX].buffer.length);
+ cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX].buffer.length;
ret = 0;
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index dea60d694343..0c8ebd2e0182 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -320,11 +320,26 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
EXPORT_SYMBOL_GPL(acpi_get_cpuid);
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
-static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
+static bool madt_entry_is_valid(struct acpi_subtable_header *entry,
+ unsigned long end)
+{
+ unsigned long start = (unsigned long)entry;
+
+ if (start >= end || end - start < sizeof(*entry))
+ return false;
+
+ return entry->length >= sizeof(*entry) && entry->length <= end - start;
+}
+
+static int get_ioapic_id(struct acpi_subtable_header *entry,
+ const unsigned long end, u32 gsi_base,
u64 *phys_addr, int *ioapic_id)
{
struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
+ if (!madt_entry_is_valid(entry, end) || BAD_MADT_ENTRY(ioapic, end))
+ return 0;
+
if (ioapic->global_irq_base != gsi_base)
return 0;
@@ -345,17 +360,19 @@ static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
return apic_id;
entry = (unsigned long)madt;
+ if (madt->header.length < sizeof(*madt))
+ return apic_id;
madt_end = entry + madt->header.length;
/* Parse all entries looking for a match. */
entry += sizeof(struct acpi_table_madt);
- while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
+ while (madt_entry_is_valid((struct acpi_subtable_header *)entry,
+ madt_end)) {
hdr = (struct acpi_subtable_header *)entry;
if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
- get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
+ get_ioapic_id(hdr, madt_end, gsi_base, phys_addr, &apic_id))
break;
- else
- entry += hdr->length;
+ entry += hdr->length;
}
return apic_id;
@@ -382,7 +399,9 @@ static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
header = (struct acpi_subtable_header *)obj->buffer.pointer;
if (header->type == ACPI_MADT_TYPE_IO_APIC)
- get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
+ get_ioapic_id(header,
+ (unsigned long)header + obj->buffer.length,
+ gsi_base, phys_addr, &apic_id);
exit:
kfree(buffer.pointer);
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 4bd16b3f0781..53fad034703e 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -168,7 +168,7 @@ static int __acpi_processor_start(struct acpi_device *device)
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
- if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
+ if (cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr);
acpi_pss_perf_init(pr);
@@ -269,9 +269,11 @@ static int __init acpi_processor_driver_init(void)
acpi_processor_ignore_ppc_init();
}
+ acpi_processor_register_idle_driver();
+
result = driver_register(&acpi_processor_driver);
if (result < 0)
- return result;
+ goto unregister_idle_drv;
result = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online",
@@ -284,8 +286,19 @@ static int __init acpi_processor_driver_init(void)
acpi_processor_throttling_init();
return 0;
+
err:
driver_unregister(&acpi_processor_driver);
+
+unregister_idle_drv:
+ acpi_processor_unregister_idle_driver();
+
+ if (acpi_processor_cpufreq_init) {
+ cpufreq_unregister_notifier(&acpi_processor_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ acpi_processor_cpufreq_init = false;
+ }
+
return result;
}
@@ -303,6 +316,7 @@ static void __exit acpi_processor_driver_exit(void)
cpuhp_remove_state_nocalls(hp_online);
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
driver_unregister(&acpi_processor_driver);
+ acpi_processor_unregister_idle_driver();
}
module_init(acpi_processor_driver_init);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index b524cf27213d..d755c266e358 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -836,19 +836,13 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
return 0;
}
-static inline void acpi_processor_cstate_first_run_checks(void)
+static inline void acpi_processor_update_max_cstate(void)
{
- static int first_run;
-
- if (first_run)
- return;
dmi_check_system(processor_power_dmi_table);
max_cstate = acpi_processor_cstate_check(max_cstate);
if (max_cstate < ACPI_C_STATES_MAX)
pr_notice("processor limited to max C-state %d\n", max_cstate);
- first_run++;
-
if (nocst)
return;
@@ -857,7 +851,7 @@ static inline void acpi_processor_cstate_first_run_checks(void)
#else
static inline int disabled_by_idle_boot_param(void) { return 0; }
-static inline void acpi_processor_cstate_first_run_checks(void) { }
+static inline void acpi_processor_update_max_cstate(void) { }
static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
{
return -ENODEV;
@@ -950,6 +944,13 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
if (obj->type == ACPI_TYPE_BUFFER) {
struct acpi_power_register *reg;
+ if (obj->buffer.length < sizeof(*reg)) {
+ acpi_handle_debug(handle,
+ "Invalid register data for _LPI state %d\n",
+ state_idx);
+ continue;
+ }
+
reg = (struct acpi_power_register *)obj->buffer.pointer;
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO &&
reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)
@@ -966,13 +967,6 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
continue;
}
- /* elements[7,8] skipped for now i.e. Residency/Usage counter*/
-
- obj = pkg_elem + 9;
- if (obj->type == ACPI_TYPE_STRING)
- strscpy(lpi_state->desc, obj->string.pointer,
- ACPI_CX_DESC_LEN);
-
lpi_state->index = state_idx;
if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) {
pr_debug("No min. residency found, assuming 10 us\n");
@@ -995,6 +989,20 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state))
lpi_state->enable_parent_state = 0;
+
+ /* Skip elements [7-8] i.e. Residency/Usage counters. */
+
+ /*
+ * Avoid out-of-bounds access if the size of the package is less
+ * than expected.
+ */
+ if (element->package.count < 10)
+ continue;
+
+ obj = pkg_elem + 9;
+ if (obj->type == ACPI_TYPE_STRING)
+ strscpy(lpi_state->desc, obj->string.pointer,
+ ACPI_CX_DESC_LEN);
}
acpi_handle_debug(handle, "Found %d power states\n", state_idx);
@@ -1365,7 +1373,59 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
return 0;
}
-static int acpi_processor_registered;
+void acpi_processor_register_idle_driver(void)
+{
+ struct acpi_processor *pr;
+ int ret = -ENODEV;
+ int cpu;
+
+ /*
+ * If a cpuidle driver is already registered, there is no need to
+ * evaluate _CST or attempt to register the ACPI idle driver.
+ */
+ if (cpuidle_get_driver()) {
+ pr_debug("cpuidle driver %pS already registered.\n", cpuidle_get_driver());
+ return;
+ }
+
+ acpi_processor_update_max_cstate();
+
+ /*
+ * ACPI idle driver is used by all possible CPUs.
+ * Use the processor power info of one in them to set up idle states.
+ * Note that the existing idle handler will be used on platforms that
+ * only support C1.
+ */
+ for_each_possible_cpu(cpu) {
+ pr = per_cpu(processors, cpu);
+ if (!pr)
+ continue;
+
+ ret = acpi_processor_get_power_info(pr);
+ if (!ret) {
+ pr->flags.power_setup_done = 1;
+ acpi_processor_setup_cpuidle_states(pr);
+ break;
+ }
+ }
+
+ if (ret) {
+ pr_debug("No ACPI power information from any CPUs.\n");
+ return;
+ }
+
+ ret = cpuidle_register_driver(&acpi_idle_driver);
+ if (ret) {
+ pr_debug("register %s failed.\n", acpi_idle_driver.name);
+ return;
+ }
+ pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name);
+}
+
+void acpi_processor_unregister_idle_driver(void)
+{
+ cpuidle_unregister_driver(&acpi_idle_driver);
+}
int acpi_processor_power_init(struct acpi_processor *pr)
{
@@ -1375,27 +1435,10 @@ int acpi_processor_power_init(struct acpi_processor *pr)
if (disabled_by_idle_boot_param())
return 0;
- acpi_processor_cstate_first_run_checks();
-
if (!acpi_processor_get_power_info(pr))
pr->flags.power_setup_done = 1;
- /*
- * Install the idle handler if processor power management is supported.
- * Note that we use previously set idle handler will be used on
- * platforms that only support C1.
- */
if (pr->flags.power) {
- /* Register acpi_idle_driver if not already registered */
- if (!acpi_processor_registered) {
- acpi_processor_setup_cpuidle_states(pr);
- retval = cpuidle_register_driver(&acpi_idle_driver);
- if (retval)
- return retval;
- pr_debug("%s registered with cpuidle\n",
- acpi_idle_driver.name);
- }
-
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -1408,14 +1451,11 @@ int acpi_processor_power_init(struct acpi_processor *pr)
*/
retval = cpuidle_register_device(dev);
if (retval) {
- if (acpi_processor_registered == 0)
- cpuidle_unregister_driver(&acpi_idle_driver);
per_cpu(acpi_cpuidle_device, pr->id) = NULL;
kfree(dev);
return retval;
}
- acpi_processor_registered++;
}
return 0;
}
@@ -1429,10 +1469,6 @@ int acpi_processor_power_exit(struct acpi_processor *pr)
if (pr->flags.power) {
cpuidle_unregister_device(dev);
- acpi_processor_registered--;
- if (acpi_processor_registered == 0)
- cpuidle_unregister_driver(&acpi_idle_driver);
-
kfree(dev);
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 2892219c7afd..b2bb9b34fb8a 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -454,12 +454,10 @@ static void acpi_device_release(struct device *dev)
kfree(acpi_dev);
}
-static void acpi_device_del(struct acpi_device *device)
+static void acpi_device_cleanup(struct acpi_device *device)
{
struct acpi_device_bus_id *acpi_device_bus_id;
- mutex_lock(&acpi_device_lock);
-
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
if (!strcmp(acpi_device_bus_id->bus_id,
acpi_device_hid(device))) {
@@ -474,6 +472,13 @@ static void acpi_device_del(struct acpi_device *device)
}
list_del(&device->wakeup_list);
+}
+
+static void acpi_device_del(struct acpi_device *device)
+{
+ mutex_lock(&acpi_device_lock);
+
+ acpi_device_cleanup(device);
mutex_unlock(&acpi_device_lock);
@@ -739,7 +744,7 @@ int acpi_device_add(struct acpi_device *device)
err:
mutex_lock(&acpi_device_lock);
- list_del(&device->wakeup_list);
+ acpi_device_cleanup(device);
err_unlock:
mutex_unlock(&acpi_device_lock);
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 962720aafeda..886b7affd436 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -135,8 +135,10 @@ static int video_detect_portege_r100(const struct dmi_system_id *d)
struct pci_dev *dev;
/* Search for Trident CyberBlade XP4m32 to confirm Portégé R100 */
dev = pci_get_device(PCI_VENDOR_ID_TRIDENT, 0x2100, NULL);
- if (dev)
+ if (dev) {
acpi_backlight_dmi = acpi_backlight_vendor;
+ pci_dev_put(dev);
+ }
return 0;
}
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 9d59e6e2d63b..2d43307eddf9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1647,6 +1647,51 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
}
#endif
+/*
+ * The Marvell 88SE6111/6121/6145 ("Thor") family stops reporting interrupts
+ * for a port when HOST_IRQ_STAT is cleared while PxIS still holds bits: PxIS
+ * keeps its content, HOST_IRQ_STAT reads back as 0, the port is never looked
+ * at again and the command in flight only ends in a timeout. On a 88SE6121
+ * this makes every SATA-2 or SATA-3 disk fail to IDENTIFY, while SATA-1 disks
+ * happen to win the race often enough to work.
+ *
+ * Clearing the host status before servicing the ports avoids it. Marvell's
+ * own driver for these chips does the same and says so ("clear global before
+ * channel"), and ahci_xgene handles its broken edge latch the same way. The
+ * price is at most one spurious interrupt per valid one, which is why this is
+ * not the generic behaviour - see AHCI 1.1 section 10.6.2.
+ *
+ * Link: https://bugzilla.kernel.org/show_bug.cgi?id=216094
+ */
+static irqreturn_t ahci_mv_irq_handler(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct ahci_host_priv *hpriv = host->private_data;
+ void __iomem *mmio = hpriv->mmio;
+ unsigned int rc;
+ u32 irq_stat, irq_masked;
+
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ irq_masked = irq_stat & hpriv->port_map;
+
+ spin_lock(&host->lock);
+
+ /*
+ * Use the unmasked value to clear the interrupt, as a spurious pending
+ * event on a dummy port might cause a screaming IRQ.
+ */
+ writel(irq_stat, mmio + HOST_IRQ_STAT);
+
+ rc = ahci_handle_port_intr(host, irq_masked);
+
+ spin_unlock(&host->lock);
+
+ return IRQ_RETVAL(rc);
+}
+
static void ahci_remap_check(struct pci_dev *pdev, int bar,
struct ahci_host_priv *hpriv)
{
@@ -1890,6 +1935,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENOMEM;
hpriv->flags |= (unsigned long)pi.private_data;
+ /* the Marvell "Thor" family needs HOST_IRQ_STAT cleared first */
+ if (board_id == board_ahci_mv)
+ hpriv->irq_handler = ahci_mv_irq_handler;
+
/* MCP65 revision A1 and A2 can't do MSI */
if (board_id == board_ahci_mcp65 &&
(pdev->revision == 0xa1 || pdev->revision == 0xa2))
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 0da8c69dc966..144e855ce154 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3222,17 +3222,13 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax,
u64 sector, u32 count)
{
- struct scsi_device *sdp = cmd->device;
- size_t len = sdp->sector_size;
+ size_t len = ATA_SECT_SIZE;
size_t r;
__le64 *buf;
u32 i = 0;
unsigned long flags;
- WARN_ON(len > ATA_SCSI_RBUF_SIZE);
-
- if (len > ATA_SCSI_RBUF_SIZE)
- len = ATA_SCSI_RBUF_SIZE;
+ BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE);
spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
buf = ((void *)ata_scsi_rbuf);
@@ -3267,13 +3263,11 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
{
struct ata_taskfile *tf = &qc->tf;
struct scsi_cmnd *scmd = qc->scsicmd;
- struct scsi_device *sdp = scmd->device;
- size_t len = sdp->sector_size;
struct ata_device *dev = qc->dev;
const u8 *cdb = scmd->cmnd;
u64 block;
u32 n_block;
- const u32 trmax = len >> 3;
+ const u32 trmax = ATA_MAX_TRIM_RNUM;
u32 size;
u16 fp;
u8 bp = 0xff;
@@ -3318,13 +3312,13 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
goto invalid_param_len;
/*
- * size must match sector size in bytes
- * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count)
- * is defined as number of 512 byte blocks to be transferred.
+ * The TRIM descriptor is a single 512-byte page, which is the maximum
+ * WRITE SAME length advertised in the Block Limits VPD page. For DATA
+ * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of
+ * 512-byte blocks to be transferred.
*/
-
size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
- if (size != len)
+ if (size != ATA_SECT_SIZE)
goto invalid_param_len;
if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
@@ -3350,6 +3344,12 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
ATA_TFLAG_WRITE;
ata_qc_set_pc_nbytes(qc);
+ /*
+ * The DSM TRIM payload is a single 512-byte page, which may be smaller
+ * than the WRITE SAME data-out buffer (one logical block); only
+ * transfer that page so the length matches the COUNT field.
+ */
+ qc->nbytes = size;
return 0;
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index e243291a7e77..cc1480c267ae 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -594,6 +594,16 @@ static int charlcd_init(struct charlcd *lcd)
return 0;
}
+static void charlcd_deinit(struct charlcd *lcd)
+{
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
+
+ if (lcd->ops->backlight) {
+ cancel_delayed_work_sync(&priv->bl_work);
+ lcd->ops->backlight(lcd, CHARLCD_OFF);
+ }
+}
+
struct charlcd *charlcd_alloc(unsigned int drvdata_size)
{
struct charlcd_priv *priv;
@@ -653,8 +663,10 @@ int charlcd_register(struct charlcd *lcd)
return ret;
ret = misc_register(&charlcd_dev);
- if (ret)
+ if (ret) {
+ charlcd_deinit(lcd);
return ret;
+ }
the_charlcd = lcd;
register_reboot_notifier(&panel_notifier);
@@ -664,16 +676,11 @@ EXPORT_SYMBOL_GPL(charlcd_register);
int charlcd_unregister(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
-
unregister_reboot_notifier(&panel_notifier);
charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
misc_deregister(&charlcd_dev);
the_charlcd = NULL;
- if (lcd->ops->backlight) {
- cancel_delayed_work_sync(&priv->bl_work);
- priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
- }
+ charlcd_deinit(lcd);
return 0;
}
diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c
index 5b59d133b6af..a2d7c2c33769 100644
--- a/drivers/base/arch_numa.c
+++ b/drivers/base/arch_numa.c
@@ -105,6 +105,18 @@ static void __init setup_node_to_cpumask_map(void)
if (nr_node_ids == MAX_NUMNODES)
setup_nr_node_ids();
+ /*
+ * This check should never be true but it makes it clear to compilers
+ * that node_to_cpumask_map is bound by nr_node_ids, avoiding false
+ * positive fortify warnings when accessing node_to_cpumask_map in the
+ * for loop below.
+ */
+ if (unlikely(nr_node_ids > MAX_NUMNODES)) {
+ pr_err("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n",
+ nr_node_ids, MAX_NUMNODES);
+ return;
+ }
+
/* allocate and clear the mapping */
for (node = 0; node < nr_node_ids; node++) {
alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index b5715b8ded89..f03ea44609bf 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -347,9 +347,14 @@ static int cache_setup_properties(unsigned int cpu)
else if (!acpi_disabled)
ret = cache_setup_acpi(cpu);
- // Assume there is no cache information available in DT/ACPI from now.
- if (ret && use_arch_cache_info())
+ /*
+ * No DT/ACPI cache nodes; fall back to arch-derived topology (e.g.
+ * arm64 CLIDR_EL1) and clear the error to avoid a spurious warning.
+ */
+ if (ret && use_arch_cache_info()) {
use_arch_info = true;
+ ret = 0;
+ }
return ret;
}
diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
index bf68e3947814..b66a529fd041 100644
--- a/drivers/base/firmware_loader/fallback.c
+++ b/drivers/base/firmware_loader/fallback.c
@@ -91,6 +91,16 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
retval = -EINTR;
goto out;
}
+
+ /*
+ * device_add() exposes the loading interface before pending_list is
+ * linked into pending_fw_head, so fw_state_done() may run first.
+ */
+ if (fw_state_is_done(fw_priv)) {
+ mutex_unlock(&fw_lock);
+ goto out;
+ }
+
list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);
diff --git a/drivers/base/property.c b/drivers/base/property.c
index f0dd0d46c04b..b6a70830555e 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -750,18 +750,31 @@ struct fwnode_handle *
fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
+ const struct fwnode_handle *parent;
+ struct fwnode_handle *child_parent __free(fwnode_handle) = NULL;
struct fwnode_handle *next;
- if (IS_ERR_OR_NULL(fwnode))
+ /*
+ * If this function is in a loop and the previous iteration returned
+ * an child from fwnode->secondary, then we need to use the secondary
+ * as parent rather than @fwnode.
+ */
+ if (child) {
+ child_parent = fwnode_get_parent(child);
+ parent = child_parent;
+ } else {
+ parent = fwnode;
+ }
+ if (IS_ERR_OR_NULL(parent))
return NULL;
/* Try to find a child in primary fwnode */
- next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
+ next = fwnode_call_ptr_op(parent, get_next_child_node, child);
if (next)
return next;
/* When no more children in primary, continue with secondary */
- return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
+ return fwnode_get_next_child_node(parent->secondary, NULL);
}
EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 8dec5228fde3..f215536340db 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -194,6 +194,7 @@ EXPORT_SYMBOL_GPL(soc_device_unregister);
static int __init soc_bus_register(void)
{
+ struct soc_device *soc_dev;
int ret;
ret = bus_register(&soc_bus_type);
@@ -201,10 +202,20 @@ static int __init soc_bus_register(void)
return ret;
soc_bus_registered = true;
- if (early_soc_dev_attr)
- return PTR_ERR(soc_device_register(early_soc_dev_attr));
+ if (early_soc_dev_attr) {
+ soc_dev = soc_device_register(early_soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto err_unregister_bus;
+ }
+ }
return 0;
+
+err_unregister_bus:
+ soc_bus_registered = false;
+ bus_unregister(&soc_bus_type);
+ return ret;
}
core_initcall(soc_bus_register);
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index a7a3e3b66bb5..c34a5fc20f1e 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -518,7 +518,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (prop->is_inline)
return -EINVAL;
- if ((index + 1) * sizeof(*ref) > prop->length)
+ if (index >= prop->length / sizeof(*ref))
return -ENOENT;
ref_array = prop->pointer;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index b200950e8fb5..8131c72a9e48 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -3053,6 +3053,8 @@ static int mtip_block_ioctl(struct block_device *dev,
if (!dd)
return -ENOTTY;
+ guard(mutex)(&dd->ioctl_mutex);
+
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)))
return -ENOTTY;
@@ -3091,6 +3093,8 @@ static int mtip_block_compat_ioctl(struct block_device *dev,
if (!dd)
return -ENOTTY;
+ guard(mutex)(&dd->ioctl_mutex);
+
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)))
return -ENOTTY;
@@ -3716,6 +3720,7 @@ static int mtip_pci_probe(struct pci_dev *pdev,
dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node);
if (!dd)
return -ENOMEM;
+ mutex_init(&dd->ioctl_mutex);
/* Attach the private data to this PCI device. */
pci_set_drvdata(pdev, dd);
@@ -3888,6 +3893,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
}
set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag);
+ mutex_lock(&dd->ioctl_mutex);
if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
del_gendisk(dd->disk);
@@ -3916,6 +3922,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
/* De-initialize the protocol layer. */
mtip_hw_exit(dd);
+ mutex_unlock(&dd->ioctl_mutex);
if (dd->isr_workq) {
destroy_workqueue(dd->isr_workq);
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index f7328f19ac5c..0963c07b5845 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -12,6 +12,7 @@
#define __MTIP32XX_H__
#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/ata.h>
#include <linux/interrupt.h>
@@ -432,6 +433,7 @@ struct driver_data {
struct request_queue *queue; /* Our request queue. */
struct blk_mq_tag_set tags; /* blk_mq tags */
+ struct mutex ioctl_mutex;
struct mtip_port *port; /* Pointer to the port data structure. */
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index 355b23f3fc5d..a011ae2055d0 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -66,7 +66,7 @@ struct nullb_page {
#define NULLB_PAGE_FREE (MAP_SZ - 2)
static LIST_HEAD(nullb_list);
-static struct mutex lock;
+static DEFINE_MUTEX(lock);
static int null_major;
static DEFINE_IDA(nullb_indexes);
static struct blk_mq_tag_set tag_set;
@@ -165,8 +165,8 @@ static bool g_blocking;
module_param_named(blocking, g_blocking, bool, 0444);
MODULE_PARM_DESC(blocking, "Register as a blocking blk-mq driver device");
-static bool shared_tags;
-module_param(shared_tags, bool, 0444);
+static bool g_shared_tags;
+module_param_named(shared_tags, g_shared_tags, bool, 0444);
MODULE_PARM_DESC(shared_tags, "Share tag set between devices for blk-mq");
static bool g_shared_tag_bitmap;
@@ -311,7 +311,15 @@ static ssize_t nullb_device_bool_attr_store(bool *val, const char *page,
return count;
}
-/* The following macro should only be used with TYPE = {uint, ulong, bool}. */
+/*
+ * The following macro should only be used with TYPE = {uint, ulong, bool}.
+ *
+ * The device configuration is modified under the global lock to serialize
+ * attribute changes against null_add_dev() and null_del_dev(): without this,
+ * an attribute could be changed while null_add_dev() is running, that is,
+ * before NULLB_DEV_FL_CONFIGURED is set, which would let null_add_dev()
+ * observe inconsistent values for the device configuration.
+ */
#define NULLB_DEVICE_ATTR(NAME, TYPE, APPLY) \
static ssize_t \
nullb_device_##NAME##_show(struct config_item *item, char *page) \
@@ -331,6 +339,7 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \
ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
if (ret < 0) \
return ret; \
+ guard(mutex)(&lock); \
if (apply_fn) \
ret = apply_fn(dev, new_value); \
else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
@@ -350,9 +359,20 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,
struct blk_mq_tag_set *set;
int ret, nr_hw_queues;
+ lockdep_assert_held(&lock);
+
if (!dev->nullb)
return 0;
+ /*
+ * A shared tag_set is mapped via the module-wide queue counts, so a
+ * per-device resize is meaningless. On shrink it would also leave
+ * mq_map[] pointing at NULLed hctx slots, causing a NULL deref in
+ * blk_mq_map_swqueue(). Reject it.
+ */
+ if (dev->shared_tags)
+ return -EINVAL;
+
/*
* Make sure at least one submit queue exists.
*/
@@ -392,25 +412,13 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,
static int nullb_apply_submit_queues(struct nullb_device *dev,
unsigned int submit_queues)
{
- int ret;
-
- mutex_lock(&lock);
- ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
- mutex_unlock(&lock);
-
- return ret;
+ return nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
}
static int nullb_apply_poll_queues(struct nullb_device *dev,
unsigned int poll_queues)
{
- int ret;
-
- mutex_lock(&lock);
- ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
- mutex_unlock(&lock);
-
- return ret;
+ return nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
}
NULLB_DEVICE_ATTR(size, ulong, NULL);
@@ -438,6 +446,7 @@ NULLB_DEVICE_ATTR(zone_max_open, uint, NULL);
NULLB_DEVICE_ATTR(zone_max_active, uint, NULL);
NULLB_DEVICE_ATTR(virt_boundary, bool, NULL);
NULLB_DEVICE_ATTR(no_sched, bool, NULL);
+NULLB_DEVICE_ATTR(shared_tags, bool, NULL);
NULLB_DEVICE_ATTR(shared_tag_bitmap, bool, NULL);
static ssize_t nullb_device_power_show(struct config_item *item, char *page)
@@ -587,6 +596,7 @@ static struct configfs_attribute *nullb_device_attrs[] = {
&nullb_device_attr_zone_offline,
&nullb_device_attr_virt_boundary,
&nullb_device_attr_no_sched,
+ &nullb_device_attr_shared_tags,
&nullb_device_attr_shared_tag_bitmap,
NULL,
};
@@ -679,10 +689,11 @@ static ssize_t memb_group_features_show(struct config_item *item, char *page)
"badblocks,blocking,blocksize,cache_size,"
"completion_nsec,discard,home_node,hw_queue_depth,"
"irqmode,max_sectors,mbps,memory_backed,no_sched,"
- "poll_queues,power,queue_mode,shared_tag_bitmap,size,"
- "submit_queues,use_per_node_hctx,virt_boundary,zoned,"
- "zone_capacity,zone_max_active,zone_max_open,"
- "zone_nr_conv,zone_offline,zone_readonly,zone_size\n");
+ "poll_queues,power,queue_mode,shared_tag_bitmap,"
+ "shared_tags,size,submit_queues,use_per_node_hctx,"
+ "virt_boundary,zoned,zone_capacity,zone_max_active,"
+ "zone_max_open,zone_nr_conv,zone_offline,zone_readonly,"
+ "zone_size\n");
}
CONFIGFS_ATTR_RO(memb_group_, features);
@@ -764,6 +775,7 @@ static struct nullb_device *null_alloc_dev(void)
dev->zone_max_active = g_zone_max_active;
dev->virt_boundary = g_virt_boundary;
dev->no_sched = g_no_sched;
+ dev->shared_tags = g_shared_tags;
dev->shared_tag_bitmap = g_shared_tag_bitmap;
return dev;
}
@@ -2130,7 +2142,14 @@ static int null_add_dev(struct nullb_device *dev)
goto out_free_nullb;
if (dev->queue_mode == NULL_Q_MQ) {
- if (shared_tags) {
+ if (dev->shared_tags) {
+ if (!tag_set.ops) {
+ rv = null_init_tag_set(NULL, &tag_set);
+ if (rv) {
+ tag_set.ops = NULL;
+ goto out_cleanup_queues;
+ }
+ }
nullb->tag_set = &tag_set;
rv = 0;
} else {
@@ -2314,26 +2333,12 @@ static int __init null_init(void)
g_submit_queues = 1;
}
- if (g_queue_mode == NULL_Q_MQ && shared_tags) {
- ret = null_init_tag_set(NULL, &tag_set);
- if (ret)
- return ret;
- }
-
config_group_init(&nullb_subsys.su_group);
mutex_init(&nullb_subsys.su_mutex);
- ret = configfs_register_subsystem(&nullb_subsys);
- if (ret)
- goto err_tagset;
-
- mutex_init(&lock);
-
null_major = register_blkdev(0, "nullb");
- if (null_major < 0) {
- ret = null_major;
- goto err_conf;
- }
+ if (null_major < 0)
+ return null_major;
for (i = 0; i < nr_devices; i++) {
ret = null_create_dev();
@@ -2341,6 +2346,10 @@ static int __init null_init(void)
goto err_dev;
}
+ ret = configfs_register_subsystem(&nullb_subsys);
+ if (ret)
+ goto err_dev;
+
pr_info("module loaded\n");
return 0;
@@ -2350,10 +2359,7 @@ static int __init null_init(void)
null_destroy_dev(nullb);
}
unregister_blkdev(null_major, "nullb");
-err_conf:
- configfs_unregister_subsystem(&nullb_subsys);
-err_tagset:
- if (g_queue_mode == NULL_Q_MQ && shared_tags)
+ if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
return ret;
}
@@ -2373,10 +2379,8 @@ static void __exit null_exit(void)
}
mutex_unlock(&lock);
- if (g_queue_mode == NULL_Q_MQ && shared_tags)
+ if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
-
- mutex_destroy(&lock);
}
module_init(null_init);
diff --git a/drivers/block/null_blk/null_blk.h b/drivers/block/null_blk/null_blk.h
index 929f659dd255..7bcfc0922ae8 100644
--- a/drivers/block/null_blk/null_blk.h
+++ b/drivers/block/null_blk/null_blk.h
@@ -119,6 +119,7 @@ struct nullb_device {
bool zoned; /* if device is zoned */
bool virt_boundary; /* virtual boundary on/off for the device */
bool no_sched; /* no IO scheduler for the device */
+ bool shared_tags; /* share tag set between devices for blk-mq */
bool shared_tag_bitmap; /* use hostwide shared tags */
};
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 2587349f60ce..356f32b6f180 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1099,8 +1099,14 @@ static inline void __ublk_complete_rq(struct request *req)
*
* Re-read simply for this unlikely case.
*/
- if (unlikely(unmapped_bytes < io->res))
+ if (unlikely(unmapped_bytes < io->res)) {
+ if (unlikely(!unmapped_bytes)) {
+ res = BLK_STS_IOERR;
+ goto exit;
+ }
+
io->res = unmapped_bytes;
+ }
/*
* Run bio->bi_end_io() with softirqs disabled. If the final fput
@@ -1437,6 +1443,12 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
if (vma->vm_flags & VM_WRITE)
return -EPERM;
+ /*
+ * The per-queue command buffer is kernel-written ABI; prevent
+ * the daemon from upgrading to writable via mprotect().
+ */
+ vm_flags_clear(vma, VM_MAYWRITE);
+
end = UBLKSRV_CMD_BUF_OFFSET + ub->dev_info.nr_hw_queues * max_sz;
if (phys_off < UBLKSRV_CMD_BUF_OFFSET || phys_off >= end)
return -EINVAL;
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 4784da07dd8a..43ddde0a4d48 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -2983,7 +2983,7 @@ int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
/* Handle all diagnostics events separately. May still call
* hci_recv_frame.
*/
- if (len >= sizeof(diagnostics_hdr) &&
+ if (len + 1 >= sizeof(diagnostics_hdr) &&
memcmp(&skb->data[2], diagnostics_hdr,
sizeof(diagnostics_hdr)) == 0) {
return btintel_diagnostics(hdev, skb);
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index d424c6a9be3a..fe9710a236d9 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -822,6 +822,7 @@ static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
{
+ int reset_err = 0;
u32 val;
int err;
@@ -920,8 +921,10 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
err = readx_poll_timeout(btmtk_usb_reset_done, hdev, val,
val & MTK_BT_RST_DONE, 20000, 1000000);
- if (err < 0)
+ if (err < 0) {
bt_dev_err(hdev, "Reset timeout");
+ reset_err = err;
+ }
if (dev_id == 0x7922) {
err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
@@ -933,7 +936,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
if (err < 0 || !val)
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
- return err;
+ return reset_err;
}
EXPORT_SYMBOL_GPL(btmtk_usb_subsys_reset);
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 019cf85d3c07..cd3275bc3ce0 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -258,12 +258,24 @@ static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev,
struct sk_buff *skb)
{
struct mtkbtsdio_hdr *sdio_hdr;
+ unsigned int len, pad_len;
int err;
- /* Make sure that there are enough rooms for SDIO header */
- if (unlikely(skb_headroom(skb) < sizeof(*sdio_hdr))) {
- err = pskb_expand_head(skb, sizeof(*sdio_hdr), 0,
- GFP_ATOMIC);
+ /* Make sure that the data buffer is not shared with anyone else and
+ * that there is enough room for the SDIO header
+ */
+ err = skb_cow_head(skb, sizeof(*sdio_hdr));
+ if (err < 0)
+ return err;
+
+ /* The transfer is rounded up to the SDIO block size, so the buffer
+ * has to provide tailroom for the padding as well
+ */
+ len = skb->len + sizeof(*sdio_hdr);
+ pad_len = round_up(len, MTK_SDIO_BLOCK_SIZE) - len;
+
+ if (unlikely(skb_tailroom(skb) < pad_len)) {
+ err = pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC);
if (err < 0)
return err;
}
@@ -276,19 +288,22 @@ static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev,
sdio_hdr->reserved = cpu_to_le16(0);
sdio_hdr->bt_type = hci_skb_pkt_type(skb);
+ /* Zero the padding so that no uninitialised memory is sent out */
+ skb_put_zero(skb, pad_len);
+
clear_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);
- err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data,
- round_up(skb->len, MTK_SDIO_BLOCK_SIZE));
+ err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data, skb->len);
if (err < 0)
- goto err_skb_pull;
+ goto err_skb_restore;
- bdev->hdev->stat.byte_tx += skb->len;
+ bdev->hdev->stat.byte_tx += len;
kfree_skb(skb);
return 0;
-err_skb_pull:
+err_skb_restore:
+ skb_trim(skb, len);
skb_pull(skb, sizeof(*sdio_hdr));
return err;
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 09ef7df5c231..a5f80e4aa0e2 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -1018,8 +1018,7 @@ int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
baswap(&bdaddr_swapped, bdaddr);
skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6,
- &bdaddr_swapped, HCI_EV_VENDOR,
- HCI_INIT_TIMEOUT);
+ &bdaddr_swapped, 0, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 08b01d93b86c..1edf8674dd10 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -747,6 +747,12 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ /* Additional Realtek 8761CU Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
@@ -907,6 +913,7 @@ struct btusb_data {
bool usb_alt6_packet_flow;
int isoc_altsetting;
int suspend_count;
+ const struct usb_device_id *match_id;
int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
int (*recv_acl)(struct hci_dev *hdev, struct sk_buff *skb);
@@ -2870,14 +2877,15 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
const bdaddr_t *bdaddr)
{
+ bdaddr_t bdaddr_swapped;
struct sk_buff *skb;
- u8 buf[6];
long ret;
- memcpy(buf, bdaddr, sizeof(bdaddr_t));
+ baswap(&bdaddr_swapped, bdaddr);
- skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(buf), buf,
- HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(bdaddr_swapped),
+ &bdaddr_swapped, HCI_EV_CMD_COMPLETE,
+ HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "Change address command failed (%ld)", ret);
@@ -3460,8 +3468,10 @@ static int btusb_setup_qca(struct hci_dev *hdev)
if (err < 0)
return err;
- btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
- btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
+ if (btdata->match_id->driver_info & BTUSB_QCA_WCN6855) {
+ btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
+ btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
+ }
if (!(status & QCA_SYSCFG_UPDATED)) {
err = btusb_setup_qca_load_nvm(hdev, &ver, info);
@@ -3488,31 +3498,14 @@ static inline int __set_diag_interface(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct usb_interface *intf = data->diag;
- int i;
+ int ret;
if (!data->diag)
return -ENODEV;
- data->diag_tx_ep = NULL;
- data->diag_rx_ep = NULL;
-
- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
- struct usb_endpoint_descriptor *ep_desc;
-
- ep_desc = &intf->cur_altsetting->endpoint[i].desc;
-
- if (!data->diag_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
- data->diag_tx_ep = ep_desc;
- continue;
- }
-
- if (!data->diag_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
- data->diag_rx_ep = ep_desc;
- continue;
- }
- }
-
- if (!data->diag_tx_ep || !data->diag_rx_ep) {
+ ret = usb_find_common_endpoints(intf->cur_altsetting, &data->diag_rx_ep,
+ &data->diag_tx_ep, NULL, NULL);
+ if (ret) {
bt_dev_err(hdev, "invalid diagnostic descriptors");
return -ENODEV;
}
@@ -3713,12 +3706,11 @@ static const struct file_operations force_poll_sync_fops = {
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct usb_endpoint_descriptor *ep_desc;
struct gpio_desc *reset_gpio;
struct btusb_data *data;
struct hci_dev *hdev;
unsigned ifnum_base;
- int i, err, priv_size;
+ int err, priv_size;
BT_DBG("intf %p id %p", intf, id);
@@ -3755,26 +3747,10 @@ static int btusb_probe(struct usb_interface *intf,
if (!data)
return -ENOMEM;
- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
- ep_desc = &intf->cur_altsetting->endpoint[i].desc;
-
- if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
- data->intr_ep = ep_desc;
- continue;
- }
-
- if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
- data->bulk_tx_ep = ep_desc;
- continue;
- }
-
- if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
- data->bulk_rx_ep = ep_desc;
- continue;
- }
- }
-
- if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+ data->match_id = id;
+ err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep,
+ &data->bulk_tx_ep, &data->intr_ep, NULL);
+ if (err) {
kfree(data);
return -ENODEV;
}
@@ -4342,10 +4318,8 @@ static struct usb_driver btusb_driver = {
.disable_hub_initiated_lpm = 1,
#ifdef CONFIG_DEV_COREDUMP
- .drvwrap = {
- .driver = {
- .coredump = btusb_coredump,
- },
+ .driver = {
+ .coredump = btusb_coredump,
},
#endif
};
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 874d23089b39..b06cb1256fd9 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -548,6 +548,7 @@ static int bcm_close(struct hci_uart *hu)
if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) {
devm_free_irq(bdev->dev, bdev->irq, bdev);
device_init_wakeup(bdev->dev, false);
+ pm_runtime_dont_use_autosuspend(bdev->dev);
pm_runtime_disable(bdev->dev);
}
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index c0436881a533..b58db0d25f08 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -987,8 +987,10 @@ static void h5_btrtl_open(struct h5 *h5)
static void h5_btrtl_close(struct h5 *h5)
{
- if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags))
+ if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
+ pm_runtime_dont_use_autosuspend(&h5->hu->serdev->dev);
pm_runtime_disable(&h5->hu->serdev->dev);
+ }
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
gpiod_set_value_cansleep(h5->enable_gpio, 0);
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 78afb9a348e7..9da8fd32180e 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -348,6 +348,7 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
devm_free_irq(&idev->pdev->dev, idev->irq, idev);
device_wakeup_disable(&idev->pdev->dev);
+ pm_runtime_dont_use_autosuspend(&idev->pdev->dev);
pm_runtime_disable(&idev->pdev->dev);
}
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 573be8c3b4dc..548e58e69eb7 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -445,7 +445,7 @@ static int hci_uart_setup(struct hci_dev *hdev)
if (IS_ERR(skb)) {
BT_ERR("%s: Reading local version information failed (%ld)",
hdev->name, PTR_ERR(skb));
- return 0;
+ return PTR_ERR(skb);
}
if (skb->len != sizeof(*ver)) {
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index 1165831570e3..753343ca8489 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -221,7 +221,7 @@ static int hci_uart_setup(struct hci_dev *hdev)
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading local version info failed (%ld)",
PTR_ERR(skb));
- return 0;
+ return PTR_ERR(skb);
}
if (skb->len != sizeof(*ver))
diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index ca62b7369939..2db5c7a519b2 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -120,9 +120,13 @@ static int virtbt_setup_zephyr(struct hci_dev *hdev)
if (IS_ERR(skb))
return PTR_ERR(skb);
- bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
+ /* Bounded print: the backend 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;
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 03a4be194e90..180b87ed6ddb 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1323,14 +1323,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
ret = dev_set_name(&mhi_dev->dev, "%s_%s",
dev_name(&mhi_cntrl->mhi_dev->dev),
mhi_dev->name);
- if (ret) {
- put_device(&mhi_dev->dev);
- return ret;
- }
+ if (ret)
+ goto err_put_channels;
ret = device_add(&mhi_dev->dev);
if (ret)
- put_device(&mhi_dev->dev);
+ goto err_put_channels;
+
+ return 0;
+
+err_put_channels:
+ put_device(&mhi_dev->dev); /* DL channel reference */
+ put_device(&mhi_dev->dev); /* UL channel reference */
+ put_device(&mhi_dev->dev); /* device_initialize() reference */
return ret;
}
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 196929fff243..e99ac5e72ac0 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -168,6 +168,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state);
void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
{
+ int __maybe_unused ret;
+ u32 tmp;
+
if (mhi_cntrl->reset) {
mhi_cntrl->reset(mhi_cntrl);
return;
@@ -176,6 +179,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
/* Generic MHI SoC reset */
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
MHI_SOC_RESET_REQ);
+ /* Flush the posted write to the device (ignore return value) */
+ ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
+ &tmp);
}
EXPORT_SYMBOL_GPL(mhi_soc_reset);
diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c
index c1fef1b4bd89..8d2eb955dc92 100644
--- a/drivers/bus/qcom-ebi2.c
+++ b/drivers/bus/qcom-ebi2.c
@@ -292,7 +292,6 @@ static void qcom_ebi2_setup_chipselect(struct device_node *np,
static int qcom_ebi2_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct device_node *child;
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *ebi2_base;
@@ -303,41 +302,23 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
u32 val;
int ret;
- ebi2xclk = devm_clk_get(dev, "ebi2x");
+ ebi2xclk = devm_clk_get_enabled(dev, "ebi2x");
if (IS_ERR(ebi2xclk))
return PTR_ERR(ebi2xclk);
- ret = clk_prepare_enable(ebi2xclk);
- if (ret) {
- dev_err(dev, "could not enable EBI2X clk (%d)\n", ret);
- return ret;
- }
-
- ebi2clk = devm_clk_get(dev, "ebi2");
- if (IS_ERR(ebi2clk)) {
- ret = PTR_ERR(ebi2clk);
- goto err_disable_2x_clk;
- }
-
- ret = clk_prepare_enable(ebi2clk);
- if (ret) {
- dev_err(dev, "could not enable EBI2 clk\n");
- goto err_disable_2x_clk;
- }
+ ebi2clk = devm_clk_get_enabled(dev, "ebi2");
+ if (IS_ERR(ebi2clk))
+ return PTR_ERR(ebi2clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ebi2_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ebi2_base)) {
- ret = PTR_ERR(ebi2_base);
- goto err_disable_clk;
- }
+ if (IS_ERR(ebi2_base))
+ return PTR_ERR(ebi2_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ebi2_xmem = devm_ioremap_resource(dev, res);
- if (IS_ERR(ebi2_xmem)) {
- ret = PTR_ERR(ebi2_xmem);
- goto err_disable_clk;
- }
+ if (IS_ERR(ebi2_xmem))
+ return PTR_ERR(ebi2_xmem);
/* Allegedly this turns the power save mode off */
writel(0UL, ebi2_xmem + EBI2_XMEM_CFG);
@@ -348,15 +329,13 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
writel(val, ebi2_base);
/* Walk over the child nodes and see what chipselects we use */
- for_each_available_child_of_node(np, child) {
+ for_each_available_child_of_node_scoped(np, child) {
u32 csindex;
/* Figure out the chipselect */
ret = of_property_read_u32(child, "reg", &csindex);
- if (ret) {
- of_node_put(child);
+ if (ret)
return ret;
- }
if (csindex > 5) {
dev_err(dev,
@@ -376,15 +355,9 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
}
if (have_children)
- return of_platform_default_populate(np, NULL, dev);
- return 0;
-
-err_disable_clk:
- clk_disable_unprepare(ebi2clk);
-err_disable_2x_clk:
- clk_disable_unprepare(ebi2xclk);
+ return devm_of_platform_populate(dev);
- return ret;
+ return 0;
}
static const struct of_device_id qcom_ebi2_of_match[] = {
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index b6a7d0d3f153..527a7690e19c 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -672,6 +672,7 @@ static struct device_node *stdout_path;
static void sysc_init_stdout_path(struct sysc *ddata)
{
+ struct device_node *chosen;
struct device_node *np = NULL;
const char *uart;
@@ -681,15 +682,18 @@ static void sysc_init_stdout_path(struct sysc *ddata)
if (stdout_path)
return;
- np = of_find_node_by_path("/chosen");
- if (!np)
+ chosen = of_find_node_by_path("/chosen");
+ if (!chosen)
goto err;
- uart = of_get_property(np, "stdout-path", NULL);
- if (!uart)
+ uart = of_get_property(chosen, "stdout-path", NULL);
+ if (!uart) {
+ of_node_put(chosen);
goto err;
+ }
np = of_find_node_by_path(uart);
+ of_node_put(chosen);
if (!np)
goto err;
diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c
index d7b42888f25c..3e032ef2ccc7 100644
--- a/drivers/char/hw_random/ks-sa-rng.c
+++ b/drivers/char/hw_random/ks-sa-rng.c
@@ -113,8 +113,7 @@ static unsigned int refill_delay_ns(unsigned long clk_rate)
static int ks_sa_rng_init(struct hwrng *rng)
{
u32 value;
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk);
/* Enable RNG module */
@@ -153,8 +152,7 @@ static int ks_sa_rng_init(struct hwrng *rng)
static void ks_sa_rng_cleanup(struct hwrng *rng)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
/* Disable RNG */
writel(0, &ks_sa_rng->reg_rng->control);
@@ -164,8 +162,7 @@ static void ks_sa_rng_cleanup(struct hwrng *rng)
static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
/* Read random data */
data[0] = readl(&ks_sa_rng->reg_rng->output_l);
@@ -179,8 +176,7 @@ static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
static int ks_sa_rng_data_present(struct hwrng *rng, int wait)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
u64 now = ktime_get_ns();
u32 ready;
@@ -225,7 +221,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev)
.data_present = ks_sa_rng_data_present,
.cleanup = ks_sa_rng_cleanup,
};
- ks_sa_rng->rng.priv = (unsigned long)dev;
ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ks_sa_rng->reg_rng))
@@ -252,9 +247,14 @@ static int ks_sa_rng_probe(struct platform_device *pdev)
return ret;
}
- platform_set_drvdata(pdev, ks_sa_rng);
+ ret = devm_hwrng_register(dev, &ks_sa_rng->rng);
+ if (ret) {
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return ret;
+ }
- return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng);
+ return 0;
}
static int ks_sa_rng_remove(struct platform_device *pdev)
diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c
index 937189191512..5f38b0114e9b 100644
--- a/drivers/char/ipmi/ipmb_dev_int.c
+++ b/drivers/char/ipmi/ipmb_dev_int.c
@@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf,
u8 msg[MAX_MSG_LEN];
ssize_t ret;
- if (count > sizeof(msg))
+ if (!count || count > sizeof(msg))
return -EINVAL;
if (copy_from_user(&msg, buf, count))
return -EFAULT;
- if (count < msg[0])
+ if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN ||
+ count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1)
return -EINVAL;
rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]);
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 7dec2138b874..11e3012349c5 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -3693,6 +3693,7 @@ int ipmi_add_smi(struct module *owner,
out_err_bmc_reg:
ipmi_bmc_unregister(intf);
out_err_started:
+ intf->in_shutdown = true;
if (intf->handlers->shutdown)
intf->handlers->shutdown(intf->send_info);
out_err:
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 58e9dcc2a308..550ddbf1f327 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode)
return IEEE1284_PH_FWD_IDLE;
}
-static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
+static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec)
{
+ struct timespec64 ts;
long to_jiffies;
- if ((tv_sec < 0) || (tv_usec < 0))
+ if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC)
return -EINVAL;
- to_jiffies = usecs_to_jiffies(tv_usec);
- to_jiffies += tv_sec * HZ;
+ ts.tv_sec = tv_sec;
+ ts.tv_nsec = tv_usec * NSEC_PER_USEC;
+ to_jiffies = timespec64_to_jiffies(&ts);
if (to_jiffies <= 0)
return -EINVAL;
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index f78c61f4163d..e3ceda557331 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -93,7 +93,9 @@ static u8 st33zp24_status(struct tpm_chip *chip)
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
- tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
+ if (tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1) != 1)
+ return 0;
+
return data;
}
@@ -104,10 +106,10 @@ static bool check_locality(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
- u8 status;
+ int status;
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
- if (status && (data &
+ if (status == 1 && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
return true;
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index d7be03c41098..f472073f6d9b 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -181,8 +181,10 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
timeout);
if (rc > 0)
return 0;
- /* At this point we know that the SINT pin is asserted, so we
- * do not need to do i2c_nuvoton_check_status */
+
+ disable_irq(priv->irq);
+ if (rc < 0)
+ return rc;
} else {
unsigned long ten_msec, stop;
bool status_valid;
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index f60bb6151402..0eddd759ed41 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -860,7 +860,7 @@ static int __init hwicap_module_init(void)
HWICAP_DEVICES,
DRIVER_NAME);
if (retval < 0)
- return retval;
+ goto failed_class;
retval = platform_driver_register(&hwicap_platform_driver);
if (retval)
@@ -871,6 +871,9 @@ static int __init hwicap_module_init(void)
failed:
unregister_chrdev_region(devt, HWICAP_DEVICES);
+ failed_class:
+ class_unregister(&icap_class);
+
return retval;
}
diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c
index 3786a0153ad1..40663ef3ef0a 100644
--- a/drivers/clk/clk-moxart.c
+++ b/drivers/clk/clk-moxart.c
@@ -17,7 +17,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node)
{
void __iomem *base;
struct clk_hw *hw;
- struct clk *ref_clk;
unsigned int mul;
const char *name = node->name;
const char *parent_name;
@@ -34,12 +33,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node)
mul = readl(base + 0x30) >> 3 & 0x3f;
iounmap(base);
- ref_clk = of_clk_get(node, 0);
- if (IS_ERR(ref_clk)) {
- pr_err("%pOF: of_clk_get failed\n", node);
- return;
- }
-
hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
if (IS_ERR(hw)) {
pr_err("%pOF: failed to register clock\n", node);
@@ -56,7 +49,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node)
{
void __iomem *base;
struct clk_hw *hw;
- struct clk *pll_clk;
unsigned int div, val;
unsigned int div_idx[] = { 2, 3, 4, 6, 8};
const char *name = node->name;
@@ -78,12 +70,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node)
val = 0;
div = div_idx[val] * 2;
- pll_clk = of_clk_get(node, 0);
- if (IS_ERR(pll_clk)) {
- pr_err("%pOF: of_clk_get failed\n", node);
- return;
- }
-
hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
if (IS_ERR(hw)) {
pr_err("%pOF: failed to register clock\n", node);
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
index 5efb10776ae5..42cfb8718ff0 100644
--- a/drivers/clk/clk-palmas.c
+++ b/drivers/clk/clk-palmas.c
@@ -194,6 +194,13 @@ static void palmas_clks_get_clk_data(struct platform_device *pdev,
cinfo->ext_control_pin = prop;
}
+static void palmas_clks_unprepare_ext_control(void *data)
+{
+ struct palmas_clock_info *cinfo = data;
+
+ clk_unprepare(cinfo->hw.clk);
+}
+
static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
{
int ret;
@@ -214,13 +221,18 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
return ret;
}
+ ret = devm_add_action_or_reset(cinfo->dev,
+ palmas_clks_unprepare_ext_control,
+ cinfo);
+ if (ret)
+ return ret;
+
ret = palmas_ext_control_req_config(cinfo->palmas,
cinfo->clk_desc->sleep_reqstr_id,
cinfo->ext_control_pin, true);
if (ret < 0) {
dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
cinfo->clk_desc->clk_name, ret);
- clk_unprepare(cinfo->hw.clk);
return ret;
}
}
diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c
index 9ab35c1af0ff..7db12918b84d 100644
--- a/drivers/clk/clk-versaclock7.c
+++ b/drivers/clk/clk-versaclock7.c
@@ -1191,7 +1191,7 @@ static int vc7_probe(struct i2c_client *client)
if (ret) {
dev_err_probe(&client->dev, ret,
"unable to register output %d\n", i);
- return ret;
+ goto err_clk;
}
switch (bank_src_map.type) {
diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c
index 93cee17db8b1..c3b7daac9313 100644
--- a/drivers/clk/hisilicon/reset.c
+++ b/drivers/clk/hisilicon/reset.c
@@ -91,7 +91,7 @@ struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev)
{
struct hisi_reset_controller *rstc;
- rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
+ rstc = devm_kzalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
if (!rstc)
return NULL;
diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c
index 3a68576cdae7..0156cd38b6fb 100644
--- a/drivers/clk/imx/clk-scu.c
+++ b/drivers/clk/imx/clk-scu.c
@@ -481,7 +481,6 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
clk->clk_type = clk_type;
init.name = name;
- init.ops = &clk_scu_ops;
if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72)
init.ops = &clk_scu_cpu_ops;
else if (rsrc_id == IMX_SC_R_PI_0_PLL)
diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c
index 019af88d7f9c..9bc036ffcf8d 100644
--- a/drivers/clk/mediatek/clk-mt8135.c
+++ b/drivers/clk/mediatek/clk-mt8135.c
@@ -409,6 +409,9 @@ static const struct mtk_gate_regs infra_cg_regs = {
GATE_MTK_FLAGS(_id, _name, _parent, &infra_cg_regs, _shift, \
&mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL)
+#define GATE_ICG_INV(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
static const struct mtk_gate infra_clks[] = {
GATE_DUMMY(CLK_DUMMY, "infra_dummy"),
GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23),
@@ -419,7 +422,7 @@ static const struct mtk_gate infra_clks[] = {
GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15),
GATE_ICG_AO(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7),
- GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6),
+ GATE_ICG_INV(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6),
GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5),
GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2),
GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1),
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 00eaca92b388..a6b46ad04794 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -1305,7 +1305,7 @@ static struct clk_regmap gxbb_32k_clk_sel = {
.name = "32k_clk_sel",
.ops = &clk_regmap_mux_ops,
.parent_data = gxbb_32k_clk_parent_data,
- .num_parents = 4,
+ .num_parents = ARRAY_SIZE(gxbb_32k_clk_parent_data),
.flags = CLK_SET_RATE_PARENT,
},
};
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
index ff3fb8b87c24..c7c0dc91a012 100644
--- a/drivers/clk/nuvoton/clk-ma35d1-pll.c
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -48,7 +48,7 @@
#define PLL_CTL1_PD BIT(0)
#define PLL_CTL1_BP BIT(1)
#define PLL_CTL1_OUTDIV GENMASK(6, 4)
-#define PLL_CTL1_FRAC GENMASK(31, 24)
+#define PLL_CTL1_FRAC GENMASK(31, 8)
#define PLL_CTL2_SLOPE GENMASK(23, 0)
#define INDIV_MIN 1
@@ -92,7 +92,7 @@ static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
outdiv = 1 << p;
pll_freq = (u64)parent_rate * n;
- div_u64(pll_freq, m * outdiv);
+ pll_freq = div_u64(pll_freq, m * outdiv);
return pll_freq;
}
@@ -110,12 +110,12 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p
if (mode == PLL_MODE_INT) {
pll_freq = (u64)parent_rate * n;
- div_u64(pll_freq, m * p);
+ pll_freq = div_u64(pll_freq, m * p);
} else {
x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
- /* 2 decimal places floating to integer (ex. 1.23 to 123) */
- n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
- pll_freq = div_u64(parent_rate * n, 100 * m * p);
+ /* convert 24-bit fraction to 3 decimal digits, rounding to closest */
+ n = n * 1000 + DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24);
+ pll_freq = div_u64((u64)parent_rate * n, 1000 * m * p);
}
return pll_freq;
}
@@ -244,35 +244,43 @@ static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw, unsigned long
return 0;
}
-static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
u32 reg_ctl[3] = { 0 };
unsigned long pll_freq;
long ret;
- if (*parent_rate < PLL_FREF_MIN_FREQ || *parent_rate > PLL_FREF_MAX_FREQ)
+ if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ)
return -EINVAL;
- ret = ma35d1_pll_find_closest(pll, rate, *parent_rate, reg_ctl, &pll_freq);
- if (ret < 0)
- return ret;
-
switch (pll->id) {
case CAPLL:
- reg_ctl[0] = readl_relaxed(pll->ctl0_base);
- pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], *parent_rate);
- return pll_freq;
case DDRPLL:
+ /* Read-only PLLs: return current rate */
+ reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+ if (pll->id == CAPLL) {
+ pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
+ } else {
+ reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+ pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
+ }
+ req->rate = pll_freq;
+ return 0;
case APLL:
case EPLL:
case VPLL:
- reg_ctl[0] = readl_relaxed(pll->ctl0_base);
- reg_ctl[1] = readl_relaxed(pll->ctl1_base);
- pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, *parent_rate);
- return pll_freq;
+ /* Configurable PLLs: find closest achievable rate */
+ ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
+ reg_ctl, &pll_freq);
+ if (ret < 0)
+ return ret;
+ req->rate = pll_freq;
+ return 0;
}
+
+ req->rate = 0;
return 0;
}
@@ -311,12 +319,12 @@ static const struct clk_ops ma35d1_clk_pll_ops = {
.unprepare = ma35d1_clk_pll_unprepare,
.set_rate = ma35d1_clk_pll_set_rate,
.recalc_rate = ma35d1_clk_pll_recalc_rate,
- .round_rate = ma35d1_clk_pll_round_rate,
+ .determine_rate = ma35d1_clk_pll_determine_rate,
};
static const struct clk_ops ma35d1_clk_fixed_pll_ops = {
.recalc_rate = ma35d1_clk_pll_recalc_rate,
- .round_rate = ma35d1_clk_pll_round_rate,
+ .determine_rate = ma35d1_clk_pll_determine_rate,
};
struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 3d6e87872ddc..aa5cca4f34cf 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -146,7 +146,7 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
if (!node) {
fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
if (!fixed)
- return -EINVAL;
+ return -ENOMEM;
fixed->fixed_rate = rate;
fixed->hw.init = &init_data;
@@ -163,7 +163,7 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
if (add_factor) {
factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
if (!factor)
- return -EINVAL;
+ return -ENOMEM;
factor->mult = factor->div = 1;
factor->hw.init = &init_data;
diff --git a/drivers/clk/qcom/gcc-mdm9607.c b/drivers/clk/qcom/gcc-mdm9607.c
index 9169cdccf3ed..d58ec1d25d94 100644
--- a/drivers/clk/qcom/gcc-mdm9607.c
+++ b/drivers/clk/qcom/gcc-mdm9607.c
@@ -26,7 +26,6 @@
enum {
P_XO,
- P_BIMC,
P_GPLL0,
P_GPLL1,
P_GPLL2,
@@ -121,7 +120,7 @@ static struct clk_alpha_pll gpll2_early = {
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
.clkr = {
.enable_reg = 0x45000,
- .enable_mask = BIT(3), /* Yeah, apparently it's not 2 */
+ .enable_mask = BIT(3), /* BIT(2) is used for BIMC PLL */
.hw.init = &(struct clk_init_data)
{
.name = "gpll2_early",
@@ -158,20 +157,6 @@ static const struct clk_parent_data gcc_xo_gpll0_gpll2[] = {
{ .hw = &gpll2.clkr.hw },
};
-static const struct parent_map gcc_xo_gpll0_gpll1_gpll2_map[] = {
- { P_XO, 0 },
- { P_GPLL0, 1 },
- { P_GPLL1, 2 },
- { P_GPLL2, 3 },
-};
-
-static const struct clk_parent_data gcc_xo_gpll0_gpll1_gpll2[] = {
- { .fw_name = "xo" },
- { .hw = &gpll0.clkr.hw },
- { .hw = &gpll1_vote.hw },
- { .hw = &gpll2.clkr.hw },
-};
-
static const struct freq_tbl ftbl_apss_ahb_clk[] = {
F(19200000, P_XO, 1, 0, 0),
F(50000000, P_GPLL0, 16, 0, 0),
@@ -192,80 +177,6 @@ static struct clk_rcg2 apss_ahb_clk_src = {
},
};
-static struct clk_pll bimc_pll = {
- .l_reg = 0x23004,
- .m_reg = 0x23008,
- .n_reg = 0x2300c,
- .config_reg = 0x23010,
- .mode_reg = 0x23000,
- .status_reg = 0x2301c,
- .status_bit = 17,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "bimc_pll",
- .parent_data = &(const struct clk_parent_data){
- .fw_name = "xo",
- },
- .num_parents = 1,
- .ops = &clk_pll_ops,
- },
-};
-
-static struct clk_regmap bimc_pll_vote = {
- .enable_reg = 0x45000,
- .enable_mask = BIT(3),
- .hw.init = &(struct clk_init_data){
- .name = "bimc_pll_vote",
- .parent_hws = (const struct clk_hw *[]){ &bimc_pll.clkr.hw },
- .num_parents = 1,
- .ops = &clk_pll_vote_ops,
- },
-};
-
-static const struct parent_map gcc_xo_gpll0_bimc_map[] = {
- { P_XO, 0 },
- { P_GPLL0, 1 },
- { P_BIMC, 2 },
-};
-
-static const struct clk_parent_data gcc_xo_gpll0_bimc[] = {
- { .fw_name = "xo" },
- { .hw = &gpll0.clkr.hw },
- { .hw = &bimc_pll_vote.hw },
-};
-
-static const struct freq_tbl ftbl_pcnoc_bfdcd_clk_src[] = {
- F(19200000, P_XO, 1, 0, 0),
- F(50000000, P_GPLL0, 16, 0, 0),
- F(100000000, P_GPLL0, 8, 0, 0),
- { }
-};
-
-static struct clk_rcg2 pcnoc_bfdcd_clk_src = {
- .cmd_rcgr = 0x27000,
- .freq_tbl = ftbl_pcnoc_bfdcd_clk_src,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_bimc_map,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "pcnoc_bfdcd_clk_src",
- .parent_data = gcc_xo_gpll0_bimc,
- .num_parents = ARRAY_SIZE(gcc_xo_gpll0_bimc),
- .ops = &clk_rcg2_ops,
- .flags = CLK_IS_CRITICAL,
- },
-};
-
-static struct clk_rcg2 system_noc_bfdcd_clk_src = {
- .cmd_rcgr = 0x26004,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_bimc_map,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "system_noc_bfdcd_clk_src",
- .parent_data = gcc_xo_gpll0_bimc,
- .num_parents = ARRAY_SIZE(gcc_xo_gpll0_bimc),
- .ops = &clk_rcg2_ops,
- },
-};
-
static const struct freq_tbl ftbl_gcc_blsp1_qup1_6_i2c_apps_clk[] = {
F(19200000, P_XO, 1, 0, 0),
F(50000000, P_GPLL0, 16, 0, 0),
@@ -674,26 +585,6 @@ static struct clk_rcg2 sdcc2_apps_clk_src = {
},
};
-static const struct freq_tbl ftbl_gcc_apss_tcu_clk[] = {
- F(155000000, P_GPLL2, 6, 0, 0),
- F(310000000, P_GPLL2, 3, 0, 0),
- F(400000000, P_GPLL0, 2, 0, 0),
- { }
-};
-
-static struct clk_rcg2 apss_tcu_clk_src = {
- .cmd_rcgr = 0x1207c,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_gpll1_gpll2_map,
- .freq_tbl = ftbl_gcc_apss_tcu_clk,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "apss_tcu_clk_src",
- .parent_data = gcc_xo_gpll0_gpll1_gpll2,
- .num_parents = 4,
- .ops = &clk_rcg2_ops,
- },
-};
-
static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
F(19200000, P_XO, 1, 0, 0),
F(57140000, P_GPLL0, 14, 0, 0),
@@ -781,8 +672,6 @@ static struct clk_branch gcc_blsp1_ahb_clk = {
.enable_mask = BIT(10),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -790,9 +679,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = {
static struct clk_branch gcc_blsp1_sleep_clk = {
.halt_reg = 0x1004,
+ .halt_check = BRANCH_HALT_VOTED,
.clkr = {
- .enable_reg = 0x1004,
- .enable_mask = BIT(0),
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_sleep_clk",
.parent_data = &(const struct clk_parent_data){
@@ -1083,8 +973,6 @@ static struct clk_branch gcc_boot_rom_ahb_clk = {
.enable_mask = BIT(7),
.hw.init = &(struct clk_init_data){
.name = "gcc_boot_rom_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -1098,9 +986,6 @@ static struct clk_branch gcc_crypto_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_crypto_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1114,9 +999,6 @@ static struct clk_branch gcc_crypto_axi_clk = {
.enable_mask = BIT(1),
.hw.init = &(struct clk_init_data){
.name = "gcc_crypto_axi_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1190,9 +1072,6 @@ static struct clk_branch gcc_mss_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_mss_cfg_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1220,9 +1099,6 @@ static struct clk_branch gcc_pdm_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pdm_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1236,9 +1112,6 @@ static struct clk_branch gcc_prng_ahb_clk = {
.enable_mask = BIT(8),
.hw.init = &(struct clk_init_data){
.name = "gcc_prng_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1251,9 +1124,6 @@ static struct clk_branch gcc_sdcc1_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc1_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1281,9 +1151,6 @@ static struct clk_branch gcc_sdcc2_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc2_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1304,19 +1171,6 @@ static struct clk_branch gcc_sdcc2_apps_clk = {
},
};
-static struct clk_rcg2 bimc_ddr_clk_src = {
- .cmd_rcgr = 0x32004,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_bimc_map,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "bimc_ddr_clk_src",
- .parent_data = gcc_xo_gpll0_bimc,
- .num_parents = 3,
- .ops = &clk_rcg2_ops,
- .flags = CLK_GET_RATE_NOCACHE,
- },
-};
-
static struct clk_branch gcc_mss_q6_bimc_axi_clk = {
.halt_reg = 0x49004,
.clkr = {
@@ -1324,9 +1178,6 @@ static struct clk_branch gcc_mss_q6_bimc_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_mss_q6_bimc_axi_clk",
- .parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1340,8 +1191,6 @@ static struct clk_branch gcc_apss_tcu_clk = {
.enable_mask = BIT(1),
.hw.init = &(struct clk_init_data){
.name = "gcc_apss_tcu_clk",
- .parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -1355,9 +1204,6 @@ static struct clk_branch gcc_smmu_cfg_clk = {
.enable_mask = BIT(12),
.hw.init = &(struct clk_init_data){
.name = "gcc_smmu_cfg_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1405,9 +1251,6 @@ static struct clk_branch gcc_usb_hs_phy_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb_hs_phy_cfg_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1420,9 +1263,6 @@ static struct clk_branch gcc_usb_hs_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb_hs_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1451,23 +1291,19 @@ static struct clk_branch gcc_apss_ahb_clk = {
.enable_mask = BIT(14),
.hw.init = &(struct clk_init_data){
.name = "gcc_apss_ahb_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
};
static struct clk_branch gcc_apss_axi_clk = {
- .halt_reg = 0x4601c,
+ .halt_reg = 0x46020,
.halt_check = BRANCH_HALT_VOTED,
.clkr = {
.enable_reg = 0x45004,
.enable_mask = BIT(13),
.hw.init = &(struct clk_init_data){
.name = "gcc_apss_axi_clk",
- .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -1480,11 +1316,6 @@ static struct clk_regmap *gcc_mdm9607_clocks[] = {
[GPLL1_VOTE] = &gpll1_vote,
[GPLL2] = &gpll2.clkr,
[GPLL2_EARLY] = &gpll2_early.clkr,
- [BIMC_PLL] = &bimc_pll.clkr,
- [BIMC_PLL_VOTE] = &bimc_pll_vote,
- [BIMC_DDR_CLK_SRC] = &bimc_ddr_clk_src.clkr,
- [PCNOC_BFDCD_CLK_SRC] = &pcnoc_bfdcd_clk_src.clkr,
- [SYSTEM_NOC_BFDCD_CLK_SRC] = &system_noc_bfdcd_clk_src.clkr,
[APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr,
[BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr,
[BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr,
@@ -1511,7 +1342,6 @@ static struct clk_regmap *gcc_mdm9607_clocks[] = {
[PDM2_CLK_SRC] = &pdm2_clk_src.clkr,
[SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr,
[SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr,
- [APSS_TCU_CLK_SRC] = &apss_tcu_clk_src.clkr,
[USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr,
[GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr,
[GCC_BLSP1_SLEEP_CLK] = &gcc_blsp1_sleep_clk.clkr,
@@ -1567,7 +1397,7 @@ static const struct qcom_reset_map gcc_mdm9607_resets[] = {
[USB_HS_HSIC_BCR] = { 0x3d05c },
[GCC_MSS_RESTART] = { 0x3e000 },
[USB_HS_BCR] = { 0x41000 },
- [USB2_HS_PHY_ONLY_BCR] = { 0x41034 },
+ [USB2_HS_PHY_ONLY_BCR] = { .reg = 0x41034, .udelay = 15 },
[QUSB2_PHY_BCR] = { 0x4103c },
};
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
index 9c7c6b23ce32..e91406e6f527 100644
--- a/drivers/clk/qcom/gcc-msm8916.c
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -1589,9 +1589,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = {
static struct clk_branch gcc_blsp1_sleep_clk = {
.halt_reg = 0x01004,
+ .halt_check = BRANCH_HALT_VOTED,
.clkr = {
- .enable_reg = 0x01004,
- .enable_mask = BIT(0),
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_sleep_clk",
.parent_data = &(const struct clk_parent_data){
diff --git a/drivers/clk/qcom/gcc-msm8939.c b/drivers/clk/qcom/gcc-msm8939.c
index e4a44377b75f..badba729b286 100644
--- a/drivers/clk/qcom/gcc-msm8939.c
+++ b/drivers/clk/qcom/gcc-msm8939.c
@@ -1916,9 +1916,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = {
static struct clk_branch gcc_blsp1_sleep_clk = {
.halt_reg = 0x01004,
+ .halt_check = BRANCH_HALT_VOTED,
.clkr = {
- .enable_reg = 0x01004,
- .enable_mask = BIT(0),
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_sleep_clk",
.ops = &clk_branch2_ops,
diff --git a/drivers/clk/qcom/gcc-qcm2290.c b/drivers/clk/qcom/gcc-qcm2290.c
index 48995e50c6bd..7b9c978f9233 100644
--- a/drivers/clk/qcom/gcc-qcm2290.c
+++ b/drivers/clk/qcom/gcc-qcm2290.c
@@ -1082,7 +1082,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = {
.name = "gcc_qupv3_wrap0_s0_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = {
@@ -1098,7 +1098,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = {
.name = "gcc_qupv3_wrap0_s1_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = {
@@ -1114,7 +1114,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = {
.name = "gcc_qupv3_wrap0_s2_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = {
@@ -1130,7 +1130,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = {
.name = "gcc_qupv3_wrap0_s3_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = {
@@ -1146,7 +1146,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = {
.name = "gcc_qupv3_wrap0_s4_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = {
@@ -1162,7 +1162,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = {
.name = "gcc_qupv3_wrap0_s5_clk_src",
.parent_data = gcc_parents_1,
.num_parents = ARRAY_SIZE(gcc_parents_1),
- .ops = &clk_rcg2_shared_ops,
+ .ops = &clk_rcg2_shared_no_init_park_ops,
};
static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = {
diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index 5a8c93b12efa..e564bde202e2 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -103,14 +103,21 @@ static int gdsc_hwctrl(struct gdsc *sc, bool en)
static int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status)
{
ktime_t start;
+ int ret;
start = ktime_get();
do {
- if (gdsc_check_status(sc, status))
+ ret = gdsc_check_status(sc, status);
+ if (ret < 0)
+ return ret;
+ if (ret)
return 0;
} while (ktime_us_delta(ktime_get(), start) < STATUS_POLL_TIMEOUT_US);
- if (gdsc_check_status(sc, status))
+ ret = gdsc_check_status(sc, status);
+ if (ret < 0)
+ return ret;
+ if (ret)
return 0;
return -ETIMEDOUT;
@@ -437,7 +444,9 @@ static int gdsc_init(struct gdsc *sc)
} else if (sc->flags & ALWAYS_ON) {
/* If ALWAYS_ON GDSCs are not ON, turn them ON */
- gdsc_enable(&sc->pd);
+ ret = gdsc_enable(&sc->pd);
+ if (ret)
+ return ret;
on = true;
}
@@ -545,10 +554,18 @@ int gdsc_register(struct gdsc_desc *desc,
void gdsc_unregister(struct gdsc_desc *desc)
{
struct device *dev = desc->dev;
+ struct gdsc **scs = desc->scs;
size_t num = desc->num;
+ int i;
- gdsc_pm_subdomain_remove(desc, num);
of_genpd_del_provider(dev->of_node);
+ gdsc_pm_subdomain_remove(desc, num);
+
+ for (i = 0; i < num; i++) {
+ if (!scs[i])
+ continue;
+ pm_genpd_remove(&scs[i]->pd);
+ }
}
/*
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index 2d42eb628926..6c7890c15004 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -893,6 +893,13 @@ static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT) & RK3588_PLLCON2_K_MASK);
}
+/*
+ * 2250 MHz <= Fvco <= 4500 MHz
+ * For Fvco > 3 GHz: period jitter +-1% frac PLL, +-0.75% int PLL
+ * For Fvco < 3 GHz: period jitter +-2% frac PLL, +-1.50% int PLL
+ * Fvco = ((m + k / 65536) * Fin) / p
+ * Fout = ((m + k / 65536) * Fin) / (p * 2^s)
+ */
static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
@@ -908,7 +915,7 @@ static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned
/* fractional mode */
u64 frac_rate64 = prate * cur.k;
- postdiv = cur.p * 65535;
+ postdiv = cur.p * 65536;
do_div(frac_rate64, postdiv);
rate64 += frac_rate64;
}
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
index d8ffcaefa480..82fc02ac45a6 100644
--- a/drivers/clk/rockchip/clk-rk3588.c
+++ b/drivers/clk/rockchip/clk-rk3588.c
@@ -2117,7 +2117,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
COMPOSITE(DCLK_VOP1_SRC, "dclk_vop1_src", gpll_cpll_v0pll_aupll_p, 0,
RK3588_CLKSEL_CON(111), 14, 2, MFLAGS, 9, 5, DFLAGS,
RK3588_CLKGATE_CON(52), 11, GFLAGS),
- COMPOSITE(DCLK_VOP2_SRC, "dclk_vop2_src", gpll_cpll_v0pll_aupll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ COMPOSITE(DCLK_VOP2_SRC, "dclk_vop2_src", gpll_cpll_v0pll_aupll_p, CLK_SET_RATE_NO_REPARENT,
RK3588_CLKSEL_CON(112), 5, 2, MFLAGS, 0, 5, DFLAGS,
RK3588_CLKGATE_CON(52), 12, GFLAGS),
COMPOSITE_NODIV(DCLK_VOP0, "dclk_vop0", dclk_vop0_p,
diff --git a/drivers/clk/tegra/clk-tegra124-emc.c b/drivers/clk/tegra/clk-tegra124-emc.c
index 5f1af6dfe715..674aef278539 100644
--- a/drivers/clk/tegra/clk-tegra124-emc.c
+++ b/drivers/clk/tegra/clk-tegra124-emc.c
@@ -539,6 +539,7 @@ struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np
clk = clk_register(NULL, &tegra->hw);
if (IS_ERR(clk)) {
+ of_node_put(tegra->emc_node);
kfree(tegra);
return clk;
}
diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c
index ff42ea75cb43..8b76c032f7d0 100644
--- a/drivers/clk/ti/adpll.c
+++ b/drivers/clk/ti/adpll.c
@@ -481,7 +481,7 @@ static const struct clk_ops ti_adpll_ops = {
static int ti_adpll_init_dco(struct ti_adpll_data *d)
{
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk *clock;
const char *postfix;
int width, err;
@@ -574,7 +574,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d,
struct clk *clk1)
{
struct ti_adpll_clkout_data *co;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_ops *ops;
const char *parent_names[2];
const char *child_name;
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index 5d5bb123ba94..262764df5466 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -309,7 +309,7 @@ static struct clk *_register_divider(struct device_node *node,
u32 flags,
struct clk_omap_divider *div)
{
- struct clk_init_data init;
+ struct clk_init_data init = {};
const char *parent_name;
const char *name;
diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c
index 1ebafa386be6..0c81ef9e56a6 100644
--- a/drivers/clk/ti/mux.c
+++ b/drivers/clk/ti/mux.c
@@ -119,14 +119,14 @@ const struct clk_ops ti_clk_mux_ops = {
};
static struct clk *_register_mux(struct device_node *node, const char *name,
- const char * const *parent_names,
+ const struct clk_parent_data *parent_data,
u8 num_parents, unsigned long flags,
struct clk_omap_reg *reg, u8 shift, u32 mask,
s8 latch, u8 clk_mux_flags, u32 *table)
{
+ struct clk_init_data init = {};
struct clk_omap_mux *mux;
struct clk *clk;
- struct clk_init_data init;
/* allocate the mux */
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
@@ -136,7 +136,7 @@ static struct clk *_register_mux(struct device_node *node, const char *name,
init.name = name;
init.ops = &ti_clk_mux_ops;
init.flags = flags;
- init.parent_names = parent_names;
+ init.parent_data = parent_data;
init.num_parents = num_parents;
/* struct clk_mux assignments */
@@ -167,24 +167,26 @@ static void of_mux_clk_setup(struct device_node *node)
struct clk *clk;
struct clk_omap_reg reg;
unsigned int num_parents;
- const char **parent_names;
+ struct clk_parent_data *parent_data;
const char *name;
u8 clk_mux_flags = 0;
u32 mask = 0;
u32 shift = 0;
s32 latch = -EINVAL;
u32 flags = CLK_SET_RATE_NO_REPARENT;
+ int i;
num_parents = of_clk_get_parent_count(node);
if (num_parents < 2) {
pr_err("mux-clock %pOFn must have parents\n", node);
return;
}
- parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL);
- if (!parent_names)
- goto cleanup;
+ parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL);
+ if (!parent_data)
+ return;
- of_clk_parent_fill(node, parent_names, num_parents);
+ for (i = 0; i < num_parents; i++)
+ parent_data[i].index = i;
if (ti_clk_get_reg_addr(node, 0, ®))
goto cleanup;
@@ -207,7 +209,7 @@ static void of_mux_clk_setup(struct device_node *node)
mask = (1 << fls(mask)) - 1;
name = ti_dt_clk_name(node);
- clk = _register_mux(node, name, parent_names, num_parents,
+ clk = _register_mux(node, name, parent_data, num_parents,
flags, ®, shift, mask, latch, clk_mux_flags,
NULL);
@@ -215,7 +217,7 @@ static void of_mux_clk_setup(struct device_node *node)
of_clk_add_provider(node, of_clk_src_simple_get, clk);
cleanup:
- kfree(parent_names);
+ kfree(parent_data);
}
CLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup);
diff --git a/drivers/clk/visconti/clkc.c b/drivers/clk/visconti/clkc.c
index d0b193b5d0b3..4018d1298880 100644
--- a/drivers/clk/visconti/clkc.c
+++ b/drivers/clk/visconti/clkc.c
@@ -81,9 +81,9 @@ static struct clk_hw *visconti_clk_register_gate(struct device *dev,
u8 rs_idx,
spinlock_t *lock)
{
+ struct clk_init_data init = {};
struct visconti_clk_gate *gate;
struct clk_parent_data *pdata;
- struct clk_init_data init;
struct clk_hw *hw;
int ret;
diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c
index 2974dd0ec6f4..4dc6aa202701 100644
--- a/drivers/clk/x86/clk-pmc-atom.c
+++ b/drivers/clk/x86/clk-pmc-atom.c
@@ -171,6 +171,9 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
return ERR_PTR(-ENOMEM);
init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
+ if (!init.name)
+ return ERR_PTR(-ENOMEM);
+
init.ops = &plt_clk_ops;
init.flags = 0;
init.parent_names = parent_names;
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index bbceb0289d45..95f8a50ba1eb 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -94,7 +94,7 @@ static int __init clps711x_timer_init(struct device_node *np)
switch (of_alias_get_id(np, "timer")) {
case CLPS711X_CLKSRC_CLOCKSOURCE:
clps711x_clksrc_init(clock, base);
- break;
+ return 0;
case CLPS711X_CLKSRC_CLOCKEVENT:
ret = _clps711x_clkevt_init(clock, base, irq);
break;
diff --git a/drivers/clocksource/timer-armada-370-xp.c b/drivers/clocksource/timer-armada-370-xp.c
index 6ec565d6939a..49735ced52bb 100644
--- a/drivers/clocksource/timer-armada-370-xp.c
+++ b/drivers/clocksource/timer-armada-370-xp.c
@@ -346,7 +346,11 @@ static int __init armada_xp_timer_init(struct device_node *np)
timer_clk = clk_get_rate(clk);
- return armada_370_xp_timer_common_init(np);
+ ret = armada_370_xp_timer_common_init(np);
+ if (ret)
+ clk_disable_unprepare(clk);
+
+ return ret;
}
TIMER_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
armada_xp_timer_init);
@@ -384,7 +388,11 @@ static int __init armada_375_timer_init(struct device_node *np)
timer25Mhz = false;
}
- return armada_370_xp_timer_common_init(np);
+ ret = armada_370_xp_timer_common_init(np);
+ if (ret)
+ clk_disable_unprepare(clk);
+
+ return ret;
}
TIMER_OF_DECLARE(armada_375, "marvell,armada-375-timer",
armada_375_timer_init);
@@ -407,7 +415,11 @@ static int __init armada_370_timer_init(struct device_node *np)
timer_clk = clk_get_rate(clk) / TIMER_DIVIDER;
timer25Mhz = false;
- return armada_370_xp_timer_common_init(np);
+ ret = armada_370_xp_timer_common_init(np);
+ if (ret)
+ clk_disable_unprepare(clk);
+
+ return ret;
}
TIMER_OF_DECLARE(armada_370, "marvell,armada-370-timer",
armada_370_timer_init);
diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c
index 7bdcc60ad43c..c2d04ab7cf2d 100644
--- a/drivers/clocksource/timer-sun4i.c
+++ b/drivers/clocksource/timer-sun4i.c
@@ -208,7 +208,7 @@ static int __init sun4i_timer_init(struct device_node *node)
sun4i_timer_clear_interrupt(timer_of_base(&to));
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
- TIMER_SYNC_TICKS, 0xffffffff);
+ TIMER_SYNC_TICKS + 1, 0xffffffff);
/* Enable timer0 interrupt */
val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG);
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index 33728c242f66..6626454b0cf7 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -53,7 +53,6 @@ static unsigned int max_freq;
static unsigned int transition_latency;
static u32 *imx6_soc_volt;
-static u32 soc_opp_count;
static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
{
@@ -346,6 +345,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
const struct property *prop;
const __be32 *val;
u32 nr, i, j;
+ u32 soc_opp_count = 0;
cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
@@ -416,7 +416,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
}
/* Make imx6_soc_volt array's size same as arm opp number */
- imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt),
+ imx6_soc_volt = devm_kcalloc(&pdev->dev, num, sizeof(*imx6_soc_volt),
GFP_KERNEL);
if (imx6_soc_volt == NULL) {
ret = -ENOMEM;
@@ -501,6 +501,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
return 0;
free_freq_table:
+ imx6_soc_volt = NULL;
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_opp:
dev_pm_opp_of_remove_table(cpu_dev);
@@ -522,6 +523,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
static void imx6q_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&imx6q_cpufreq_driver);
+ imx6_soc_volt = NULL;
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
dev_pm_opp_of_remove_table(cpu_dev);
regulator_put(arm_reg);
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 048e2579e4fc..df865612158a 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -2047,8 +2047,6 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
if (pstate_funcs.get_vid)
pstate_funcs.get_vid(cpu);
-
- intel_pstate_set_min_pstate(cpu);
}
/*
@@ -2746,6 +2744,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
{
int ret = __intel_pstate_cpu_init(policy);
+ struct cpudata *cpu;
if (ret)
return ret;
@@ -2756,11 +2755,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
*/
policy->policy = CPUFREQ_POLICY_POWERSAVE;
- if (hwp_active) {
- struct cpudata *cpu = all_cpu_data[policy->cpu];
-
+ cpu = all_cpu_data[policy->cpu];
+ if (hwp_active)
cpu->epp_cached = intel_pstate_get_epp(cpu, 0);
- }
+ else
+ intel_pstate_set_min_pstate(cpu);
return 0;
}
@@ -2983,8 +2982,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY;
- /* This reflects the intel_pstate_get_cpu_pstates() setting. */
- policy->cur = policy->cpuinfo.min_freq;
req = kcalloc(2, sizeof(*req), GFP_KERNEL);
if (!req) {
@@ -3005,9 +3002,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy)
WRITE_ONCE(cpu->hwp_req_cached, value);
cpu->epp_cached = intel_pstate_get_epp(cpu, value);
+
+ intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate,
+ cpu->pstate.max_pstate,
+ cpu->pstate.min_pstate, false);
} else {
policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY;
+ intel_pstate_set_min_pstate(cpu);
}
+ policy->cur = policy->cpuinfo.min_freq;
freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100);
diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c
index 78b875db6b66..c4d3ec2eafa8 100644
--- a/drivers/cpufreq/spear-cpufreq.c
+++ b/drivers/cpufreq/spear-cpufreq.c
@@ -79,9 +79,9 @@ static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq)
int ret = 0;
sys_clk = clk_get_parent(spear_cpufreq.clk);
- if (IS_ERR(sys_clk)) {
+ if (!sys_clk) {
pr_err("failed to get cpu's parent (sys) clock\n");
- return PTR_ERR(sys_clk);
+ return -EINVAL;
}
/* Set the rate of the source clock before changing the parent */
diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c
index 1af63c189039..03e3772cab3a 100644
--- a/drivers/cpuidle/dt_idle_genpd.c
+++ b/drivers/cpuidle/dt_idle_genpd.c
@@ -99,7 +99,7 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
if (!pd)
goto out;
- pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
+ pd->name = kstrdup(kbasename(of_node_full_name(np)), GFP_KERNEL);
if (!pd->name)
goto free_pd;
@@ -112,7 +112,6 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
goto free_name;
pd->free_states = pd_free_states;
- pd->name = kbasename(pd->name);
pd->states = states;
pd->state_count = state_count;
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 9da5a0388080..c64431380d30 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -27,15 +27,14 @@ static struct atmel_ecc_driver_data driver_data;
/**
* struct atmel_ecdh_ctx - transformation context
- * @client : pointer to i2c client device
- * @fallback : used for unsupported curves or when user wants to use its own
- * private key.
- * @public_key : generated when calling set_secret(). It's the responsibility
- * of the user to not call set_secret() while
- * generate_public_key() or compute_shared_secret() are in flight.
- * @curve_id : elliptic curve id
- * @do_fallback: true when the device doesn't support the curve or when the user
- * wants to use its own private key.
+ * @client: I2C client device
+ * @fallback: ECDH fallback used for caller-provided private keys
+ * @public_key: cached public key for the device-generated private key
+ * @curve_id: elliptic curve id
+ * @do_fallback: true when ECDH operations should use @fallback
+ *
+ * The caller must not invoke set_secret() while generate_public_key()
+ * or compute_shared_secret() are in flight.
*/
struct atmel_ecdh_ctx {
struct i2c_client *client;
@@ -55,8 +54,8 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq,
if (status)
goto free_work_data;
- /* might want less than we've got */
- n_sz = min_t(size_t, ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len);
+ /* copy only as much as requested, capped at 32 bytes */
+ n_sz = min(ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len);
/* copy the shared secret */
copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, n_sz),
@@ -64,15 +63,15 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq,
if (copied != n_sz)
status = -EINVAL;
- /* fall through */
free_work_data:
kfree_sensitive(work_data);
kpp_request_complete(req, status);
}
/*
- * A random private key is generated and stored in the device. The device
- * returns the pair public key.
+ * If no private key is provided, generate one in the device and cache
+ * the corresponding public key. The generated private key never leaves
+ * the device.
*/
static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
unsigned int len)
@@ -83,9 +82,7 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
struct ecdh params;
int ret = -ENOMEM;
- /* free the old public key, if any */
kfree(ctx->public_key);
- /* make sure you don't free the old public key twice */
ctx->public_key = NULL;
if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0) {
@@ -94,7 +91,6 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
}
if (params.key_size) {
- /* fallback to ecdh software implementation */
ctx->do_fallback = true;
return crypto_kpp_set_secret(ctx->fallback, buf, len);
}
@@ -103,11 +99,6 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
if (!cmd)
return -ENOMEM;
- /*
- * The device only supports NIST P256 ECC keys. The public key size will
- * always be the same. Use a macro for the key size to avoid unnecessary
- * computations.
- */
public_key = kmalloc(ATMEL_ECC_PUBKEY_SIZE, GFP_KERNEL);
if (!public_key)
goto free_cmd;
@@ -120,7 +111,6 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
if (ret)
goto free_public_key;
- /* save the public key */
memcpy(public_key, &cmd->data[RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE);
ctx->public_key = public_key;
@@ -149,8 +139,8 @@ static int atmel_ecdh_generate_public_key(struct kpp_request *req)
if (!ctx->public_key)
return -EINVAL;
- /* might want less than we've got */
- nbytes = min_t(size_t, ATMEL_ECC_PUBKEY_SIZE, req->dst_len);
+ /* copy only as much as requested, capped at 64 bytes */
+ nbytes = min(ATMEL_ECC_PUBKEY_SIZE, req->dst_len);
/* public key was saved at private key generation */
copied = sg_copy_from_buffer(req->dst,
@@ -175,7 +165,10 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)
return crypto_kpp_compute_shared_secret(req);
}
- /* must have exactly two points to be on the curve */
+ if (!ctx->public_key)
+ return -EINVAL;
+
+ /* A P-256 public key must contain two 32-byte coordinates */
if (req->src_len != ATMEL_ECC_PUBKEY_SIZE)
return -EINVAL;
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index a3f9887b928f..2bb46ba94cd4 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -30,10 +30,14 @@ static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
struct hwrng *rng = areq;
- if (status)
+ if (status) {
dev_warn_ratelimited(&i2c_priv->client->dev,
"i2c transaction failed (%d)\n",
status);
+ kfree(work_data);
+ atomic_dec(&i2c_priv->tfm_count);
+ return;
+ }
rng->priv = (unsigned long)work_data;
atomic_dec(&i2c_priv->tfm_count);
diff --git a/drivers/crypto/gemini/sl3516-ce-cipher.c b/drivers/crypto/gemini/sl3516-ce-cipher.c
index 49dce9e0a834..d1b4732e18e7 100644
--- a/drivers/crypto/gemini/sl3516-ce-cipher.c
+++ b/drivers/crypto/gemini/sl3516-ce-cipher.c
@@ -56,10 +56,6 @@ static bool sl3516_ce_need_fallback(struct skcipher_request *areq)
ce->fallback_mod16++;
return true;
}
- if ((sg_dma_len(sg) % 16) != 0) {
- ce->fallback_mod16++;
- return true;
- }
if (!IS_ALIGNED(sg->offset, 16)) {
ce->fallback_align16++;
return true;
@@ -72,10 +68,6 @@ static bool sl3516_ce_need_fallback(struct skcipher_request *areq)
ce->fallback_mod16++;
return true;
}
- if ((sg_dma_len(sg) % 16) != 0) {
- ce->fallback_mod16++;
- return true;
- }
if (!IS_ALIGNED(sg->offset, 16)) {
ce->fallback_align16++;
return true;
diff --git a/drivers/crypto/hisilicon/sec2/sec_crypto.c b/drivers/crypto/hisilicon/sec2/sec_crypto.c
index 8db3d2990816..2bfe3524c265 100644
--- a/drivers/crypto/hisilicon/sec2/sec_crypto.c
+++ b/drivers/crypto/hisilicon/sec2/sec_crypto.c
@@ -98,7 +98,6 @@
#define IV_FLAGS_OFFSET 0x6
#define IV_CM_OFFSET 0x3
#define IV_LAST_BYTE1 1
-#define IV_LAST_BYTE2 2
#define IV_LAST_BYTE_MASK 0xFF
#define IV_CTR_INIT 0x1
#define IV_BYTE_OFFSET 0x8
@@ -1469,7 +1468,7 @@ static void set_aead_auth_iv(struct sec_ctx *ctx, struct sec_req *req)
struct sec_cipher_req *c_req = &req->c_req;
u32 data_size = aead_req->cryptlen;
u8 flage = 0;
- u8 cm, cl;
+ u8 cm, cl, i;
/* the specification has been checked in aead_iv_demension_check() */
cl = c_req->c_ivin[0] + 1;
@@ -1493,15 +1492,16 @@ static void set_aead_auth_iv(struct sec_ctx *ctx, struct sec_req *req)
* the last 32bit is counter's initial number,
* but the nonce uses the first 16bit
* the tail 16bit fill with the cipher length
+ * When CL is 3, the tail 24bit fill with the cipher length.
*/
if (!c_req->encrypt)
data_size = aead_req->cryptlen - authsize;
- a_req->a_ivin[ctx->c_ctx.ivsize - IV_LAST_BYTE1] =
- data_size & IV_LAST_BYTE_MASK;
- data_size >>= IV_BYTE_OFFSET;
- a_req->a_ivin[ctx->c_ctx.ivsize - IV_LAST_BYTE2] =
+ for (i = 1; i <= cl; i++) {
+ a_req->a_ivin[ctx->c_ctx.ivsize - i] =
data_size & IV_LAST_BYTE_MASK;
+ data_size >>= IV_BYTE_OFFSET;
+ }
}
static void sec_aead_set_iv(struct sec_ctx *ctx, struct sec_req *req)
diff --git a/drivers/crypto/intel/keembay/keembay-ocs-aes-core.c b/drivers/crypto/intel/keembay/keembay-ocs-aes-core.c
index 1e2fd9a754ec..2003805f08da 100644
--- a/drivers/crypto/intel/keembay/keembay-ocs-aes-core.c
+++ b/drivers/crypto/intel/keembay/keembay-ocs-aes-core.c
@@ -1541,7 +1541,7 @@ static int register_aes_algs(struct ocs_aes_dev *aes_dev)
/*
* If any algorithm fails to register, all preceding algorithms that
- * were successfully registered will be automatically unregistered.
+ * were registered in the same call are automatically unregistered.
*/
ret = crypto_engine_register_aeads(algs_aead, ARRAY_SIZE(algs_aead));
if (ret)
@@ -1549,7 +1549,7 @@ static int register_aes_algs(struct ocs_aes_dev *aes_dev)
ret = crypto_engine_register_skciphers(algs, ARRAY_SIZE(algs));
if (ret)
- crypto_engine_unregister_aeads(algs_aead, ARRAY_SIZE(algs));
+ crypto_engine_unregister_aeads(algs_aead, ARRAY_SIZE(algs_aead));
return ret;
}
@@ -1561,6 +1561,7 @@ static const struct of_device_id kmb_ocs_aes_of_match[] = {
},
{}
};
+MODULE_DEVICE_TABLE(of, kmb_ocs_aes_of_match);
static int kmb_ocs_aes_remove(struct platform_device *pdev)
{
@@ -1604,6 +1605,8 @@ static int kmb_ocs_aes_probe(struct platform_device *pdev)
if (IS_ERR(aes_dev->base_reg))
return PTR_ERR(aes_dev->base_reg);
+ init_completion(&aes_dev->irq_completion);
+
/* Get and request IRQ */
aes_dev->irq = platform_get_irq(pdev, 0);
if (aes_dev->irq < 0)
@@ -1621,8 +1624,6 @@ static int kmb_ocs_aes_probe(struct platform_device *pdev)
list_add_tail(&aes_dev->list, &ocs_aes.dev_list);
spin_unlock(&ocs_aes.lock);
- init_completion(&aes_dev->irq_completion);
-
/* Initialize crypto engine */
aes_dev->engine = crypto_engine_alloc_init(dev, true);
if (!aes_dev->engine) {
diff --git a/drivers/crypto/intel/qat/qat_common/qat_algs.c b/drivers/crypto/intel/qat/qat_common/qat_algs.c
index d69cc1e5e023..274bcc83a1fc 100644
--- a/drivers/crypto/intel/qat/qat_common/qat_algs.c
+++ b/drivers/crypto/intel/qat/qat_common/qat_algs.c
@@ -481,6 +481,7 @@ static void qat_alg_xts_reverse_key(const u8 *key_forward, unsigned int keylen,
memcpy(key_reverse + AES_BLOCK_SIZE, key - AES_BLOCK_SIZE,
AES_BLOCK_SIZE);
}
+ memzero_explicit(&aes_expanded, sizeof(aes_expanded));
}
static void qat_alg_skcipher_init_dec(struct qat_alg_skcipher_ctx *ctx,
diff --git a/drivers/crypto/rockchip/rk3288_crypto_ahash.c b/drivers/crypto/rockchip/rk3288_crypto_ahash.c
index 8f3406289df4..da71dfb345df 100644
--- a/drivers/crypto/rockchip/rk3288_crypto_ahash.c
+++ b/drivers/crypto/rockchip/rk3288_crypto_ahash.c
@@ -322,7 +322,12 @@ static int rk_hash_run(struct crypto_engine *engine, void *breq)
* efficiency, and make it response quickly when dma
* complete.
*/
- readl_poll_timeout(rkc->reg + RK_CRYPTO_HASH_STS, v, v == 0, 10, 1000);
+ err = readl_poll_timeout(rkc->reg + RK_CRYPTO_HASH_STS, v,
+ v == 0, 10, 1000);
+ if (err) {
+ dev_err(rkc->dev, "HASH idle timeout\n");
+ goto theend;
+ }
for (i = 0; i < crypto_ahash_digestsize(tfm) / 4; i++) {
v = readl(rkc->reg + RK_CRYPTO_HASH_DOUT_0 + i * 4);
diff --git a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c
index af221d5d999f..f1a1eb9f7d07 100644
--- a/drivers/crypto/sa2ul.c
+++ b/drivers/crypto/sa2ul.c
@@ -2426,7 +2426,10 @@ static int sa_ul_probe(struct platform_device *pdev)
return ret;
}
- sa_init_mem(dev_data);
+ ret = sa_init_mem(dev_data);
+ if (ret)
+ goto disable_pm;
+
ret = sa_dma_init(dev_data);
if (ret)
goto destroy_dma_pool;
@@ -2461,6 +2464,7 @@ static int sa_ul_probe(struct platform_device *pdev)
destroy_dma_pool:
dma_pool_destroy(dev_data->sc_pool);
+disable_pm:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index fecaa18f4dd2..059e95e08620 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1333,6 +1333,11 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
if (rc)
break;
+ if (!le16_to_cpu(po->count)) {
+ dev_dbg(&cxlmd->dev, "Poison empty payload!\n");
+ break;
+ }
+
for (int i = 0; i < le16_to_cpu(po->count); i++)
trace_cxl_poison(cxlmd, cxlr, &po->record[i],
po->flags, po->overflow_ts,
@@ -1399,6 +1404,8 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev)
mutex_init(&mds->mbox_mutex);
mutex_init(&mds->event.log_lock);
mds->cxlds.dev = dev;
+ mds->cxlds.reg_map.host = dev;
+ mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE;
mds->cxlds.type = CXL_DEVTYPE_CLASSMEM;
return mds;
diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c
index fc94f5240327..317b2fbbef66 100644
--- a/drivers/cxl/core/pmem.c
+++ b/drivers/cxl/core/pmem.c
@@ -217,12 +217,14 @@ static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_nvdimm_bridge *cxl_nvb,
dev->bus = &cxl_bus_type;
dev->type = &cxl_nvdimm_type;
/*
- * A "%llx" string is 17-bytes vs dimm_id that is max
- * NVDIMM_KEY_DESC_LEN
+ * dev_id is the nvdimm dimm_id used for security key lookup.
+ * It must match id_show(), which emits the CXL serial as an
+ * unsigned decimal. A u64 decimal string is at most 20 digits
+ * plus NUL.
*/
- BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 17 ||
+ BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 21 ||
sizeof(cxl_nvd->dev_id) > NVDIMM_KEY_DESC_LEN);
- sprintf(cxl_nvd->dev_id, "%llx", cxlmd->cxlds->serial);
+ sprintf(cxl_nvd->dev_id, "%llu", cxlmd->cxlds->serial);
return cxl_nvd;
}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1728cae1e840..af4f8bbe63c4 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1651,14 +1651,13 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
break;
}
}
- put_device(dev);
-
if (rc)
dev_err(port->uport_dev,
"failed to find %s:%s in target list of %s\n",
dev_name(&port->dev),
- dev_name(port->parent_dport->dport_dev),
- dev_name(&cxlsd->cxld.dev));
+ dev_name(port->parent_dport->dport_dev), dev_name(dev));
+
+ put_device(dev);
return rc;
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index bcfcbf65d18a..a7a1b71626de 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -528,7 +528,8 @@ struct cxl_nvdimm_bridge {
struct nvdimm_bus_descriptor nd_desc;
};
-#define CXL_DEV_ID_LEN 19
+/* Holds a u64 serial as a decimal string: up to 20 digits + NUL */
+#define CXL_DEV_ID_LEN 21
struct cxl_nvdimm {
struct device dev;
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index edb46123e3eb..36c90eafc5fb 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -400,6 +400,7 @@ enum cxl_devtype {
*
* @dev: The device associated with this CXL state
* @cxlmd: The device representing the CXL.mem capabilities of @dev
+ * @reg_map: component and ras register mapping parameters
* @regs: Parsed register blocks
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
@@ -414,6 +415,7 @@ enum cxl_devtype {
struct cxl_dev_state {
struct device *dev;
struct cxl_memdev *cxlmd;
+ struct cxl_register_map reg_map;
struct cxl_regs regs;
int cxl_dvsec;
bool rcd;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index aacd93f9067d..3482f95e0b46 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -825,15 +825,19 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* still be useful for management functions so don't return an error.
*/
cxlds->component_reg_phys = CXL_RESOURCE_NONE;
- rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
- if (rc)
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
+ &cxlds->reg_map);
+ if (rc) {
+ if (rc == -EPROBE_DEFER)
+ return rc;
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
- else if (!map.component_map.ras.valid)
+ } else if (!cxlds->reg_map.component_map.ras.valid) {
dev_dbg(&pdev->dev, "RAS registers not found\n");
+ }
- cxlds->component_reg_phys = map.resource;
+ cxlds->component_reg_phys = cxlds->reg_map.resource;
- rc = cxl_map_component_regs(&map, &cxlds->regs.component,
+ rc = cxl_map_component_regs(&cxlds->reg_map, &cxlds->regs.component,
BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index 7cb8994f8809..a5140a25269e 100644
--- a/drivers/cxl/pmem.c
+++ b/drivers/cxl/pmem.c
@@ -40,7 +40,7 @@ static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_dev_state *cxlds = cxl_nvd->cxlmd->cxlds;
- return sysfs_emit(buf, "%lld\n", cxlds->serial);
+ return sysfs_emit(buf, "%llu\n", cxlds->serial);
}
static DEVICE_ATTR_RO(id);
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 0da9232ea175..b55a015e8576 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -232,6 +232,7 @@ EXPORT_SYMBOL_GPL(dax_recovery_write);
int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
u64 len, int mf_flags)
{
+ const struct dax_holder_operations *ops;
int rc, id;
id = dax_read_lock();
@@ -240,12 +241,19 @@ int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
goto out;
}
- if (!dax_dev->holder_ops) {
+ /*
+ * Read holder_ops once: a concurrent fs_put_dax() can clear it without
+ * synchronizing against readers. Without the single fetch the compiler
+ * could reload between the NULL check and the call and dereference a
+ * NULL ops.
+ */
+ ops = READ_ONCE(dax_dev->holder_ops);
+ if (!ops) {
rc = -EOPNOTSUPP;
goto out;
}
- rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
+ rc = ops->notify_failure(dax_dev, off, len, mf_flags);
out:
dax_read_unlock(id);
return rc;
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 9ae789d4aca7..1fd2dbc5d717 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/dmaengine.h>
@@ -212,6 +213,35 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
return 1;
}
+static void dw_edma_terminate_vdesc(struct virt_dma_desc *vd)
+{
+ list_del(&vd->node);
+ dma_cookie_complete(&vd->tx);
+ vchan_terminate_vdesc(vd);
+}
+
+static void dw_edma_terminate_vdesc_list(struct list_head *head)
+{
+ struct virt_dma_desc *vd, *_vd;
+
+ list_for_each_entry_safe(vd, _vd, head, node)
+ dw_edma_terminate_vdesc(vd);
+}
+
+/* Must be called with vc.lock held. */
+static void dw_edma_terminate_all_descs(struct dw_edma_chan *chan)
+{
+ /*
+ * This order must not be reversed. Cookies are assigned when
+ * descriptors are submitted, so desc_issued contains older cookies
+ * than desc_submitted. Completing desc_submitted first could move
+ * chan->vc.chan.completed_cookie backwards when desc_issued is
+ * terminated afterwards.
+ */
+ dw_edma_terminate_vdesc_list(&chan->vc.desc_issued);
+ dw_edma_terminate_vdesc_list(&chan->vc.desc_submitted);
+}
+
static void dw_edma_device_caps(struct dma_chan *dchan,
struct dma_slave_caps *caps)
{
@@ -246,6 +276,8 @@ static int dw_edma_device_pause(struct dma_chan *dchan)
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
int err = 0;
+ guard(spinlock_irqsave)(&chan->vc.lock);
+
if (!chan->configured)
err = -EPERM;
else if (chan->status != EDMA_ST_BUSY)
@@ -263,6 +295,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan)
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
int err = 0;
+ guard(spinlock_irqsave)(&chan->vc.lock);
+
if (!chan->configured) {
err = -EPERM;
} else if (chan->status != EDMA_ST_PAUSE) {
@@ -271,7 +305,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan)
err = -EPERM;
} else {
chan->status = EDMA_ST_BUSY;
- dw_edma_start_transfer(chan);
+ if (!dw_edma_start_transfer(chan))
+ chan->status = EDMA_ST_IDLE;
}
return err;
@@ -282,25 +317,29 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan)
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
int err = 0;
+ guard(spinlock_irqsave)(&chan->vc.lock);
+
if (!chan->configured) {
- /* Do nothing */
+ dw_edma_terminate_all_descs(chan);
} else if (chan->status == EDMA_ST_PAUSE) {
+ dw_edma_terminate_all_descs(chan);
chan->status = EDMA_ST_IDLE;
- chan->configured = false;
} else if (chan->status == EDMA_ST_IDLE) {
- chan->configured = false;
+ dw_edma_terminate_all_descs(chan);
} else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) {
/*
* The channel is in a false BUSY state, probably didn't
* receive or lost an interrupt
*/
+ dw_edma_terminate_all_descs(chan);
chan->status = EDMA_ST_IDLE;
- chan->configured = false;
} else if (chan->request > EDMA_REQ_PAUSE) {
err = -EPERM;
} else {
chan->request = EDMA_REQ_STOP;
}
+ if (chan->status == EDMA_ST_IDLE)
+ chan->request = EDMA_REQ_NONE;
return err;
}
@@ -310,11 +349,9 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
unsigned long flags;
- if (!chan->configured)
- return;
-
spin_lock_irqsave(&chan->vc.lock, flags);
- if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE &&
+ if (chan->configured && vchan_issue_pending(&chan->vc) &&
+ chan->request == EDMA_REQ_NONE &&
chan->status == EDMA_ST_IDLE) {
chan->status = EDMA_ST_BUSY;
dw_edma_start_transfer(chan);
@@ -621,10 +658,16 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
unsigned long flags;
spin_lock_irqsave(&chan->vc.lock, flags);
+ if (chan->status == EDMA_ST_PAUSE) {
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+ return;
+ }
+
vd = vchan_next_desc(&chan->vc);
if (vd) {
switch (chan->request) {
case EDMA_REQ_NONE:
+ case EDMA_REQ_PAUSE:
desc = vd2dw_edma_desc(vd);
if (!desc->chunks_alloc) {
dw_hdma_set_callback_result(vd,
@@ -633,23 +676,23 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
vchan_cookie_complete(vd);
}
+ if (chan->request == EDMA_REQ_PAUSE) {
+ chan->request = EDMA_REQ_NONE;
+ chan->status = EDMA_ST_PAUSE;
+ break;
+ }
+
/* Continue transferring if there are remaining chunks or issued requests.
*/
chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
break;
case EDMA_REQ_STOP:
- list_del(&vd->node);
- vchan_cookie_complete(vd);
+ dw_edma_terminate_all_descs(chan);
chan->request = EDMA_REQ_NONE;
chan->status = EDMA_ST_IDLE;
break;
- case EDMA_REQ_PAUSE:
- chan->request = EDMA_REQ_NONE;
- chan->status = EDMA_ST_PAUSE;
- break;
-
default:
break;
}
@@ -664,14 +707,16 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
spin_lock_irqsave(&chan->vc.lock, flags);
vd = vchan_next_desc(&chan->vc);
- if (vd) {
+ if (vd && chan->request == EDMA_REQ_STOP) {
+ dw_edma_terminate_all_descs(chan);
+ } else if (vd) {
dw_hdma_set_callback_result(vd, DMA_TRANS_ABORTED);
list_del(&vd->node);
vchan_cookie_complete(vd);
}
- spin_unlock_irqrestore(&chan->vc.lock, flags);
chan->request = EDMA_REQ_NONE;
chan->status = EDMA_ST_IDLE;
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
}
static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
@@ -712,21 +757,49 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
return 0;
}
-static void dw_edma_free_chan_resources(struct dma_chan *dchan)
+static void dw_edma_wait_termination(struct dma_chan *dchan)
{
+ struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
unsigned long timeout = jiffies + msecs_to_jiffies(5000);
- int ret;
+ bool stopping;
+ /*
+ * A STOP may be deferred to a later interrupt while the channel is still
+ * running. Wait until that handler completes the termination.
+ */
while (time_before(jiffies, timeout)) {
- ret = dw_edma_device_terminate_all(dchan);
- if (!ret)
- break;
+ scoped_guard(spinlock_irqsave, &chan->vc.lock)
+ stopping = chan->request == EDMA_REQ_STOP;
- if (time_after_eq(jiffies, timeout))
+ if (!stopping)
return;
- cpu_relax();
+ fsleep(1000);
}
+
+ dev_warn(chan->dw->chip->dev,
+ "timeout waiting for channel termination\n");
+}
+
+static void dw_edma_device_synchronize(struct dma_chan *dchan)
+{
+ struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+
+ dw_edma_wait_termination(dchan);
+ vchan_synchronize(&chan->vc);
+}
+
+static void dw_edma_free_chan_resources(struct dma_chan *dchan)
+{
+ struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+
+ dw_edma_device_terminate_all(dchan);
+ dw_edma_device_synchronize(dchan);
+
+ scoped_guard(spinlock_irqsave, &chan->vc.lock)
+ chan->configured = false;
+
+ vchan_free_chan_resources(&chan->vc);
}
static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
@@ -785,7 +858,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
else
irq->rd_mask |= BIT(chan->id);
- irq->dw = dw;
memcpy(&chan->msi, &irq->msi, sizeof(chan->msi));
dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n",
@@ -823,6 +895,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
dma->device_pause = dw_edma_device_pause;
dma->device_resume = dw_edma_device_resume;
dma->device_terminate_all = dw_edma_device_terminate_all;
+ dma->device_synchronize = dw_edma_device_synchronize;
dma->device_issue_pending = dw_edma_device_issue_pending;
dma->device_tx_status = dw_edma_device_tx_status;
dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
@@ -872,6 +945,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
if (chip->nr_irqs == 1) {
/* Common IRQ shared among all channels */
irq = chip->ops->irq_vector(dev, 0);
+ dw->irq[0].dw = dw;
err = request_irq(irq, dw_edma_interrupt_common,
IRQF_SHARED, dw->name, &dw->irq[0]);
if (err) {
@@ -897,6 +971,7 @@ static int dw_edma_irq_request(struct dw_edma *dw,
for (i = 0; i < (*wr_alloc + *rd_alloc); i++) {
irq = chip->ops->irq_vector(dev, i);
+ dw->irq[i].dw = dw;
err = request_irq(irq,
i < *wr_alloc ?
dw_edma_interrupt_write :
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index ce8f7254bab2..d2e5ddbb7b66 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -79,7 +79,7 @@ static enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan)
u32 tmp;
tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK,
- GET_CH_32(dw, chan->id, chan->dir, ch_stat));
+ GET_CH_32(dw, chan->dir, chan->id, ch_stat));
if (tmp == 1)
return DMA_IN_PROGRESS;
diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c
index c1350a36fddd..bbd4d44bae22 100644
--- a/drivers/dma/hisi_dma.c
+++ b/drivers/dma/hisi_dma.c
@@ -983,7 +983,7 @@ static int hisi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hdma_dev = devm_kzalloc(dev, struct_size(hdma_dev, chan, chan_num),
GFP_KERNEL);
if (!hdma_dev)
- return -EINVAL;
+ return -ENOMEM;
hdma_dev->base = pcim_iomap_table(pdev)[PCI_BAR_2];
hdma_dev->pdev = pdev;
diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
index aa42a09fde1a..98c9bdd1814f 100644
--- a/drivers/dma/mediatek/mtk-uart-apdma.c
+++ b/drivers/dma/mediatek/mtk-uart-apdma.c
@@ -530,7 +530,7 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev)
for (i = 0; i < mtkd->dma_requests; i++) {
c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
if (!c) {
- rc = -ENODEV;
+ rc = -ENOMEM;
goto err_no_dma;
}
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index d5882c8537e5..f931e63e1884 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -1395,6 +1395,7 @@ static int bam_dma_remove(struct platform_device *pdev)
struct bam_device *bdev = platform_get_drvdata(pdev);
u32 i;
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
of_dma_controller_free(pdev->dev.of_node);
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 3d728dd1ecde..2c5a85259eb7 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -1867,8 +1867,10 @@ static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data)
if (status & XILINX_MCDMA_IRQ_IOC_MASK) {
spin_lock(&chan->lock);
xilinx_dma_complete_descriptor(chan);
- chan->idle = true;
- chan->start_transfer(chan);
+ if (list_empty(&chan->active_list)) {
+ chan->idle = true;
+ chan->start_transfer(chan);
+ }
spin_unlock(&chan->lock);
}
@@ -1924,8 +1926,10 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
XILINX_DMA_DMASR_DLY_CNT_IRQ)) {
spin_lock(&chan->lock);
xilinx_dma_complete_descriptor(chan);
- chan->idle = true;
- chan->start_transfer(chan);
+ if (list_empty(&chan->active_list)) {
+ chan->idle = true;
+ chan->start_transfer(chan);
+ }
spin_unlock(&chan->lock);
}
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index e1b949aedf9e..22e5ed1f7320 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -146,6 +146,7 @@ static int scmi_protocol_device_request(const struct scmi_device_id *id_table)
*/
static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
{
+ struct scmi_requested_dev *rdev, *victim = NULL;
struct list_head *phead;
pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
@@ -154,29 +155,57 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table
mutex_lock(&scmi_requested_devices_mtx);
phead = idr_find(&scmi_requested_devices, id_table->protocol_id);
if (phead) {
- struct scmi_requested_dev *victim, *tmp;
-
- list_for_each_entry_safe(victim, tmp, phead, node) {
- if (!strcmp(victim->id_table->name, id_table->name)) {
- list_del(&victim->node);
-
- mutex_unlock(&scmi_requested_devices_mtx);
- blocking_notifier_call_chain(&scmi_requested_devices_nh,
- SCMI_BUS_NOTIFY_DEVICE_UNREQUEST,
- (void *)victim->id_table);
- kfree(victim);
- mutex_lock(&scmi_requested_devices_mtx);
+ list_for_each_entry(rdev, phead, node) {
+ if (!strcmp(rdev->id_table->name, id_table->name)) {
+ victim = rdev;
+ list_del(&rdev->node);
break;
}
}
- if (list_empty(phead)) {
+ if (victim && list_empty(phead)) {
idr_remove(&scmi_requested_devices,
id_table->protocol_id);
kfree(phead);
}
}
mutex_unlock(&scmi_requested_devices_mtx);
+
+ if (victim) {
+ blocking_notifier_call_chain(&scmi_requested_devices_nh,
+ SCMI_BUS_NOTIFY_DEVICE_UNREQUEST,
+ (void *)victim->id_table);
+ kfree(victim);
+ }
+}
+
+static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
+{
+ const struct scmi_device_id *entry;
+ int ret;
+
+ for (entry = id_table; entry->name; entry++) {
+ ret = scmi_protocol_device_request(entry);
+ if (ret)
+ goto err_unrequest;
+ }
+
+ return 0;
+
+err_unrequest:
+ while (entry != id_table)
+ scmi_protocol_device_unrequest(--entry);
+
+ return ret;
+}
+
+static void
+scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
+{
+ const struct scmi_device_id *entry;
+
+ for (entry = id_table; entry->name; entry++)
+ scmi_protocol_device_unrequest(entry);
}
static const struct scmi_device_id *
@@ -275,7 +304,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
if (!driver->probe)
return -EINVAL;
- retval = scmi_protocol_device_request(driver->id_table);
+ retval = scmi_protocol_table_register(driver->id_table);
if (retval)
return retval;
@@ -285,17 +314,21 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
driver->driver.mod_name = mod_name;
retval = driver_register(&driver->driver);
- if (!retval)
- pr_debug("Registered new scmi driver %s\n", driver->name);
+ if (retval) {
+ scmi_protocol_table_unregister(driver->id_table);
+ return retval;
+ }
- return retval;
+ pr_debug("Registered new scmi driver %s\n", driver->name);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(scmi_driver_register);
void scmi_driver_unregister(struct scmi_driver *driver)
{
driver_unregister(&driver->driver);
- scmi_protocol_device_unrequest(driver->id_table);
+ scmi_protocol_table_unregister(driver->id_table);
}
EXPORT_SYMBOL_GPL(scmi_driver_unregister);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index fbe893734411..d253e9d41d84 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -31,6 +31,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/processor.h>
+#include <linux/rcupdate.h>
#include <linux/refcount.h>
#include <linux/slab.h>
@@ -2286,21 +2287,31 @@ static int scmi_handle_put(const struct scmi_handle *handle)
return 0;
}
-static void scmi_device_link_add(struct device *consumer,
+static bool scmi_device_link_add(struct device *consumer,
struct device *supplier)
{
struct device_link *link;
link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER);
- WARN_ON(!link);
+ return !WARN_ON(!link);
+}
+
+static void scmi_clear_handle(struct scmi_device *scmi_dev)
+{
+ if (!scmi_dev->handle)
+ return;
+
+ scmi_handle_put(scmi_dev->handle);
+ scmi_dev->handle = NULL;
}
static void scmi_set_handle(struct scmi_device *scmi_dev)
{
scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
- if (scmi_dev->handle)
- scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev);
+ if (scmi_dev->handle &&
+ !scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev))
+ scmi_clear_handle(scmi_dev);
}
static int __scmi_xfer_info_init(struct scmi_info *sinfo,
@@ -2463,6 +2474,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
"unable to allocate SCMI idr slot err %d\n", ret);
/* Destroy channel and device only if created by this call. */
if (tdev) {
+ info->desc->ops->chan_free(prot_id, cinfo, idr);
of_node_put(of_node);
scmi_device_destroy(info->dev, prot_id, name);
devm_kfree(info->dev, cinfo);
@@ -2524,9 +2536,11 @@ static int scmi_channels_setup(struct scmi_info *info)
if (of_property_read_u32(child, "reg", &prot_id))
continue;
- if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) {
dev_err(info->dev,
"Out of range protocol %d\n", prot_id);
+ continue;
+ }
ret = scmi_txrx_setup(info, child, prot_id);
if (ret) {
@@ -2538,7 +2552,7 @@ static int scmi_channels_setup(struct scmi_info *info)
return 0;
}
-static int scmi_chan_destroy(int id, void *p, void *idr)
+static int scmi_chan_destroy(int id, void *p, void *data)
{
struct scmi_chan_info *cinfo = p;
@@ -2547,12 +2561,10 @@ static int scmi_chan_destroy(int id, void *p, void *idr)
struct scmi_device *sdev = to_scmi_dev(cinfo->dev);
of_node_put(cinfo->dev->of_node);
- scmi_device_destroy(info->dev, id, sdev->name);
+ scmi_device_destroy(info->dev, cinfo->id, sdev->name);
cinfo->dev = NULL;
}
- idr_remove(idr, id);
-
return 0;
}
@@ -2579,6 +2591,7 @@ static int scmi_bus_notifier(struct notifier_block *nb,
{
struct scmi_info *info = bus_nb_to_scmi_info(nb);
struct scmi_device *sdev = to_scmi_dev(data);
+ const char *status;
/* Skip transport devices and devices of different SCMI instances */
if (!strncmp(sdev->name, "__scmi_transport_device", 23) ||
@@ -2589,18 +2602,22 @@ static int scmi_bus_notifier(struct notifier_block *nb,
case BUS_NOTIFY_BIND_DRIVER:
/* setup handle now as the transport is ready */
scmi_set_handle(sdev);
+ status = "about to be BOUND.";
+ break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
+ scmi_clear_handle(sdev);
+ status = "NOT BOUND.";
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
- scmi_handle_put(sdev->handle);
- sdev->handle = NULL;
+ scmi_clear_handle(sdev);
+ status = "UNBOUND.";
break;
default:
return NOTIFY_DONE;
}
dev_dbg(info->dev, "Device %s (%s) is now %s\n", dev_name(&sdev->dev),
- sdev->name, action == BUS_NOTIFY_BIND_DRIVER ?
- "about to be BOUND." : "UNBOUND.");
+ sdev->name, status);
return NOTIFY_OK;
}
@@ -2612,7 +2629,9 @@ static int scmi_device_request_notifier(struct notifier_block *nb,
struct scmi_device_id *id_table = data;
struct scmi_info *info = req_nb_to_scmi_info(nb);
+ rcu_read_lock();
np = idr_find(&info->active_protocols, id_table->protocol_id);
+ rcu_read_unlock();
if (!np)
return NOTIFY_DONE;
@@ -2877,8 +2896,10 @@ static int scmi_probe(struct platform_device *pdev)
if (of_property_read_u32(child, "reg", &prot_id))
continue;
- if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) {
dev_err(dev, "Out of range protocol %d\n", prot_id);
+ continue;
+ }
if (!scmi_is_protocol_implemented(handle, prot_id)) {
dev_err(dev, "SCMI protocol %d not implemented\n",
@@ -2936,6 +2957,9 @@ static int scmi_remove(struct platform_device *pdev)
list_del(&info->node);
mutex_unlock(&scmi_list_mutex);
+ blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
+ &info->dev_req_nb);
+
scmi_notification_exit(&info->handle);
mutex_lock(&info->protocols_mtx);
@@ -2946,8 +2970,6 @@ static int scmi_remove(struct platform_device *pdev)
of_node_put(child);
idr_destroy(&info->active_protocols);
- blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
- &info->dev_req_nb);
bus_unregister_notifier(&scmi_bus_type, &info->bus_nb);
/* Safe to free channels since no more users */
diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c
index 33ae94745aef..8c5e2b6b7fe1 100644
--- a/drivers/firmware/google/coreboot_table.c
+++ b/drivers/firmware/google/coreboot_table.c
@@ -81,16 +81,20 @@ void coreboot_driver_unregister(struct coreboot_driver *driver)
}
EXPORT_SYMBOL(coreboot_driver_unregister);
-static int coreboot_table_populate(struct device *dev, void *ptr)
+static int coreboot_table_populate(struct device *dev, void *ptr, resource_size_t len)
{
int i, ret;
void *ptr_entry;
struct coreboot_device *device;
struct coreboot_table_entry *entry;
struct coreboot_table_header *header = ptr;
+ void *ptr_end;
+ ptr_end = ptr + len;
ptr_entry = ptr + header->header_bytes;
for (i = 0; i < header->table_entries; i++) {
+ if (ptr_entry + sizeof(*entry) > ptr_end)
+ return -EINVAL;
entry = ptr_entry;
if (entry->size < sizeof(*entry)) {
@@ -98,6 +102,9 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
return -EINVAL;
}
+ if (ptr_entry + entry->size > ptr_end)
+ return -EINVAL;
+
device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
if (!device)
return -ENOMEM;
@@ -132,6 +139,7 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
static int coreboot_table_probe(struct platform_device *pdev)
{
resource_size_t len;
+ resource_size_t table_span;
struct coreboot_table_header *header;
struct resource *res;
struct device *dev = &pdev->dev;
@@ -143,7 +151,7 @@ static int coreboot_table_probe(struct platform_device *pdev)
return -EINVAL;
len = resource_size(res);
- if (!res->start || !len)
+ if (!res->start || len < sizeof(*header))
return -EINVAL;
/* Check just the header first to make sure things are sane */
@@ -151,19 +159,27 @@ static int coreboot_table_probe(struct platform_device *pdev)
if (!header)
return -ENOMEM;
- len = header->header_bytes + header->table_bytes;
ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
+
+ if (!ret &&
+ (header->header_bytes < sizeof(*header) ||
+ check_add_overflow((resource_size_t)header->header_bytes,
+ (resource_size_t)header->table_bytes,
+ &table_span) ||
+ table_span > len))
+ ret = -EINVAL;
+
memunmap(header);
if (ret) {
dev_warn(dev, "coreboot table missing or corrupt!\n");
return -ENODEV;
}
- ptr = memremap(res->start, len, MEMREMAP_WB);
+ ptr = memremap(res->start, table_span, MEMREMAP_WB);
if (!ptr)
return -ENOMEM;
- ret = coreboot_table_populate(dev, ptr);
+ ret = coreboot_table_populate(dev, ptr, table_span);
memunmap(ptr);
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 5295ff90482b..d56d92cc4a34 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sizes.h>
+#include <linux/string.h>
#define CVP_BAR 0 /* BAR used for data transfer in memory mode */
#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */
@@ -265,7 +266,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
static int altera_cvp_send_block(struct altera_cvp_conf *conf,
const u32 *data, size_t len)
{
- u32 mask, words = len / sizeof(u32);
+ u32 words = len / sizeof(u32);
int i, remainder;
for (i = 0; i < words; i++)
@@ -274,9 +275,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf,
/* write up to 3 trailing bytes, if any */
remainder = len % sizeof(u32);
if (remainder) {
- mask = BIT(remainder * 8) - 1;
- if (mask)
- conf->write_data(conf, *data & mask);
+ u32 word = 0;
+
+ memcpy(&word, data, remainder);
+ conf->write_data(conf, word);
}
return 0;
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index cacb9cc5757e..070a1315500c 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
&ctype, sizeof(ctype));
if (ret < 0)
- goto init_done;
+ goto init_error;
- ret = wait_for_completion_timeout(
- &priv->status_return_completion, S10_RECONFIG_TIMEOUT);
- if (!ret) {
+ if (!wait_for_completion_timeout(&priv->status_return_completion,
+ S10_RECONFIG_TIMEOUT)) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
- ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
/* Allocate buffers from the service layer's pool. */
@@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
ret = PTR_ERR(kbuf);
- goto init_done;
+ goto init_error;
}
priv->svc_bufs[i].buf = kbuf;
priv->svc_bufs[i].lock = 0;
}
-init_done:
+ return 0;
+
+init_error:
stratix10_svc_done(priv->chan);
return ret;
}
@@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
if (!s10_free_buffers(mgr))
dev_err(dev, "%s not all buffers were freed\n", __func__);
+ if (ret < 0)
+ stratix10_svc_done(priv->chan);
+
return ret;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index fdfed86c8432..b2c5f99b2e57 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -1719,6 +1719,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
size >>= 1;
aligned_size = PAGE_ALIGN(size);
+ /* reject AQL queue with size < 2 */
+ if (!aligned_size) {
+ ret = -EINVAL;
+ goto err_alignment_size;
+ }
+
(*mem)->alloc_flags = flags;
amdgpu_sync_create(&(*mem)->sync);
@@ -1795,6 +1801,7 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id);
err_reserve_limit:
amdgpu_sync_free(&(*mem)->sync);
+err_alignment_size:
mutex_destroy(&(*mem)->lock);
if (gobj)
drm_gem_object_put(gobj);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 892a40c43963..47d6352c2def 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -3586,6 +3586,14 @@ static void amdgpu_device_set_mcbp(struct amdgpu_device *adev)
DRM_INFO("MCBP is enabled\n");
}
+static bool
+amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px)
+{
+ return !pci_is_thunderbolt_attached(adev->pdev) &&
+ (px || (!dev_is_removable(&adev->pdev->dev) &&
+ apple_gmux_detect(NULL, NULL)));
+}
+
/**
* amdgpu_device_init - initialize the driver
*
@@ -4002,8 +4010,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
px = amdgpu_device_supports_px(ddev);
- if (px || (!dev_is_removable(&adev->pdev->dev) &&
- apple_gmux_detect(NULL, NULL)))
+ if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_register_client(adev->pdev,
&amdgpu_switcheroo_ops, px);
@@ -4162,8 +4169,7 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
px = amdgpu_device_supports_px(adev_to_drm(adev));
- if (px || (!dev_is_removable(&adev->pdev->dev) &&
- apple_gmux_detect(NULL, NULL)))
+ if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_unregister_client(adev->pdev);
if (px)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 8a2072056e3e..90fe4833240f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2247,6 +2247,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev);
+ pm_runtime_dont_use_autosuspend(dev->dev);
}
if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 2) &&
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index dc860544e6e2..94670e7e116f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -1760,7 +1760,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev,
after->start = eaddr + 1;
after->last = tmp->last;
after->offset = tmp->offset;
- after->offset += (after->start - tmp->start) << PAGE_SHIFT;
+ after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT;
after->flags = tmp->flags;
after->bo_va = tmp->bo_va;
list_add(&after->list, &tmp->bo_va->invalids);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
index d4f043700108..c2a6fcc4eda7 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
@@ -1860,11 +1860,13 @@ static int gfx_v6_0_ring_test_ring(struct amdgpu_ring *ring)
return r;
}
-static void gfx_v6_0_ring_emit_vgt_flush(struct amdgpu_ring *ring)
+static void gfx_v6_0_ring_emit_event_write(struct amdgpu_ring *ring,
+ uint32_t event_type,
+ uint32_t event_index)
{
amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE, 0));
- amdgpu_ring_write(ring, EVENT_TYPE(VGT_FLUSH) |
- EVENT_INDEX(0));
+ amdgpu_ring_write(ring, EVENT_TYPE(event_type) |
+ EVENT_INDEX(event_index));
}
static void gfx_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
@@ -1903,12 +1905,6 @@ static void gfx_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
unsigned vmid = AMDGPU_JOB_GET_VMID(job);
u32 header, control = 0;
- /* insert SWITCH_BUFFER packet before first IB in the ring frame */
- if (flags & AMDGPU_HAVE_CTX_SWITCH) {
- amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
- amdgpu_ring_write(ring, 0);
- }
-
if (ib->flags & AMDGPU_IB_FLAG_CE)
header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
else
@@ -2330,7 +2326,7 @@ static int gfx_v6_0_cp_resume(struct amdgpu_device *adev)
static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
{
- int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+ int usepfp = 1;
uint32_t seq = ring->fence_drv.sync_seq;
uint64_t addr = ring->fence_drv.gpu_addr;
@@ -2343,20 +2339,12 @@ static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, seq);
amdgpu_ring_write(ring, 0xffffffff);
amdgpu_ring_write(ring, 4); /* poll interval */
-
- if (usepfp) {
- /* synce CE with ME to prevent CE fetch CEIB before context switch done */
- amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
- amdgpu_ring_write(ring, 0);
- amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
- amdgpu_ring_write(ring, 0);
- }
}
static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
unsigned vmid, uint64_t pd_addr)
{
- int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+ int usepfp = 1;
amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
@@ -2374,19 +2362,13 @@ static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
/* sync PFP to ME, otherwise we might get invalid PFP reads */
amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
amdgpu_ring_write(ring, 0x0);
-
- /* synce CE with ME to prevent CE fetch CEIB before context switch done */
- amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
- amdgpu_ring_write(ring, 0);
- amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
- amdgpu_ring_write(ring, 0);
}
}
static void gfx_v6_0_ring_emit_wreg(struct amdgpu_ring *ring,
uint32_t reg, uint32_t val)
{
- int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+ int usepfp = 1;
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
@@ -2999,12 +2981,30 @@ static uint64_t gfx_v6_0_get_gpu_clock_counter(struct amdgpu_device *adev)
return clock;
}
+static void gfx_v6_0_ring_emit_sb(struct amdgpu_ring *ring)
+{
+ amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+ amdgpu_ring_write(ring, 0);
+}
+
static void gfx_v6_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
{
- if (flags & AMDGPU_HAVE_CTX_SWITCH)
- gfx_v6_0_ring_emit_vgt_flush(ring);
+ u32 dw2 = 0x80000000; /* set load_enable otherwise this package is just NOPs */
+
+ if (flags & AMDGPU_HAVE_CTX_SWITCH) {
+ gfx_v6_0_ring_emit_event_write(ring, VS_PARTIAL_FLUSH, 4);
+ gfx_v6_0_ring_emit_event_write(ring, VGT_FLUSH, 0);
+
+ /* set load_global_config (load_global_uconfig doesn't exist on GFX6) */
+ dw2 |= 0x1;
+ /* set load_cs_sh_regs */
+ dw2 |= 0x01000000;
+ /* set load_per_context_state & load_gfx_sh_regs */
+ dw2 |= 0x10002;
+ }
+
amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
- amdgpu_ring_write(ring, 0x80000000);
+ amdgpu_ring_write(ring, dw2);
amdgpu_ring_write(ring, 0);
}
@@ -3536,11 +3536,12 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
.emit_frame_size =
5 + 5 + /* hdp flush / invalidate */
14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
- 7 + 4 + /* gfx_v6_0_ring_emit_pipeline_sync */
- SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 6 + /* gfx_v6_0_ring_emit_vm_flush */
- 3 + 2 + /* gfx_v6_ring_emit_cntxcntl including vgt flush */
+ 7 + /* gfx_v6_0_ring_emit_pipeline_sync */
+ SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
+ 3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
+ 3 + 2 + 2 + /* gfx_v6_ring_emit_cntxcntl including VGT flush */
5, /* SURFACE_SYNC */
- .emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
+ .emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
.emit_ib = gfx_v6_0_ring_emit_ib,
.emit_fence = gfx_v6_0_ring_emit_fence,
.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3548,6 +3549,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
.test_ring = gfx_v6_0_ring_test_ring,
.test_ib = gfx_v6_0_ring_test_ib,
.insert_nop = amdgpu_ring_insert_nop,
+ .emit_switch_buffer = gfx_v6_0_ring_emit_sb,
.emit_cntxcntl = gfx_v6_ring_emit_cntxcntl,
.emit_wreg = gfx_v6_0_ring_emit_wreg,
.emit_mem_sync = gfx_v6_0_emit_mem_sync,
@@ -3563,10 +3565,11 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
.emit_frame_size =
5 + 5 + /* hdp flush / invalidate */
7 + /* gfx_v6_0_ring_emit_pipeline_sync */
- SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v6_0_ring_emit_vm_flush */
+ SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
+ 3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
5, /* SURFACE_SYNC */
- .emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
+ .emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
.emit_ib = gfx_v6_0_ring_emit_ib,
.emit_fence = gfx_v6_0_ring_emit_fence,
.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3574,6 +3577,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
.test_ring = gfx_v6_0_ring_test_ring,
.test_ib = gfx_v6_0_ring_test_ib,
.insert_nop = amdgpu_ring_insert_nop,
+ .emit_switch_buffer = gfx_v6_0_ring_emit_sb,
.emit_wreg = gfx_v6_0_ring_emit_wreg,
.emit_mem_sync = gfx_v6_0_emit_mem_sync,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
index 37b93af917fe..0c56a46ba78e 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
@@ -1844,9 +1844,13 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job,
len_dw = msg[1] / 4;
num_buffers = msg[2];
- /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
- if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
- DRM_ERROR("VCN message has too many buffers!\n");
+ /* Verify that all indices fit within the claimed length.
+ * There are 6 dwords in the header before the first buffer.
+ * Each buffer has 4 dwords. Any trailing dwords after the
+ * last buffer are ignored.
+ */
+ if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+ DRM_ERROR("Invalid VCN message!\n");
r = -EINVAL;
goto out;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
index 61f981fe9c5a..9d3368fa9cb0 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
@@ -1722,9 +1722,13 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job,
len_dw = msg[1] / 4;
num_buffers = msg[2];
- /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
- if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
- DRM_ERROR("VCN message has too many buffers!\n");
+ /* Verify that all indices fit within the claimed length.
+ * There are 6 dwords in the header before the first buffer.
+ * Each buffer has 4 dwords. Any trailing dwords after the
+ * last buffer are ignored.
+ */
+ if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+ DRM_ERROR("Invalid VCN message!\n");
r = -EINVAL;
goto out;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 5ed8aff7bd81..2e4a3f72b88a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -1183,7 +1183,8 @@ static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep,
if (flags & KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM)
size >>= 1;
- atomic64_add(PAGE_ALIGN(size), &pdd->vram_usage);
+ size = PAGE_ALIGN(size);
+ atomic64_add(size, &pdd->vram_usage);
}
mutex_unlock(&p->mutex);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index a4cb23d059bd..191a3c5cded5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -118,6 +118,12 @@ static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
uint32_t expected;
int delta;
+ /* A LUT with fewer than two entries can't be interpolated and would
+ * divide by zero below (size - 1); it can't be treated as linear.
+ */
+ if (size < 2)
+ return false;
+
for (i = 0; i < size; i++) {
/* All color values should equal */
if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
index 5a965c26bf20..64d7e4eb8c74 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
@@ -102,6 +102,11 @@ static int get_estimated_bw(struct dc_link *link)
{
uint8_t bw_estimated_bw = 0;
+ if (link->dpia_bw_alloc_config.bw_granularity == 0) {
+ DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
+ return 0;
+ }
+
core_link_read_dpcd(
link,
ESTIMATED_BW,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
index 9dc01de3e720..73e0d10c02fa 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
@@ -2216,12 +2216,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table(
if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
voltage_id = sclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddgfx_lookup_table->count) {
+ pr_err("amdgpu: sclk[%u] vddgfx index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddgfx_lookup_table->count);
+ return -EINVAL;
+ }
sclk_table->entries[entry_id].vddgfx =
table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd;
}
} else {
for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
voltage_id = sclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: sclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
sclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
@@ -2229,12 +2241,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table(
for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
voltage_id = mclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
voltage_id = mm_table->entries[entry_id].vddcInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mm_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
index 6c87b3d4ab36..084ed8c5bf42 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
@@ -685,10 +685,18 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table(
case 3: vdt = table_info->vdd_dep_on_pixclk; break;
case 4: vdt = table_info->vdd_dep_on_dispclk; break;
case 5: vdt = table_info->vdd_dep_on_phyclk; break;
+ default:
+ continue;
}
for (entry_id = 0; entry_id < vdt->count; entry_id++) {
voltage_id = vdt->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: clk_dep[%u][%u] vddc index %u out of bounds (%u)\n",
+ i, entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
vdt->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
@@ -696,23 +704,48 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table(
for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
voltage_id = mm_table->entries[entry_id].vddcInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mm_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
voltage_id = mclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+
voltage_id = mclk_table->entries[entry_id].vddciInd;
+ if (voltage_id >= table_info->vddci_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddci index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddci_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddci =
table_info->vddci_lookup_table->entries[voltage_id].us_vdd;
+
voltage_id = mclk_table->entries[entry_id].mvddInd;
+ if (voltage_id >= table_info->vddmem_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddmem index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddmem_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].mvdd =
table_info->vddmem_lookup_table->entries[voltage_id].us_vdd;
}
-
return 0;
}
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index 44955601a9df..eeefb0fa70d8 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -2161,7 +2161,7 @@ cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge)
cdns_mhdp_state = kzalloc(sizeof(*cdns_mhdp_state), GFP_KERNEL);
if (!cdns_mhdp_state)
- return NULL;
+ return ERR_PTR(-ENOMEM);
__drm_atomic_helper_bridge_reset(bridge, &cdns_mhdp_state->base);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 6c1d79474505..2df2ef78fdca 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3401,7 +3401,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
break;
default:
dev_err(dev, "reg-io-width must be 1 or 4\n");
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ goto err_res;
}
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 6a3f29390313..b503b5e35a6c 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -441,7 +441,7 @@ static ssize_t tc_aux_transfer(struct drm_dp_aux *aux,
* address-only transfer
*/
if (size)
- size = FIELD_GET(AUX_BYTES, auxstatus);
+ size = min_t(size_t, size, FIELD_GET(AUX_BYTES, auxstatus));
msg->reply = FIELD_GET(AUX_STATUS, auxstatus);
switch (request) {
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 814b1da36ee9..edf34aaec243 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -1304,6 +1304,8 @@ static void complete_signaling(struct drm_device *dev,
* to prevent a double free in drm_atomic_state_clear.
*/
if (event && (event->base.fence || event->base.file_priv)) {
+ if (crtc_state->commit && crtc_state->commit->abort_completion)
+ drm_crtc_commit_put(crtc_state->commit);
drm_event_cancel_free(dev, &event->base);
crtc_state->event = NULL;
}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index d453d710ef0c..a86ffef49451 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -470,6 +470,22 @@ void drm_dev_exit(int idx)
}
EXPORT_SYMBOL(drm_dev_exit);
+/*
+ * Mark the device as unplugged and wait for any in-flight drm_dev_enter()
+ * critical sections to complete.
+ */
+static void drm_dev_synchronize_unplug(struct drm_device *dev)
+{
+ /*
+ * After synchronizing any critical read section is guaranteed to see
+ * the new value of ->unplugged, and any critical section which might
+ * still have seen the old value of ->unplugged is guaranteed to have
+ * finished.
+ */
+ dev->unplugged = true;
+ synchronize_srcu(&drm_unplug_srcu);
+}
+
/**
* drm_dev_unplug - unplug a DRM device
* @dev: DRM device
@@ -482,15 +498,7 @@ EXPORT_SYMBOL(drm_dev_exit);
*/
void drm_dev_unplug(struct drm_device *dev)
{
- /*
- * After synchronizing any critical read section is guaranteed to see
- * the new value of ->unplugged, and any critical section which might
- * still have seen the old value of ->unplugged is guaranteed to have
- * finished.
- */
- dev->unplugged = true;
- synchronize_srcu(&drm_unplug_srcu);
-
+ drm_dev_synchronize_unplug(dev);
drm_dev_unregister(dev);
/* Clear all CPU mappings pointing to this device */
@@ -931,6 +939,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
goto err_minors;
dev->registered = true;
+ dev->unplugged = false;
if (driver->load) {
ret = driver->load(dev, flags);
@@ -956,6 +965,13 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
if (dev->driver->unload)
dev->driver->unload(dev);
err_minors:
+ /*
+ * If a minor was registered before the failure, userspace could have
+ * opened it and entered a drm_dev_enter() critical section. Ensure all
+ * such sections complete before we clean up.
+ */
+ drm_dev_synchronize_unplug(dev);
+
remove_compat_control_link(dev);
drm_minor_unregister(dev, DRM_MINOR_ACCEL);
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index 70032bba1c97..7b4ba9fab299 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -29,7 +29,6 @@
#include <drm/drm_mode.h>
#include "drm_crtc_internal.h"
-#include "drm_internal.h"
/**
* DOC: overview
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
index 034e78360d4f..fd4054dcdcab 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -397,8 +397,16 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect
}
num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
- for (i = 0; i < num_modes; i++)
- modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+ for (i = 0; i < num_modes; i++) {
+ char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+ if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
+ ret = -EIO;
+ goto free;
+ }
+
+ modes[i] = mode;
+ }
ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
free:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index db7fce4e8cc3..165e7e26e689 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -72,46 +72,28 @@ static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode)
static int hibmc_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
- plane);
- struct drm_framebuffer *fb = new_plane_state->fb;
- struct drm_crtc *crtc = new_plane_state->crtc;
- struct drm_crtc_state *crtc_state;
- u32 src_w = new_plane_state->src_w >> 16;
- u32 src_h = new_plane_state->src_h >> 16;
-
- if (!crtc || !fb)
- return 0;
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc_state *new_crtc_state = NULL;
+ int ret;
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
+ if (new_plane_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
- if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
- drm_dbg_atomic(plane->dev, "scale not support\n");
- return -EINVAL;
- }
-
- if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) {
- drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
- return -EINVAL;
- }
-
- if (!crtc_state->enable)
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, true);
+ if (ret)
+ return ret;
+ else if (!new_plane_state->visible)
return 0;
- if (new_plane_state->crtc_x + new_plane_state->crtc_w >
- crtc_state->adjusted_mode.hdisplay ||
- new_plane_state->crtc_y + new_plane_state->crtc_h >
- crtc_state->adjusted_mode.vdisplay) {
- drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
- return -EINVAL;
- }
-
if (new_plane_state->fb->pitches[0] % 128 != 0) {
drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
return -EINVAL;
}
+
return 0;
}
@@ -153,10 +135,8 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane,
}
static const u32 channel_formats1[] = {
- DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
- DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
- DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
- DRM_FORMAT_ABGR8888
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB565,
};
static const struct drm_plane_funcs hibmc_plane_funcs = {
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index fcacdc21643c..9b1eeda6a9a0 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -1014,6 +1014,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
(struct intel_device_info *) ent->driver_data;
int err;
+ if (!intel_info)
+ return -ENODEV;
+
if (intel_info->require_force_probe && !id_forced(pdev->device)) {
dev_info(&pdev->dev,
"Your graphics device %04x is not properly supported by i915 in this\n"
diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c
index 02cef0cea657..3843797d376b 100644
--- a/drivers/gpu/drm/lima/lima_device.c
+++ b/drivers/gpu/drm/lima/lima_device.c
@@ -368,12 +368,6 @@ int lima_device_init(struct lima_device *ldev)
if (err)
goto err_out0;
- ldev->empty_vm = lima_vm_create(ldev);
- if (!ldev->empty_vm) {
- err = -ENOMEM;
- goto err_out1;
- }
-
ldev->va_start = 0;
if (ldev->id == lima_gpu_mali450) {
ldev->va_end = LIMA_VA_RESERVE_START;
@@ -387,6 +381,12 @@ int lima_device_init(struct lima_device *ldev)
} else
ldev->va_end = LIMA_VA_RESERVE_END;
+ ldev->empty_vm = lima_vm_create(ldev);
+ if (!ldev->empty_vm) {
+ err = -ENOMEM;
+ goto err_out1;
+ }
+
ldev->iomem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ldev->iomem)) {
dev_err(ldev->dev, "fail to ioremap iomem\n");
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index e816ddcac2f8..b5b77d9f6b7c 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -492,7 +492,7 @@ static void a6xx_rpmh_stop(struct a6xx_gmu *gmu)
int ret;
u32 val;
- if (test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status))
+ if (!test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status))
return;
gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1);
@@ -1100,6 +1100,9 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu)
/* Stop the interrupts and mask the hardware */
a6xx_gmu_irq_disable(gmu);
+ /* Halt the gmu cm3 core */
+ gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1);
+
/* Tell RPMh to power off the GPU */
a6xx_rpmh_stop(gmu);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c
index 260accc151d4..d29df73c02c5 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c
@@ -383,6 +383,10 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc,
trace_dpu_core_perf_update_clk(kms->dev, !crtc->enabled, clk_rate);
+ /* If we're going offline, PM callbacks will disable the clocks instead */
+ if (!clk_rate)
+ return 0;
+
clk_rate = min(clk_rate, kms->perf.max_core_clk_rate);
ret = dev_pm_opp_set_rate(&kms->pdev->dev, clk_rate);
if (ret) {
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index f5ac0e20665f..be61f265fad9 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -513,8 +513,6 @@ int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host)
{
- /* Drop the performance state vote */
- dev_pm_opp_set_rate(&msm_host->pdev->dev, 0);
clk_disable_unprepare(msm_host->esc_clk);
clk_disable_unprepare(msm_host->pixel_clk);
clk_disable_unprepare(msm_host->byte_intf_clk);
@@ -635,12 +633,24 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
{
+ long rounded_byte_clk_rate;
+
if (!msm_host->mode) {
pr_err("%s: mode not set\n", __func__);
return -EINVAL;
}
dsi_calc_pclk(msm_host, is_bonded_dsi);
+
+ rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
+ msm_host->byte_clk_rate);
+ if (rounded_byte_clk_rate < 0) {
+ pr_err("%s: failed to round byte clock rate, %ld\n",
+ __func__, rounded_byte_clk_rate);
+ return rounded_byte_clk_rate;
+ }
+
+ msm_host->byte_clk_rate = rounded_byte_clk_rate;
msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk);
return 0;
}
diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c
index 2541d2de4e45..16dd2957128e 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_kms.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c
@@ -373,14 +373,23 @@ static void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
int ret;
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ /* Disable the layer for DMA. */
reg &= ~CTRLDESCL0_5_EN;
+ /*
+ * It is necessary to wait for the full frame to finish streaming
+ * through the DMA engine before we can safely disable it by removing
+ * the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the
+ * hardware confused and unable to resume streaming for the next frame.
+ */
+ reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ /* Wait for the frame to finish or timeout after 50 ms. */
ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
- reg, !(reg & CTRLDESCL0_5_EN),
- 0, 36000); /* Wait ~2 frame times max */
+ reg, !(reg & CTRLDESCL0_5_SHADOW_LOAD_EN),
+ 200, 50000);
if (ret)
- drm_err(lcdif->drm, "Failed to disable controller!\n");
+ drm_err(lcdif->drm, "Timed out waiting for final vblank!\n");
reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
reg &= ~DISP_PARA_DISP_ON;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index e8ce33c89548..a0b018106576 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -307,11 +307,20 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
rep->offset = nvbo->offset;
if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50 &&
!nouveau_cli_uvmm(cli)) {
+ int ret;
+
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
+ if (ret)
+ return ret;
+
vma = nouveau_vma_find(nvbo, vmm);
- if (!vma)
+ if (!vma) {
+ ttm_bo_unreserve(&nvbo->bo);
return -EINVAL;
+ }
rep->offset = vma->addr;
+ ttm_bo_unreserve(&nvbo->bo);
} else
rep->offset = 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index b14895f75b3c..5bb63177cf6b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags)
struct nouveau_sgdma_be *nvbe;
enum ttm_caching caching;
- if (nvbo->force_coherent)
- caching = ttm_uncached;
- else if (drm->agp.bridge)
+ if (nvbo->force_coherent || drm->agp.bridge)
caching = ttm_write_combined;
else
caching = ttm_cached;
diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index 53a514802d62..1310ee5f4973 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -1191,6 +1191,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)
op->va.range);
if (!op->reg || op->reg->dirty) {
ret = -ENOENT;
+ op->reg = NULL;
goto unwind_continue;
}
@@ -1199,6 +1200,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)
op->va.range);
if (IS_ERR(op->ops)) {
ret = PTR_ERR(op->ops);
+ op->reg = NULL;
goto unwind_continue;
}
@@ -1390,6 +1392,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)
op->va.range);
break;
case OP_UNMAP_SPARSE:
+ op->reg->dirty = false;
__nouveau_uvma_region_insert(uvmm, op->reg);
nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new,
op->ops);
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c
index ea63c64d3a1a..b9b013ba80e2 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.c
@@ -455,15 +455,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
del_timer(&dsi->te_timer);
#endif
- /* make a copy and unlock, so that isrs can unregister
- * themselves */
- memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
- sizeof(dsi->isr_tables));
+ dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus);
spin_unlock(&dsi->irq_lock);
- dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
-
dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.h b/drivers/gpu/drm/omapdrm/dss/dsi.h
index 601707c0ecc4..2b25247ea893 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.h
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.h
@@ -379,8 +379,6 @@ struct dsi_data {
spinlock_t irq_lock;
struct dsi_isr_tables isr_tables;
- /* space for a copy used by the interrupt handler */
- struct dsi_isr_tables isr_tables_copy;
int update_vc;
#ifdef DSI_PERF_MEASURE
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 53b3b24d7d7c..aacc5cf758d9 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -830,6 +830,13 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel)
return ret;
}
+static void panel_edp_put_adapter(void *_adap)
+{
+ struct i2c_adapter *adap = _adap;
+
+ put_device(&adap->dev);
+}
+
static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
struct drm_dp_aux *aux)
{
@@ -877,6 +884,11 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
if (!panel->ddc)
return -EPROBE_DEFER;
+
+ err = devm_add_action_or_reset(dev, panel_edp_put_adapter,
+ panel->ddc);
+ if (err)
+ return err;
} else if (aux) {
panel->ddc = &aux->ddc;
}
@@ -890,7 +902,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
err = drm_panel_of_backlight(&panel->base);
if (err)
- goto err_finished_ddc_init;
+ return err;
/*
* We use runtime PM for prepare / unprepare since those power the panel
@@ -931,9 +943,6 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
err_finished_pm_runtime:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
-err_finished_ddc_init:
- if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
- put_device(&panel->ddc->dev);
return err;
}
@@ -948,8 +957,6 @@ static void panel_edp_remove(struct device *dev)
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
- if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
- put_device(&panel->ddc->dev);
kfree(panel->edid);
panel->edid = NULL;
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
index 79f611963c61..f73b603be0b0 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
@@ -89,16 +89,22 @@ static int s6d16d0_prepare(struct drm_panel *panel)
MIPI_DSI_DCS_TEAR_MODE_VBLANK);
if (ret) {
dev_err(s6->dev, "failed to enable vblank TE (%d)\n", ret);
- return ret;
+ goto err_power_off;
}
/* Exit sleep mode and power on */
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret) {
dev_err(s6->dev, "failed to exit sleep mode (%d)\n", ret);
- return ret;
+ goto err_power_off;
}
return 0;
+
+err_power_off:
+ gpiod_set_value_cansleep(s6->reset_gpio, 1);
+ regulator_disable(s6->supply);
+
+ return ret;
}
static int s6d16d0_enable(struct drm_panel *panel)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 97486eba01b7..bd5bce9d8c2c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -425,6 +425,8 @@ static bool is_yuv_output(u32 bus_format)
switch (bus_format) {
case MEDIA_BUS_FMT_YUV8_1X24:
case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_YUYV8_2X8:
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index 4943577ec7fb..9dfbf23f55e1 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -683,7 +683,7 @@ static int sun4i_backend_init_sat(struct device *dev) {
ret = clk_prepare_enable(backend->sat_clk);
if (ret) {
dev_err(dev, "Couldn't enable the SAT clock\n");
- return ret;
+ goto err_assert_reset;
}
return 0;
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index c06d7cd45388..49fd3523cdbb 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -208,7 +208,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
planes = sunxi_engine_layers_init(drm, engine);
if (IS_ERR(planes)) {
dev_err(drm->dev, "Couldn't create the planes\n");
- return NULL;
+ return ERR_CAST(planes);
}
/* find primary and cursor planes for drm_crtc_init_with_planes */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index a1a2c845ade0..ab4409dcbf30 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -717,9 +717,11 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
case DRM_MODE_ENCODER_DSI:
/* DSI is tied to special case of CPU interface */
sun4i_tcon0_mode_set_cpu(tcon, encoder, mode);
+ sun4i_tcon_set_mux(tcon, 0, encoder);
break;
case DRM_MODE_ENCODER_LVDS:
sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
+ sun4i_tcon_set_mux(tcon, 0, encoder);
break;
case DRM_MODE_ENCODER_NONE:
sun4i_tcon0_mode_set_rgb(tcon, encoder, mode);
@@ -970,6 +972,7 @@ static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
continue;
ret = of_property_read_u32(remote, "reg", ®);
+ of_node_put(remote);
if (ret)
continue;
@@ -1407,7 +1410,7 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
{
struct device_node *port, *remote;
struct platform_device *pdev;
- int id, ret;
+ int id, ret = 0;
/* find TCON TOP platform device and TCON id */
@@ -1430,21 +1433,20 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
encoder->encoder_type == DRM_MODE_ENCODER_TMDS) {
ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id);
- if (ret) {
- put_device(&pdev->dev);
- return ret;
- }
+ if (ret)
+ goto out_put_device;
}
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) {
ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id);
- if (ret) {
- put_device(&pdev->dev);
- return ret;
- }
+ if (ret)
+ goto out_put_device;
}
- return 0;
+out_put_device:
+ put_device(&pdev->dev);
+
+ return ret;
}
static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 4727dfaa8fb9..af9be9ba8b96 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -83,6 +83,8 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
of_node_put(remote_port);
}
}
+
+ of_node_put(port);
} else {
crtcs = drm_of_find_possible_crtcs(drm, node);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 4fa69c463dc4..489ea94693ff 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -91,7 +91,7 @@ static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
},
}, {
594000000, {
- { 0x1a40, 0x0003 },
+ { 0x1a7c, 0x0003 },
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
index 7ba75011adf9..328b84df2ee5 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
@@ -891,20 +891,26 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
lan3coefftab32_left[offset + i]);
regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i),
lan3coefftab32_right[offset + i]);
+ }
+ offset = sun8i_vi_scaler_coef_index(vstep) *
+ SUN8I_VI_SCALER_COEFF_COUNT;
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
+ regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
+ lan2coefftab32[offset + i]);
+
+ offset = sun8i_vi_scaler_coef_index(hstep / format->hsub) *
+ SUN8I_VI_SCALER_COEFF_COUNT;
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i),
ch_left[offset + i]);
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i),
ch_right[offset + i]);
}
-
- offset = sun8i_vi_scaler_coef_index(hstep) *
+ offset = sun8i_vi_scaler_coef_index(vstep / format->vsub) *
SUN8I_VI_SCALER_COEFF_COUNT;
- for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
- regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
- lan2coefftab32[offset + i]);
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i),
cy[offset + i]);
- }
}
void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index acce210e2554..0f55ead3ddbb 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -261,6 +261,7 @@ static const struct of_device_id tve200_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, tve200_of_match);
static struct platform_driver tve200_driver = {
.driver = {
diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c
index a18cc8d8caf5..ec819164e84f 100644
--- a/drivers/gpu/host1x/debug.c
+++ b/drivers/gpu/host1x/debug.c
@@ -31,7 +31,7 @@ void host1x_debug_output(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
- len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
+ len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, false);
@@ -43,7 +43,7 @@ void host1x_debug_cont(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
- len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
+ len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, true);
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index d44b8de890be..46b6494768b4 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -36,10 +36,9 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
for (i = 0; i < words; i += TRACE_MAX_LENGTH) {
u32 num_words = min(words - i, TRACE_MAX_LENGTH);
- offset += i * sizeof(u32);
-
trace_host1x_cdma_push_gather(dev_name(dev), bo,
- num_words, offset,
+ num_words,
+ offset + i * sizeof(u32),
mem);
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index b924980b5783..446ed394dc48 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2370,9 +2370,16 @@ EXPORT_SYMBOL_GPL(hid_hw_start);
*
* This is usually called from remove function or from probe when something
* failed and hid_hw_start was called already.
+ *
+ * If the caller enabled HID input via hid_device_io_start() and is unwinding
+ * without an explicit hid_device_io_stop(), quiesce input first so that
+ * in-flight reports cannot reach handlers (e.g. hidraw_report_event) whose
+ * backing objects hid_disconnect() is about to free.
*/
void hid_hw_stop(struct hid_device *hdev)
{
+ if (hdev->io_started)
+ hid_device_io_stop(hdev);
hid_disconnect(hdev);
hdev->ll_driver->stop(hdev);
}
@@ -2694,6 +2701,8 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
*/
if (ret) {
+ if (hdev->io_started)
+ hid_device_io_stop(hdev);
devres_release_group(&hdev->dev, hdev->devres_group_id);
hid_close_report(hdev);
hdev->driver = NULL;
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index e3fcf1353fb3..71f168eff29b 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -336,6 +336,8 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
if (entry->wdata.combine) {
switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_WHEEL:
+ if (size < 7)
+ return 0;
rd[5] = rd[3];
rd[6] = 0x7F;
return 1;
@@ -343,10 +345,14 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+ if (size < 6)
+ return 0;
rd[4] = rd[3];
rd[5] = 0x7F;
return 1;
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ if (size < 7)
+ return 0;
rd[5] = rd[4];
rd[6] = 0x7F;
return 1;
@@ -366,6 +372,8 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
}
/* Compute a combined axis when wheel does not supply it */
+ if (size <= offset + 1)
+ return 0;
rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;
rd[offset+1] = 0x7F;
return 1;
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index 99af0b541316..6678b14e9855 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -759,6 +759,9 @@ static int mcp2221_raw_event(struct hid_device *hdev,
u8 *buf;
struct mcp2221 *mcp = hid_get_drvdata(hdev);
+ if (size < 4)
+ return 0;
+
switch (data[0]) {
case MCP2221_I2C_WR_DATA:
@@ -820,6 +823,10 @@ static int mcp2221_raw_event(struct hid_device *hdev,
mcp->status = -EINVAL;
break;
}
+ if (4 + data[3] > size) {
+ mcp->status = -EINVAL;
+ break;
+ }
buf = mcp->rxbuf;
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
@@ -943,6 +950,8 @@ static void mcp2221_hid_unregister(void *ptr)
{
struct hid_device *hdev = ptr;
+ if (hdev->io_started)
+ hid_device_io_stop(hdev);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index a69b04ffbb2b..115f496368e5 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -1164,7 +1164,6 @@ static void joycon_parse_imu_report(struct joycon_ctlr *ctlr,
dropped_threshold = ctlr->imu_avg_delta_ms * 3 / 2;
dropped_pkts = (delta - min(delta, dropped_threshold)) /
ctlr->imu_avg_delta_ms;
- ctlr->imu_timestamp_us += 1000 * ctlr->imu_avg_delta_ms;
if (dropped_pkts > JC_IMU_DROPPED_PKT_WARNING) {
hid_warn(ctlr->hdev,
"compensating for %u dropped IMU reports\n",
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
index d01176da8896..0d325d69e64b 100644
--- a/drivers/hid/hid-picolcd_debugfs.c
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -98,6 +98,15 @@ static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
ret = resp->raw_data[2];
if (ret > s)
ret = s;
+ /*
+ * raw_data[2] is a device-supplied length; also clamp it to
+ * what picolcd_raw_event() actually stored (raw_size), or a
+ * hostile device overruns the raw_data[] buffer.
+ */
+ if (ret > resp->raw_size - 3)
+ ret = resp->raw_size - 3;
+ if (ret < 0)
+ ret = 0;
if (copy_to_user(u, resp->raw_data+3, ret))
ret = -EFAULT;
else
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 84e7ba5314d3..8788192c0956 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -235,7 +235,23 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr,
break;
}
- read_input_count = data->readReport[1];
+ read_input_count = min_t(int, data->readReport[1],
+ data->input_report_size - 2);
+ if (!read_input_count) {
+ /*
+ * A zero length reply advances neither
+ * bytes_read nor bytes_needed, and because a
+ * reply did arrive the wait above does not
+ * time out either, so a device answering 0
+ * forever would spin here indefinitely with
+ * page_mutex held.
+ */
+ hid_warn(hdev, "%s: zero-length read reply\n",
+ __func__);
+ clear_bit(RMI_READ_DATA_PENDING, &data->flags);
+ ret = -EIO;
+ break;
+ }
memcpy(buf + bytes_read, &data->readReport[2],
min(read_input_count, bytes_needed));
@@ -271,6 +287,11 @@ static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr,
goto exit;
}
+ if (len + 4 > data->output_report_size) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
data->writeReport[0] = RMI_WRITE_REPORT_ID;
data->writeReport[1] = len;
data->writeReport[2] = addr & 0xFF;
@@ -668,8 +689,16 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
- if (id->driver_data)
- data->device_flags = id->driver_data;
+ /*
+ * RMI_DEVICE can only mean "this probe validated the RMI reports and
+ * allocated writeReport": every bail-out to start below skips that
+ * allocation, and device_flags left carrying RMI_DEVICE from
+ * driver_data would send rmi_input_configured() into rmi_set_page()
+ * with writeReport still NULL. A bind through the new_id sysfs
+ * attribute can supply driver_data with the bit set, so do not let
+ * driver_data grant it.
+ */
+ data->device_flags = id->driver_data & ~RMI_DEVICE;
/*
* Check for the RMI specific report ids. If they are misisng
@@ -698,6 +727,17 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
data->output_report_size = hid_report_len(output_report);
+ /*
+ * The write reports built by this driver occupy 6 bytes and the read
+ * handshake looks at the first 3 bytes of an input report, so refuse
+ * to drive a device whose reports cannot hold them.
+ */
+ if (data->output_report_size < 6 || data->input_report_size < 3) {
+ hid_err(hdev, "rmi reports too small (out=%u in=%u)\n",
+ data->output_report_size, data->input_report_size);
+ goto start;
+ }
+
data->device_flags |= RMI_DEVICE;
alloc_size = data->output_report_size + data->input_report_size;
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index 00a1abc7e839..ab17179afb3c 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -36,6 +36,8 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4};
static void kone_profile_activated(struct kone_device *kone, uint new_profile)
{
+ if (new_profile < 1 || new_profile > ARRAY_SIZE(kone->profiles))
+ new_profile = 1;
kone->actual_profile = new_profile;
kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi;
}
@@ -793,8 +795,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
{
switch (event->event) {
case kone_mouse_event_switch_profile:
- kone->actual_dpi = kone->profiles[event->value - 1].
- startup_dpi;
+ if (event->value >= 1 &&
+ event->value <= ARRAY_SIZE(kone->profiles))
+ kone->actual_dpi =
+ kone->profiles[event->value - 1].startup_dpi;
fallthrough;
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index e413662f7508..e54b6a8a92ef 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES];
/* protects modifications of devices array */
static DEFINE_MUTEX(devices_lock);
+static void roccat_free_device(struct roccat_device *device)
+{
+ int i;
+
+ for (i = 0; i < ROCCAT_CBUF_SIZE; i++)
+ kfree(device->cbuf[i].value);
+ kfree(device);
+}
+
static ssize_t roccat_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
@@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file)
hid_hw_power(device->hid, PM_HINT_NORMAL);
hid_hw_close(device->hid);
} else {
- kfree(device);
+ roccat_free_device(device);
}
}
@@ -374,7 +383,7 @@ void roccat_disconnect(int minor)
hid_hw_close(device->hid);
wake_up_interruptible(&device->wait);
} else {
- kfree(device);
+ roccat_free_device(device);
}
}
EXPORT_SYMBOL_GPL(roccat_disconnect);
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index 2147cb350acb..635ab761dcb0 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -610,7 +610,7 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom
&sensor_inst->fields[i].
hid_custom_attribute_group);
if (ret)
- break;
+ goto err_remove_groups;
/* For power or report field store indexes */
if (sensor_inst->fields[i].attribute.attrib_id ==
@@ -622,6 +622,13 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom
}
return ret;
+
+err_remove_groups:
+ while (--i >= 0)
+ sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
+ &sensor_inst->fields[i].hid_custom_attribute_group);
+ kfree(sensor_inst->fields);
+ return ret;
}
static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom *
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 3fc2967bc817..316654294f25 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -239,12 +239,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, int buffer_size, void *buffer)
{
struct hid_report *report;
+ struct hid_field *field;
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
- int report_size;
+ size_t field_size;
+ size_t report_size;
+ size_t copied = 0;
+ size_t to_copy;
int ret = 0;
- u8 *val_ptr;
- int buffer_index = 0;
- int i;
+ unsigned int i;
+
+ if (!buffer || buffer_size <= 0)
+ return -EINVAL;
memset(buffer, 0, buffer_size);
@@ -258,26 +263,29 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
hid_hw_wait(hsdev->hdev);
+ field = report->field[field_index];
+
/* calculate number of bytes required to read this field */
- report_size = DIV_ROUND_UP(report->field[field_index]->report_size,
- 8) *
- report->field[field_index]->report_count;
- if (!report_size) {
+ field_size = DIV_ROUND_UP(field->report_size, 8);
+ /* HID core stores each parsed report value in a __s32 slot. */
+ if (!field_size || field_size > sizeof(field->value[0])) {
+ ret = -EINVAL;
+ goto done_proc;
+ }
+ if (field->report_count > SIZE_MAX / field_size) {
ret = -EINVAL;
goto done_proc;
}
- ret = min(report_size, buffer_size);
- val_ptr = (u8 *)report->field[field_index]->value;
- for (i = 0; i < report->field[field_index]->report_count; ++i) {
- if (buffer_index >= ret)
- break;
+ report_size = field_size * field->report_count;
+ report_size = min_t(size_t, report_size, buffer_size);
- memcpy(&((u8 *)buffer)[buffer_index], val_ptr,
- report->field[field_index]->report_size / 8);
- val_ptr += sizeof(__s32);
- buffer_index += (report->field[field_index]->report_size / 8);
+ for (i = 0; i < field->report_count && copied < report_size; ++i) {
+ to_copy = min(field_size, report_size - copied);
+ memcpy(&((u8 *)buffer)[copied], &field->value[i], to_copy);
+ copied += to_copy;
}
+ ret = copied;
done_proc:
mutex_unlock(&data->mutex);
diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile
index 55bd5e0f35af..38d5d827f3ce 100644
--- a/drivers/hid/i2c-hid/Makefile
+++ b/drivers/hid/i2c-hid/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_I2C_HID_CORE) += i2c-hid.o
i2c-hid-objs = i2c-hid-core.o
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
-obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o
+obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o i2c-hid-acpi-prp0001.o
obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o
obj-$(CONFIG_I2C_HID_OF_ELAN) += i2c-hid-of-elan.o
obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
new file mode 100644
index 000000000000..d2cf4714ae7f
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HID over I2C driver for PRP0001 devices missing hid-descr-addr
+ *
+ * Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3, use
+ * _HID "PRP0001" with _DSD compatible "hid-over-i2c" but lack "hid-descr-addr"
+ * from the _DSD. The HID descriptor address is provided only through an ACPI
+ * _DSM. The TPD0 node in the DSDT shows _DSM Function 1 returning 0x20.
+ *
+ * Copyright (C) 2026 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "i2c-hid.h"
+#include "i2c-hid-acpi.h"
+
+static int i2c_hid_acpi_prp0001_power_up(struct i2chid_ops *ops)
+{
+ /* give the device time to power up */
+ msleep(750);
+ return 0;
+}
+
+static struct i2chid_ops i2c_hid_acpi_prp0001_ops = {
+ .power_up = i2c_hid_acpi_prp0001_power_up,
+ /*
+ * No .restore_sequence needed: the _DSM on these devices returns a
+ * constant (0x20) with no side effects, unlike some PNP0C50 _DSM
+ * implementations that switch the hardware between PS/2 and I2C modes.
+ */
+};
+
+static int i2c_hid_acpi_prp0001_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct acpi_device *adev;
+ u16 hid_descriptor_address;
+ int ret;
+
+ /* If hid-descr-addr is present, let i2c-hid-of handle it */
+ if (device_property_present(dev, "hid-descr-addr"))
+ return -ENODEV;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return -ENODEV;
+
+ ret = i2c_hid_acpi_get_descriptor(adev);
+ if (ret < 0)
+ return ret;
+ dev_warn(dev,
+ "hid-descr-addr device property NOT found, using ACPI _DSM fallback. Contact vendor for firmware update!\n");
+ hid_descriptor_address = ret;
+
+ /*
+ * No acpi_device_fix_up_power() needed: TPD0 has no _PS0, _PS3, _PSC
+ * or _PRx methods and follows I2C bus power.
+ */
+ return i2c_hid_core_probe(client, &i2c_hid_acpi_prp0001_ops,
+ hid_descriptor_address, 0);
+}
+
+static const struct of_device_id i2c_hid_acpi_prp0001_of_match[] = {
+ { .compatible = "hid-over-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_hid_acpi_prp0001_of_match);
+
+static const struct i2c_device_id i2c_hid_acpi_prp0001_id[] = {
+ { .name = "hid-over-i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, i2c_hid_acpi_prp0001_id);
+
+static struct i2c_driver i2c_hid_acpi_prp0001_driver = {
+ .driver = {
+ .name = "i2c_hid_acpi_prp0001",
+ .pm = &i2c_hid_core_pm,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ /*
+ * of_match_ptr() makes this NULL when CONFIG_OF=n, but that's
+ * fine: the I2C id_table with "hid-over-i2c" handles matching
+ * via client->name (set by acpi_set_modalias() from the _DSD
+ * compatible property).
+ */
+ .of_match_table = of_match_ptr(i2c_hid_acpi_prp0001_of_match),
+ },
+
+ .probe = i2c_hid_acpi_prp0001_probe,
+ .remove = i2c_hid_core_remove,
+ .shutdown = i2c_hid_core_shutdown,
+ .id_table = i2c_hid_acpi_prp0001_id,
+};
+
+module_i2c_driver(i2c_hid_acpi_prp0001_driver);
+
+MODULE_DESCRIPTION("HID over I2C driver for PRP0001 devices missing hid-descr-addr");
+MODULE_AUTHOR("谢致邦 (XIE Zhibang) <Yeking@Red54.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c
index abd700a101f4..13f977d6aab6 100644
--- a/drivers/hid/i2c-hid/i2c-hid-acpi.c
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c
@@ -25,9 +25,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>
-#include <linux/uuid.h>
#include "i2c-hid.h"
+#include "i2c-hid-acpi.h"
struct i2c_hid_acpi {
struct i2chid_ops ops;
@@ -48,39 +48,11 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
{ }
};
-/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
-static guid_t i2c_hid_guid =
- GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
- 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
-
-static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi)
-{
- struct acpi_device *adev = ihid_acpi->adev;
- acpi_handle handle = acpi_device_handle(adev);
- union acpi_object *obj;
- u16 hid_descriptor_address;
-
- if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
- return -ENODEV;
-
- obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
- ACPI_TYPE_INTEGER);
- if (!obj) {
- acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n");
- return -ENODEV;
- }
-
- hid_descriptor_address = obj->integer.value;
- ACPI_FREE(obj);
-
- return hid_descriptor_address;
-}
-
static void i2c_hid_acpi_restore_sequence(struct i2chid_ops *ops)
{
struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
- i2c_hid_acpi_get_descriptor(ihid_acpi);
+ i2c_hid_acpi_get_descriptor(ihid_acpi->adev);
}
static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
@@ -93,24 +65,28 @@ static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
static int i2c_hid_acpi_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ struct acpi_device *adev = ACPI_COMPANION(dev);
struct i2c_hid_acpi *ihid_acpi;
u16 hid_descriptor_address;
int ret;
- ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
+ if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
+ return -ENODEV;
+
+ ret = i2c_hid_acpi_get_descriptor(adev);
+ if (ret < 0)
+ return ret;
+ hid_descriptor_address = ret;
+
+ ihid_acpi = devm_kzalloc(dev, sizeof(*ihid_acpi), GFP_KERNEL);
if (!ihid_acpi)
return -ENOMEM;
- ihid_acpi->adev = ACPI_COMPANION(dev);
+ ihid_acpi->adev = adev;
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
ihid_acpi->ops.restore_sequence = i2c_hid_acpi_restore_sequence;
- ret = i2c_hid_acpi_get_descriptor(ihid_acpi);
- if (ret < 0)
- return ret;
- hid_descriptor_address = ret;
-
- acpi_device_fix_up_power(ihid_acpi->adev);
+ acpi_device_fix_up_power(adev);
return i2c_hid_core_probe(client, &ihid_acpi->ops,
hid_descriptor_address, 0);
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.h b/drivers/hid/i2c-hid/i2c-hid-acpi.h
new file mode 100644
index 000000000000..0bbed1853313
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _I2C_HID_ACPI_H
+#define _I2C_HID_ACPI_H
+
+#include <linux/acpi.h>
+#include <linux/uuid.h>
+
+static inline int i2c_hid_acpi_get_descriptor(struct acpi_device *adev)
+{
+ /* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
+ static const guid_t i2c_hid_guid =
+ GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
+ 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
+
+ acpi_handle handle = acpi_device_handle(adev);
+ union acpi_object *obj;
+ u16 addr;
+
+ obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid,
+ 1, 1, NULL, ACPI_TYPE_INTEGER);
+ if (!obj) {
+ acpi_handle_err(handle,
+ "Error _DSM call to get HID descriptor address failed\n");
+ return -ENODEV;
+ }
+
+ addr = obj->integer.value;
+ ACPI_FREE(obj);
+ return addr;
+}
+
+#endif
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index 0a350780407e..b36284e2d5cb 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -420,12 +420,23 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
return ret;
}
-static int i2c_hid_execute_reset(struct i2c_hid *ihid)
+static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
{
size_t length = 0;
int ret;
- i2c_hid_dbg(ihid, "resetting...\n");
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ /*
+ * This prevents sending feature reports while the device is
+ * being reset. Otherwise we may lose the reset complete
+ * interrupt.
+ */
+ lockdep_assert_held(&ihid->reset_lock);
+
+ ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
+ if (ret)
+ return ret;
/* Prepare reset command. Command register goes first. */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
@@ -438,60 +449,45 @@ static int i2c_hid_execute_reset(struct i2c_hid *ihid)
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
if (ret) {
- dev_err(&ihid->client->dev, "failed to reset device.\n");
- goto out;
- }
-
- if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
- msleep(100);
- goto out;
+ dev_err(&ihid->client->dev,
+ "failed to reset device: %d\n", ret);
+ goto err_clear_reset;
}
- i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
- if (!wait_event_timeout(ihid->wait,
- !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
- msecs_to_jiffies(5000))) {
- ret = -ENODATA;
- goto out;
- }
- i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
+ return 0;
-out:
+err_clear_reset:
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+ i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
return ret;
}
-static int i2c_hid_hwreset(struct i2c_hid *ihid)
+static int i2c_hid_finish_hwreset(struct i2c_hid *ihid)
{
- int ret;
-
- i2c_hid_dbg(ihid, "%s\n", __func__);
+ int ret = 0;
- /*
- * This prevents sending feature reports while the device is
- * being reset. Otherwise we may lose the reset complete
- * interrupt.
- */
- mutex_lock(&ihid->reset_lock);
-
- ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
- if (ret)
- goto out_unlock;
+ i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
- ret = i2c_hid_execute_reset(ihid);
- if (ret) {
- dev_err(&ihid->client->dev,
- "failed to reset device: %d\n", ret);
- i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
- goto out_unlock;
+ if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
+ msleep(100);
+ clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+ } else if (!wait_event_timeout(ihid->wait,
+ !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
+ msecs_to_jiffies(5000))) {
+ ret = -ENODATA;
+ goto err_clear_reset;
}
+ i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
/* At least some SIS devices need this after reset */
if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
-out_unlock:
- mutex_unlock(&ihid->reset_lock);
+ return ret;
+
+err_clear_reset:
+ clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+ i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
return ret;
}
@@ -720,11 +716,10 @@ static int i2c_hid_parse(struct hid_device *hid)
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct i2c_hid_desc *hdesc = &ihid->hdesc;
+ char *rdesc = NULL, *use_override = NULL;
unsigned int rsize;
- char *rdesc;
int ret;
int tries = 3;
- char *use_override;
i2c_hid_dbg(ihid, "entering %s\n", __func__);
@@ -734,11 +729,15 @@ static int i2c_hid_parse(struct hid_device *hid)
return -EINVAL;
}
+ mutex_lock(&ihid->reset_lock);
do {
- ret = i2c_hid_hwreset(ihid);
- if (ret)
+ ret = i2c_hid_start_hwreset(ihid);
+ if (ret == 0)
+ ret = i2c_hid_finish_hwreset(ihid);
+ else
msleep(1000);
} while (tries-- > 0 && ret);
+ mutex_unlock(&ihid->reset_lock);
if (ret)
return ret;
@@ -751,11 +750,8 @@ static int i2c_hid_parse(struct hid_device *hid)
i2c_hid_dbg(ihid, "Using a HID report descriptor override\n");
} else {
rdesc = kzalloc(rsize, GFP_KERNEL);
-
- if (!rdesc) {
- dbg_hid("couldn't allocate rdesc memory\n");
+ if (!rdesc)
return -ENOMEM;
- }
i2c_hid_dbg(ihid, "asking HID report descriptor\n");
@@ -763,24 +759,22 @@ static int i2c_hid_parse(struct hid_device *hid)
ihid->hdesc.wReportDescRegister,
rdesc, rsize);
if (ret) {
- hid_err(hid, "reading report descriptor failed\n");
- kfree(rdesc);
- return -EIO;
+ dev_err(&client->dev, "reading report descriptor failed\n");
+ goto out;
}
}
i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
ret = hid_parse_report(hid, rdesc, rsize);
+ if (ret)
+ dbg_hid("parsing report descriptor failed\n");
+
+out:
if (!use_override)
kfree(rdesc);
- if (ret) {
- dbg_hid("parsing report descriptor failed\n");
- return ret;
- }
-
- return 0;
+ return ret;
}
static int i2c_hid_start(struct hid_device *hid)
@@ -987,10 +981,15 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
* However some ALPS touchpads generate IRQ storm without reset, so
* let's still reset them here.
*/
- if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
- ret = i2c_hid_hwreset(ihid);
- else
+ if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) {
+ mutex_lock(&ihid->reset_lock);
+ ret = i2c_hid_start_hwreset(ihid);
+ if (ret == 0)
+ ret = i2c_hid_finish_hwreset(ihid);
+ mutex_unlock(&ihid->reset_lock);
+ } else {
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
+ }
if (ret)
return ret;
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
index f1597ad67e7c..f4dbcd1d1d47 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
@@ -51,8 +51,10 @@ static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
return ret;
ret = regulator_enable(ihid_goodix->vddio);
- if (ret)
+ if (ret) {
+ regulator_disable(ihid_goodix->vdd);
return ret;
+ }
if (ihid_goodix->timings->post_power_delay_ms)
msleep(ihid_goodix->timings->post_power_delay_ms);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 859d0d9c5894..58ff34799771 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1594,6 +1594,19 @@ static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
return 0;
}
+ if (wacom->features.type == INTUOSP2_BT ||
+ wacom->features.type == INTUOSP2S_BT) {
+ if (len < 286) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Pro2 BT report too short: %zu bytes\n", len);
+ return 0;
+ }
+ } else if (len < 46) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Pro2 BT report too short: %zu bytes\n", len);
+ return 0;
+ }
+
wacom_intuos_pro2_bt_pen(wacom);
if (wacom->features.type == INTUOSP2_BT ||
wacom->features.type == INTUOSP2S_BT) {
diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c
index 4c1a00f9929e..f2d14631f1e5 100644
--- a/drivers/hsi/controllers/omap_ssi_core.c
+++ b/drivers/hsi/controllers/omap_ssi_core.c
@@ -511,6 +511,12 @@ static int ssi_probe(struct platform_device *pd)
pm_runtime_enable(&pd->dev);
+ ssi->device.dma_mask = &ssi->device.coherent_dma_mask;
+
+ err = dma_set_mask_and_coherent(&ssi->device, DMA_BIT_MASK(32));
+ if (err)
+ goto out2;
+
err = ssi_hw_init(ssi);
if (err < 0)
goto out2;
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index bb7c859e799d..288d540ebabf 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -17,70 +17,34 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/sysfs.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#define THERMAL_PID_REG 0xfd
#define THERMAL_SMSC_ID_REG 0xfe
#define THERMAL_REVISION_REG 0xff
-enum emc1403_chip { emc1402, emc1403, emc1404 };
+enum emc1403_chip { emc1402, emc1403, emc1404, emc1428 };
struct thermal_data {
+ enum emc1403_chip chip;
struct regmap *regmap;
- struct mutex mutex;
- const struct attribute_group *groups[4];
};
-static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct thermal_data *data = dev_get_drvdata(dev);
unsigned int val;
int retval;
- retval = regmap_read(data->regmap, sda->index, &val);
+ retval = regmap_read(data->regmap, 0x03, &val);
if (retval < 0)
return retval;
- return sprintf(buf, "%d000\n", val);
+ return sprintf(buf, "%d\n", !!(val & BIT(6)));
}
-static ssize_t bit_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t power_state_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
- struct thermal_data *data = dev_get_drvdata(dev);
- unsigned int val;
- int retval;
-
- retval = regmap_read(data->regmap, sda->nr, &val);
- if (retval < 0)
- return retval;
- return sprintf(buf, "%d\n", !!(val & sda->index));
-}
-
-static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
- struct thermal_data *data = dev_get_drvdata(dev);
- unsigned long val;
- int retval;
-
- if (kstrtoul(buf, 10, &val))
- return -EINVAL;
- retval = regmap_write(data->regmap, sda->index,
- DIV_ROUND_CLOSEST(val, 1000));
- if (retval < 0)
- return retval;
- return count;
-}
-
-static ssize_t bit_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
struct thermal_data *data = dev_get_drvdata(dev);
unsigned long val;
int retval;
@@ -88,233 +52,20 @@ static ssize_t bit_store(struct device *dev, struct device_attribute *attr,
if (kstrtoul(buf, 10, &val))
return -EINVAL;
- retval = regmap_update_bits(data->regmap, sda->nr, sda->index,
- val ? sda->index : 0);
+ retval = regmap_update_bits(data->regmap, 0x03, BIT(6),
+ val ? BIT(6) : 0);
if (retval < 0)
return retval;
return count;
}
-static ssize_t show_hyst_common(struct device *dev,
- struct device_attribute *attr, char *buf,
- bool is_min)
-{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
- struct thermal_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
- unsigned int limit;
- unsigned int hyst;
- int retval;
-
- retval = regmap_read(regmap, sda->index, &limit);
- if (retval < 0)
- return retval;
-
- retval = regmap_read(regmap, 0x21, &hyst);
- if (retval < 0)
- return retval;
-
- return sprintf(buf, "%d000\n", is_min ? limit + hyst : limit - hyst);
-}
-
-static ssize_t hyst_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return show_hyst_common(dev, attr, buf, false);
-}
-
-static ssize_t min_hyst_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return show_hyst_common(dev, attr, buf, true);
-}
-
-static ssize_t hyst_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
- struct thermal_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
- unsigned int limit;
- int retval;
- int hyst;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val))
- return -EINVAL;
-
- mutex_lock(&data->mutex);
- retval = regmap_read(regmap, sda->index, &limit);
- if (retval < 0)
- goto fail;
-
- hyst = limit * 1000 - val;
- hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255);
- retval = regmap_write(regmap, 0x21, hyst);
- if (retval == 0)
- retval = count;
-fail:
- mutex_unlock(&data->mutex);
- return retval;
-}
-
-/*
- * Sensors. We pass the actual i2c register to the methods.
- */
-
-static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 0x06);
-static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 0x05);
-static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, 0x20);
-static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0x00);
-static SENSOR_DEVICE_ATTR_2_RO(temp1_min_alarm, bit, 0x36, 0x01);
-static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, bit, 0x35, 0x01);
-static SENSOR_DEVICE_ATTR_2_RO(temp1_crit_alarm, bit, 0x37, 0x01);
-static SENSOR_DEVICE_ATTR_RO(temp1_min_hyst, min_hyst, 0x06);
-static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, hyst, 0x05);
-static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, hyst, 0x20);
-
-static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, 0x08);
-static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, 0x07);
-static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, 0x19);
-static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 0x01);
-static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, bit, 0x1b, 0x02);
-static SENSOR_DEVICE_ATTR_2_RO(temp2_min_alarm, bit, 0x36, 0x02);
-static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, bit, 0x35, 0x02);
-static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, bit, 0x37, 0x02);
-static SENSOR_DEVICE_ATTR_RO(temp2_min_hyst, min_hyst, 0x08);
-static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, hyst, 0x07);
-static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, hyst, 0x19);
-
-static SENSOR_DEVICE_ATTR_RW(temp3_min, temp, 0x16);
-static SENSOR_DEVICE_ATTR_RW(temp3_max, temp, 0x15);
-static SENSOR_DEVICE_ATTR_RW(temp3_crit, temp, 0x1A);
-static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 0x23);
-static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, bit, 0x1b, 0x04);
-static SENSOR_DEVICE_ATTR_2_RO(temp3_min_alarm, bit, 0x36, 0x04);
-static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, bit, 0x35, 0x04);
-static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, bit, 0x37, 0x04);
-static SENSOR_DEVICE_ATTR_RO(temp3_min_hyst, min_hyst, 0x16);
-static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, hyst, 0x15);
-static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, hyst, 0x1A);
-
-static SENSOR_DEVICE_ATTR_RW(temp4_min, temp, 0x2D);
-static SENSOR_DEVICE_ATTR_RW(temp4_max, temp, 0x2C);
-static SENSOR_DEVICE_ATTR_RW(temp4_crit, temp, 0x30);
-static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 0x2A);
-static SENSOR_DEVICE_ATTR_2_RO(temp4_fault, bit, 0x1b, 0x08);
-static SENSOR_DEVICE_ATTR_2_RO(temp4_min_alarm, bit, 0x36, 0x08);
-static SENSOR_DEVICE_ATTR_2_RO(temp4_max_alarm, bit, 0x35, 0x08);
-static SENSOR_DEVICE_ATTR_2_RO(temp4_crit_alarm, bit, 0x37, 0x08);
-static SENSOR_DEVICE_ATTR_RO(temp4_min_hyst, min_hyst, 0x2D);
-static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, hyst, 0x2C);
-static SENSOR_DEVICE_ATTR_RO(temp4_crit_hyst, hyst, 0x30);
-
-static SENSOR_DEVICE_ATTR_2_RW(power_state, bit, 0x03, 0x40);
-
-static struct attribute *emc1402_attrs[] = {
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
-
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_min_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
-
- &sensor_dev_attr_power_state.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group emc1402_group = {
- .attrs = emc1402_attrs,
-};
+static DEVICE_ATTR_RW(power_state);
static struct attribute *emc1403_attrs[] = {
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
-
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp3_crit.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_min_hyst.dev_attr.attr,
- &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &dev_attr_power_state.attr,
NULL
};
-
-static const struct attribute_group emc1403_group = {
- .attrs = emc1403_attrs,
-};
-
-static struct attribute *emc1404_attrs[] = {
- &sensor_dev_attr_temp4_min.dev_attr.attr,
- &sensor_dev_attr_temp4_max.dev_attr.attr,
- &sensor_dev_attr_temp4_crit.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_min_hyst.dev_attr.attr,
- &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp4_crit_hyst.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group emc1404_group = {
- .attrs = emc1404_attrs,
-};
-
-/*
- * EMC14x2 uses a different register and different bits to report alarm and
- * fault status. For simplicity, provide a separate attribute group for this
- * chip series.
- * Since we can not re-use the same attribute names, create a separate attribute
- * array.
- */
-static struct sensor_device_attribute_2 emc1402_alarms[] = {
- SENSOR_ATTR_2_RO(temp1_min_alarm, bit, 0x02, 0x20),
- SENSOR_ATTR_2_RO(temp1_max_alarm, bit, 0x02, 0x40),
- SENSOR_ATTR_2_RO(temp1_crit_alarm, bit, 0x02, 0x01),
-
- SENSOR_ATTR_2_RO(temp2_fault, bit, 0x02, 0x04),
- SENSOR_ATTR_2_RO(temp2_min_alarm, bit, 0x02, 0x08),
- SENSOR_ATTR_2_RO(temp2_max_alarm, bit, 0x02, 0x10),
- SENSOR_ATTR_2_RO(temp2_crit_alarm, bit, 0x02, 0x02),
-};
-
-static struct attribute *emc1402_alarm_attrs[] = {
- &emc1402_alarms[0].dev_attr.attr,
- &emc1402_alarms[1].dev_attr.attr,
- &emc1402_alarms[2].dev_attr.attr,
- &emc1402_alarms[3].dev_attr.attr,
- &emc1402_alarms[4].dev_attr.attr,
- &emc1402_alarms[5].dev_attr.attr,
- &emc1402_alarms[6].dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group emc1402_alarm_group = {
- .attrs = emc1402_alarm_attrs,
-};
+ATTRIBUTE_GROUPS(emc1403);
static int emc1403_detect(struct i2c_client *client,
struct i2c_board_info *info)
@@ -346,6 +97,15 @@ static int emc1403_detect(struct i2c_client *client,
case 0x27:
strscpy(info->type, "emc1424", I2C_NAME_SIZE);
break;
+ case 0x29:
+ strscpy(info->type, "emc1428", I2C_NAME_SIZE);
+ break;
+ case 0x59:
+ strscpy(info->type, "emc1438", I2C_NAME_SIZE);
+ break;
+ case 0x60:
+ strscpy(info->type, "emc1442", I2C_NAME_SIZE);
+ break;
default:
return -ENODEV;
}
@@ -373,6 +133,14 @@ static bool emc1403_regmap_is_volatile(struct device *dev, unsigned int reg)
case 0x35: /* high limit status */
case 0x36: /* low limit status */
case 0x37: /* therm limit status */
+ case 0x41: /* external diode 4 high byte */
+ case 0x42: /* external diode 4 low byte */
+ case 0x43: /* external diode 5 high byte */
+ case 0x44: /* external diode 5 low byte */
+ case 0x45: /* external diode 6 high byte */
+ case 0x46: /* external diode 6 low byte */
+ case 0x47: /* external diode 7 high byte */
+ case 0x48: /* external diode 7 low byte */
return true;
default:
return false;
@@ -386,51 +154,415 @@ static const struct regmap_config emc1403_regmap_config = {
.volatile_reg = emc1403_regmap_is_volatile,
};
-static const struct i2c_device_id emc1403_idtable[];
+enum emc1403_reg_map {temp_min, temp_max, temp_crit, temp_input};
-static int emc1403_probe(struct i2c_client *client)
-{
- struct thermal_data *data;
- struct device *hwmon_dev;
- const struct i2c_device_id *id = i2c_match_id(emc1403_idtable, client);
+static u8 ema1403_temp_map[] = {
+ [hwmon_temp_min] = temp_min,
+ [hwmon_temp_max] = temp_max,
+ [hwmon_temp_crit] = temp_crit,
+ [hwmon_temp_input] = temp_input,
+};
- data = devm_kzalloc(&client->dev, sizeof(struct thermal_data),
- GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
+static u8 emc1403_temp_regs[][4] = {
+ [0] = {
+ [temp_min] = 0x06,
+ [temp_max] = 0x05,
+ [temp_crit] = 0x20,
+ [temp_input] = 0x00,
+ },
+ [1] = {
+ [temp_min] = 0x08,
+ [temp_max] = 0x07,
+ [temp_crit] = 0x19,
+ [temp_input] = 0x01,
+ },
+ [2] = {
+ [temp_min] = 0x16,
+ [temp_max] = 0x15,
+ [temp_crit] = 0x1a,
+ [temp_input] = 0x23,
+ },
+ [3] = {
+ [temp_min] = 0x2d,
+ [temp_max] = 0x2c,
+ [temp_crit] = 0x30,
+ [temp_input] = 0x2a,
+ },
+ [4] = {
+ [temp_min] = 0x51,
+ [temp_max] = 0x50,
+ [temp_crit] = 0x64,
+ [temp_input] = 0x41,
+ },
+ [5] = {
+ [temp_min] = 0x55,
+ [temp_max] = 0x54,
+ [temp_crit] = 0x65,
+ [temp_input] = 0x43
+ },
+ [6] = {
+ [temp_min] = 0x59,
+ [temp_max] = 0x58,
+ [temp_crit] = 0x66,
+ [temp_input] = 0x45,
+ },
+ [7] = {
+ [temp_min] = 0x5d,
+ [temp_max] = 0x5c,
+ [temp_crit] = 0x67,
+ [temp_input] = 0x47,
+ },
+};
- data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config);
- if (IS_ERR(data->regmap))
- return PTR_ERR(data->regmap);
+static s8 emc1403_temp_regs_low[][4] = {
+ [0] = {
+ [temp_min] = -1,
+ [temp_max] = -1,
+ [temp_crit] = -1,
+ [temp_input] = 0x29,
+ },
+ [1] = {
+ [temp_min] = 0x14,
+ [temp_max] = 0x13,
+ [temp_crit] = -1,
+ [temp_input] = 0x10,
+ },
+ [2] = {
+ [temp_min] = 0x18,
+ [temp_max] = 0x17,
+ [temp_crit] = -1,
+ [temp_input] = 0x24,
+ },
+ [3] = {
+ [temp_min] = 0x2f,
+ [temp_max] = 0x2e,
+ [temp_crit] = -1,
+ [temp_input] = 0x2b,
+ },
+ [4] = {
+ [temp_min] = 0x53,
+ [temp_max] = 0x52,
+ [temp_crit] = -1,
+ [temp_input] = 0x42,
+ },
+ [5] = {
+ [temp_min] = 0x57,
+ [temp_max] = 0x56,
+ [temp_crit] = -1,
+ [temp_input] = 0x44,
+ },
+ [6] = {
+ [temp_min] = 0x5b,
+ [temp_max] = 0x5a,
+ [temp_crit] = -1,
+ [temp_input] = 0x46,
+ },
+ [7] = {
+ [temp_min] = 0x5f,
+ [temp_max] = 0x5e,
+ [temp_crit] = -1,
+ [temp_input] = 0x48,
+ },
+};
- mutex_init(&data->mutex);
-
- switch (id->driver_data) {
- case emc1404:
- data->groups[2] = &emc1404_group;
- fallthrough;
- case emc1403:
- data->groups[1] = &emc1403_group;
- fallthrough;
- case emc1402:
- data->groups[0] = &emc1402_group;
+static int emc1403_get_temp(struct thermal_data *data, int channel,
+ enum emc1403_reg_map map, long *val)
+{
+ unsigned int regvalh;
+ unsigned int regvall = 0;
+ int ret;
+ s8 reg;
+
+ ret = regmap_read(data->regmap, emc1403_temp_regs[channel][map], ®valh);
+ if (ret < 0)
+ return ret;
+
+ reg = emc1403_temp_regs_low[channel][map];
+ if (reg >= 0) {
+ ret = regmap_read(data->regmap, reg, ®vall);
+ if (ret < 0)
+ return ret;
}
- if (id->driver_data == emc1402)
- data->groups[1] = &emc1402_alarm_group;
+ if (data->chip == emc1428)
+ *val = sign_extend32((regvalh << 3) | (regvall >> 5), 10) * 125;
+ else
+ *val = ((regvalh << 3) | (regvall >> 5)) * 125;
+
+ return 0;
+}
+
+static int emc1403_get_hyst(struct thermal_data *data, int channel,
+ enum emc1403_reg_map map, long *val)
+{
+ int hyst, ret;
+ long limit;
- hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
- client->name, data,
- data->groups);
- if (IS_ERR(hwmon_dev))
- return PTR_ERR(hwmon_dev);
+ ret = emc1403_get_temp(data, channel, map, &limit);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(data->regmap, 0x21, &hyst);
+ if (ret < 0)
+ return ret;
+
+ *val = limit - hyst * 1000;
- dev_info(&client->dev, "%s Thermal chip found\n", id->name);
return 0;
}
-static const unsigned short emc1403_address_list[] = {
- 0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
+static int emc1403_temp_read(struct thermal_data *data, u32 attr, int channel, long *val)
+{
+ unsigned int regval;
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_input:
+ ret = emc1403_get_temp(data, channel, ema1403_temp_map[attr], val);
+ break;
+ case hwmon_temp_max_hyst:
+ ret = emc1403_get_hyst(data, channel, temp_max, val);
+ break;
+ case hwmon_temp_crit_hyst:
+ ret = emc1403_get_hyst(data, channel, temp_crit, val);
+ break;
+ case hwmon_temp_min_alarm:
+ if (data->chip == emc1402) {
+ ret = regmap_read(data->regmap, 0x02, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(5 - 2 * channel));
+ } else {
+ ret = regmap_read(data->regmap, 0x36, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(channel));
+ }
+ break;
+ case hwmon_temp_max_alarm:
+ if (data->chip == emc1402) {
+ ret = regmap_read(data->regmap, 0x02, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(6 - 2 * channel));
+ } else {
+ ret = regmap_read(data->regmap, 0x35, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(channel));
+ }
+ break;
+ case hwmon_temp_crit_alarm:
+ if (data->chip == emc1402) {
+ ret = regmap_read(data->regmap, 0x02, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(channel));
+ } else {
+ ret = regmap_read(data->regmap, 0x37, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(channel));
+ }
+ break;
+ case hwmon_temp_fault:
+ ret = regmap_read(data->regmap, 0x1b, ®val);
+ if (ret < 0)
+ break;
+ *val = !!(regval & BIT(channel));
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int emc1403_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct thermal_data *data = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return emc1403_temp_read(data, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int emc1403_set_hyst(struct thermal_data *data, long val)
+{
+ int hyst, ret;
+ long limit;
+
+ if (data->chip == emc1428)
+ val = clamp_val(val, -128000, 127000);
+ else
+ val = clamp_val(val, 0, 255000);
+
+ ret = emc1403_get_temp(data, 0, temp_crit, &limit);
+ if (ret < 0)
+ return ret;
+
+ hyst = limit - val;
+ if (data->chip == emc1428)
+ hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 127);
+ else
+ hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255);
+ return regmap_write(data->regmap, 0x21, hyst);
+}
+
+static int emc1403_set_temp(struct thermal_data *data, int channel,
+ enum emc1403_reg_map map, long val)
+{
+ unsigned int regval;
+ int ret;
+ u8 regh;
+ s8 regl;
+
+ regh = emc1403_temp_regs[channel][map];
+ regl = emc1403_temp_regs_low[channel][map];
+
+ if (regl >= 0) {
+ if (data->chip == emc1428)
+ val = clamp_val(val, -128000, 127875);
+ else
+ val = clamp_val(val, 0, 255875);
+ regval = DIV_ROUND_CLOSEST(val, 125);
+ ret = regmap_write(data->regmap, regh, (regval >> 3) & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(data->regmap, regl, (regval & 0x07) << 5);
+ } else {
+ if (data->chip == emc1428)
+ val = clamp_val(val, -128000, 127000);
+ else
+ val = clamp_val(val, 0, 255000);
+ regval = DIV_ROUND_CLOSEST(val, 1000);
+ ret = regmap_write(data->regmap, regh, regval);
+ }
+ return ret;
+}
+
+static int emc1403_temp_write(struct thermal_data *data, u32 attr, int channel, long val)
+{
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ return emc1403_set_temp(data, channel, ema1403_temp_map[attr], val);
+ case hwmon_temp_crit_hyst:
+ return emc1403_set_hyst(data, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int emc1403_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct thermal_data *data = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return emc1403_temp_write(data, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t emc1403_temp_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct thermal_data *data = _data;
+
+ if (data->chip == emc1402 && channel > 1)
+ return 0;
+ if (data->chip == emc1403 && channel > 2)
+ return 0;
+ if (data->chip != emc1428 && channel > 3)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_fault:
+ case hwmon_temp_max_hyst:
+ return 0444;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ return 0644;
+ case hwmon_temp_crit_hyst:
+ if (channel == 0)
+ return 0644;
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static umode_t emc1403_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ return emc1403_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info * const emc1403_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT
+ ),
+ NULL
+};
+
+static const struct hwmon_ops emc1403_hwmon_ops = {
+ .is_visible = emc1403_is_visible,
+ .read = emc1403_read,
+ .write = emc1403_write,
+};
+
+static const struct hwmon_chip_info emc1403_chip_info = {
+ .ops = &emc1403_hwmon_ops,
+ .info = emc1403_info,
};
/* Last digit of chip name indicates number of channels */
@@ -444,10 +576,40 @@ static const struct i2c_device_id emc1403_idtable[] = {
{ "emc1422", emc1402 },
{ "emc1423", emc1403 },
{ "emc1424", emc1404 },
+ { "emc1428", emc1428 },
+ { "emc1438", emc1428 },
+ { "emc1442", emc1402 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
+static int emc1403_probe(struct i2c_client *client)
+{
+ struct thermal_data *data;
+ struct device *hwmon_dev;
+ const struct i2c_device_id *id = i2c_match_id(emc1403_idtable, client);
+
+ data = devm_kzalloc(&client->dev, sizeof(struct thermal_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->chip = id->driver_data;
+ data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
+ client->name, data,
+ &emc1403_chip_info,
+ emc1403_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const unsigned short emc1403_address_list[] = {
+ 0x18, 0x1c, 0x29, 0x3c, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
+};
+
static struct i2c_driver sensor_emc1403 = {
.class = I2C_CLASS_HWMON,
.driver = {
diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c
index af7e62685898..da73afc1cd32 100644
--- a/drivers/hwmon/max6621.c
+++ b/drivers/hwmon/max6621.c
@@ -17,8 +17,8 @@
#define MAX6621_DRV_NAME "max6621"
#define MAX6621_TEMP_INPUT_REG_NUM 9
-#define MAX6621_TEMP_INPUT_MIN -127000
-#define MAX6621_TEMP_INPUT_MAX 128000
+#define MAX6621_TEMP_INPUT_MIN -128000
+#define MAX6621_TEMP_INPUT_MAX 127000
#define MAX6621_TEMP_ALERT_CHAN_SHIFT 1
#define MAX6621_TEMP_S0D0_REG 0x00
@@ -239,7 +239,7 @@ max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
if (ret)
return ret;
- *val = (regval >> MAX6621_REG_TEMP_SHIFT) *
+ *val = ((s16)regval >> MAX6621_REG_TEMP_SHIFT) *
1000L;
break;
@@ -254,7 +254,7 @@ max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
if (ret)
return ret;
- *val = regval * 1000L;
+ *val = (s16)regval * 1000L;
break;
case hwmon_temp_crit_alarm:
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index f279dd010b73..c13f4bf73ffc 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -468,7 +468,7 @@ static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
out:
mutex_unlock(&hwspinlock_tree_lock);
- return 0;
+ return ret;
}
static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
index c302072b293a..e1a59b434505 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
@@ -76,7 +76,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
- if (idx < ETM_MAX_SEQ_STATES) {
+ if (idx < ETM_MAX_SEQ_TRANSITIONS) {
reg_csdev->driver_regval = &drvcfg->seq_ctrl[idx];
err = 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 331df260d074..ace8027d7cb7 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -87,7 +87,7 @@ static int etm4_probe_cpu(unsigned int cpu);
static inline bool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n)
{
return (n < drvdata->nr_ss_cmp) &&
- drvdata->nr_pe &&
+ drvdata->nr_pe_cmp &&
(drvdata->config.ss_status[n] & TRCSSCSRn_PC);
}
@@ -527,7 +527,8 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR);
if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR);
@@ -847,8 +848,10 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset);
if (cfg_hash) {
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
- if (ret)
+ if (ret) {
+ etm4_release_trace_id(drvdata);
return ret;
+ }
}
spin_lock(&drvdata->spinlock);
@@ -1376,6 +1379,8 @@ static void etm4_init_arch_data(void *info)
drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up);
/* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */
drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5);
+ if (drvdata->nrseqstate)
+ drvdata->nr_seq_ctrls = ETM_MAX_SEQ_TRANSITIONS;
/* NUMCNTR, bits[30:28] number of counters available for tracing */
drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5);
etm4_cs_lock(drvdata, csa);
@@ -1785,7 +1790,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
if (drvdata->nr_pe_cmp)
state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
@@ -1919,7 +1924,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index a9f19629f3f8..2a96a4c43a63 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -221,7 +221,7 @@ static ssize_t reset_store(struct device *dev,
config->vipcssctlr = 0x0;
/* Disable seq events */
- for (i = 0; i < drvdata->nrseqstate-1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
config->seq_ctrl[i] = 0x0;
config->seq_rst = 0x0;
config->seq_state = 0x0;
@@ -1393,9 +1393,11 @@ static ssize_t seq_idx_store(struct device *dev,
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
+ if (!drvdata->nr_seq_ctrls)
+ return -ENOTSUPP;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val >= drvdata->nrseqstate - 1)
+ if (val >= drvdata->nr_seq_ctrls)
return -EINVAL;
/*
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index d8b8102d790c..dbf489a9d388 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -570,6 +570,7 @@
#define ETM_MAX_NR_PE 8
#define ETMv4_MAX_CNTR 4
#define ETM_MAX_SEQ_STATES 4
+#define ETM_MAX_SEQ_TRANSITIONS 3
#define ETM_MAX_EXT_INP_SEL 4
#define ETM_MAX_EXT_INP 256
#define ETM_MAX_EXT_OUT 4
@@ -825,7 +826,6 @@ struct etmv4_config {
u32 eventctrl1;
u32 stall_ctrl;
u32 ts_ctrl;
- u32 syncfreq;
u32 ccctlr;
u32 bb_ctrl;
u32 vinst_ctrl;
@@ -833,7 +833,8 @@ struct etmv4_config {
u32 vissctlr;
u32 vipcssctlr;
u8 seq_idx;
- u32 seq_ctrl[ETM_MAX_SEQ_STATES];
+ u8 syncfreq;
+ u32 seq_ctrl[ETM_MAX_SEQ_TRANSITIONS];
u32 seq_rst;
u32 seq_state;
u8 cntr_idx;
@@ -885,7 +886,7 @@ struct etmv4_save_state {
u32 trcvissctlr;
u32 trcvipcssctlr;
- u32 trcseqevr[ETM_MAX_SEQ_STATES];
+ u32 trcseqevr[ETM_MAX_SEQ_TRANSITIONS];
u32 trcseqrstevr;
u32 trcseqstr;
u32 trcextinselr;
@@ -938,6 +939,7 @@ struct etmv4_save_state {
* @numcidc: Number of contextID comparators.
* @numvmidc: Number of VMID comparators.
* @nrseqstate: The number of sequencer states that are implemented.
+ * @nr_seq_ctrls: The number of sequence state transition control registers.
* @nr_event: Indicates how many events the trace unit support.
* @nr_resource:The number of resource selection pairs available for tracing.
* @nr_ss_cmp: Number of single-shot comparator controls that are available.
@@ -1005,6 +1007,7 @@ struct etmv4_drvdata {
u8 numextinsel;
u8 numvmidc;
u8 nrseqstate;
+ u8 nr_seq_ctrls;
u8 nr_event;
u8 nr_resource;
u8 nr_ss_cmp;
diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c
index 24a1f7797aeb..7ecd5f50292d 100644
--- a/drivers/hwtracing/ptt/hisi_ptt.c
+++ b/drivers/hwtracing/ptt/hisi_ptt.c
@@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt)
HISI_PTT_WAIT_TRACE_TIMEOUT_US);
}
-static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt)
+static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt)
{
u32 val;
- readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS,
- val, !val, HISI_PTT_RESET_POLL_INTERVAL_US,
- HISI_PTT_RESET_TIMEOUT_US);
+ return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS,
+ val, !val, HISI_PTT_RESET_POLL_INTERVAL_US,
+ HISI_PTT_RESET_TIMEOUT_US);
}
static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt)
@@ -198,14 +198,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
return -EBUSY;
}
- ctrl->started = true;
-
/* Reset the DMA before start tracing */
val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
val |= HISI_PTT_TRACE_CTRL_RST;
writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
- hisi_ptt_wait_dma_reset_done(hisi_ptt);
+ if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) {
+ pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n");
+ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
+ val &= ~HISI_PTT_TRACE_CTRL_RST;
+ writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
+ return -ETIMEDOUT;
+ }
val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
val &= ~HISI_PTT_TRACE_CTRL_RST;
@@ -230,6 +234,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
if (!hisi_ptt->trace_ctrl.is_port)
val |= HISI_PTT_TRACE_CTRL_FILTER_MODE;
+ ctrl->started = true;
+
/* Start the Trace */
val |= HISI_PTT_TRACE_CTRL_EN;
writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 350ccfbe8634..30168990f30e 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -768,7 +768,11 @@ static int ocores_i2c_resume(struct device *dev)
rate = clk_get_rate(i2c->clk) / 1000;
if (rate)
i2c->ip_clock_khz = rate;
- return ocores_init(dev, i2c);
+ ret = ocores_init(dev, i2c);
+ if (ret)
+ clk_disable_unprepare(i2c->clk);
+
+ return ret;
}
static DEFINE_NOIRQ_DEV_PM_OPS(ocores_i2c_pm,
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index f64bcdaae62d..2c414d7a325c 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1808,6 +1808,12 @@ void i2c_del_adapter(struct i2c_adapter *adap)
return;
}
+ /*
+ * This drains any in-flight writers, so all
+ * clients will be caught by i2c_deregister_clients().
+ */
+ device_remove_file(&adap->dev, &dev_attr_new_device);
+
i2c_acpi_remove_space_handler(adap);
i2c_deregister_clients(adap);
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 57ff09f18c37..661590036976 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -433,6 +433,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
return 0;
err_free_priv:
+ of_node_put(priv->adap.dev.of_node);
kfree(priv);
return ret;
}
diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c
index 299abb6dd942..fbbde2f8087a 100644
--- a/drivers/i2c/muxes/i2c-demux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c
@@ -245,6 +245,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL);
if (!props[i].name || !props[i].value) {
err = -ENOMEM;
+ of_node_put(adap_np);
goto err_rollback;
}
props[i].length = 3;
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index c062e94ce221..330f87c65bff 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1648,7 +1648,9 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
if (ret) {
dev_err(&master->dev,
"Failed to add I3C device (err = %d)\n", ret);
+ desc->dev->desc = NULL;
put_device(&desc->dev->dev);
+ desc->dev = NULL;
}
}
}
@@ -2505,11 +2507,12 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
if (!i3cdev->dev)
continue;
- i3cdev->dev->desc = NULL;
- if (device_is_registered(&i3cdev->dev->dev))
+ if (device_is_registered(&i3cdev->dev->dev)) {
+ get_device(&i3cdev->dev->dev);
device_unregister(&i3cdev->dev->dev);
- else
- put_device(&i3cdev->dev->dev);
+ }
+ i3cdev->dev->desc = NULL;
+ put_device(&i3cdev->dev->dev);
i3cdev->dev = NULL;
}
}
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index ccb521bcb73e..1510858038d8 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -829,7 +829,15 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
dw_i3c_master_dequeue_xfer(master, xfer);
- newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0);
+ /*
+ * cmd->rx_len holds the number of addresses ENTDAA left unassigned.
+ * On an empty bus rx_len == maxdevs, so avoid GENMASK(-1, 0).
+ */
+ if (cmd->rx_len >= master->maxdevs)
+ newdevs = 0;
+ else
+ newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0);
+
newdevs &= ~olddevs;
for (pos = 0; pos < master->maxdevs; pos++) {
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 8bceced3c305..10c400da6640 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -583,6 +583,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
static int i3c_hci_init(struct i3c_hci *hci)
{
u32 regval, offset;
+ bool size_in_dwords;
int ret;
/* Validate HCI hardware version */
@@ -606,36 +607,43 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->caps = reg_read(HC_CAPABILITIES);
DBG("caps = %#x", hci->caps);
+ size_in_dwords = hci->version_major < 1 ||
+ (hci->version_major == 1 && hci->version_minor < 1);
+
regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
- hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval);
- dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
- hci->DAT_entries, hci->DAT_entry_size * 4, offset);
+ hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
+ if (size_in_dwords)
+ hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
+ dev_dbg(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
+ hci->DAT_entries, hci->DAT_entry_size, offset);
regval = reg_read(DCT_SECTION);
offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
- hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval);
- dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
- hci->DCT_entries, hci->DCT_entry_size * 4, offset);
+ hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
+ if (size_in_dwords)
+ hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
+ dev_dbg(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
+ hci->DCT_entries, hci->DCT_entry_size, offset);
regval = reg_read(RING_HEADERS_SECTION);
offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
hci->RHS_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
regval = reg_read(PIO_SECTION);
offset = FIELD_GET(PIO_REGS_OFFSET, regval);
hci->PIO_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "PIO section at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "PIO section at offset %#x\n", offset);
regval = reg_read(EXT_CAPS_SECTION);
offset = FIELD_GET(EXT_CAPS_OFFSET, regval);
hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
ret = i3c_hci_parse_ext_caps(hci);
if (ret)
@@ -705,7 +713,7 @@ static int i3c_hci_init(struct i3c_hci *hci)
ret = -EIO;
} else {
hci->io = &mipi_i3c_hci_dma;
- dev_info(&hci->master.dev, "Using DMA\n");
+ dev_dbg(&hci->master.dev, "Using DMA\n");
}
}
@@ -717,7 +725,7 @@ static int i3c_hci_init(struct i3c_hci *hci)
ret = -EIO;
} else {
hci->io = &mipi_i3c_hci_pio;
- dev_info(&hci->master.dev, "Using PIO\n");
+ dev_dbg(&hci->master.dev, "Using PIO\n");
}
}
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 624d00b853a5..2cf4974fffa7 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -216,7 +216,7 @@ static int hci_dma_init(struct i3c_hci *hci)
regval = rhs_reg_read(CONTROL);
nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
- dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
+ dev_dbg(&hci->master.dev, "%d DMA rings available\n", nr_rings);
if (unlikely(nr_rings > 8)) {
dev_err(&hci->master.dev, "number of rings should be <= 8\n");
nr_rings = 8;
@@ -232,7 +232,7 @@ static int hci_dma_init(struct i3c_hci *hci)
for (i = 0; i < rings->total; i++) {
u32 offset = rhs_reg_read(RHn_OFFSET(i));
- dev_info(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
+ dev_dbg(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
ret = -EINVAL;
if (!offset)
goto err_out;
diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
index 2e9b23efdc45..4e5519628178 100644
--- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
+++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
@@ -27,9 +27,9 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
hci->vendor_version_id = readl(base + 0x08);
hci->vendor_product_id = readl(base + 0x0c);
- dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
- dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
- dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
+ dev_dbg(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
+ dev_dbg(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
+ dev_dbg(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
/* ought to go in a table if this grows too much */
switch (hci->vendor_mipi_id) {
@@ -49,7 +49,7 @@ static int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base)
static const char * const functionality[] = {
"(unknown)", "master only", "target only",
"primary/secondary master" };
- dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
+ dev_dbg(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
if (operation_mode & 0x1)
return 0;
dev_err(&hci->master.dev, "only master mode is currently supported\n");
@@ -61,7 +61,7 @@ static int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base)
u32 bus_instance = readl(base + 0x04);
unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance);
- dev_info(&hci->master.dev, "%d bus instances\n", count);
+ dev_dbg(&hci->master.dev, "%d bus instances\n", count);
return 0;
}
@@ -71,8 +71,7 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
unsigned int index;
- dev_info(&hci->master.dev, "transfer mode table has %d entries\n",
- entries);
+ dev_dbg(&hci->master.dev, "transfer mode table has %d entries\n", entries);
base += 4; /* skip header */
for (index = 0; index < entries; index++) {
u32 mode_entry = readl(base);
@@ -94,19 +93,19 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
base += 4; /* skip header */
- dev_info(&hci->master.dev, "available data rates:\n");
+ dev_dbg(&hci->master.dev, "available data rates:\n");
for (index = 0; index < entries; index++) {
rate_entry = readl(base);
DBG("entry %d: 0x%08x", index, rate_entry);
rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
- dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n",
- rate_id,
- mode_id == XFERRATE_MODE_I3C ? "I3C" :
- mode_id == XFERRATE_MODE_I2C ? "I2C" :
- "unknown mode",
- rate);
+ dev_dbg(&hci->master.dev, "rate %d for %s = %d kHz\n",
+ rate_id,
+ mode_id == XFERRATE_MODE_I3C ? "I3C" :
+ mode_id == XFERRATE_MODE_I2C ? "I2C" :
+ "unknown mode",
+ rate);
base += 4;
}
@@ -120,8 +119,8 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
u32 autocmd_ext_config = readl(base + 0x08);
unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config);
- dev_info(&hci->master.dev, "%d/%d active auto-command entries\n",
- count, max_count);
+ dev_dbg(&hci->master.dev, "%d/%d active auto-command entries\n",
+ count, max_count);
/* remember auto-command register location for later use */
hci->AUTOCMD_regs = base;
return 0;
@@ -129,46 +128,46 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
static int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "debug registers present\n");
+ dev_dbg(&hci->master.dev, "debug registers present\n");
hci->DEBUG_regs = base;
return 0;
}
static int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "scheduled commands available\n");
+ dev_dbg(&hci->master.dev, "scheduled commands available\n");
/* hci->schedcmd_regs = base; */
return 0;
}
static int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Non-Current Master support available\n");
+ dev_dbg(&hci->master.dev, "Non-Current Master support available\n");
/* hci->NCM_regs = base; */
return 0;
}
static int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "CCC Response Configuration available\n");
+ dev_dbg(&hci->master.dev, "CCC Response Configuration available\n");
return 0;
}
static int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Global DAT available\n");
+ dev_dbg(&hci->master.dev, "Global DAT available\n");
return 0;
}
static int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Master Multi-Lane support available\n");
+ dev_dbg(&hci->master.dev, "Master Multi-Lane support available\n");
return 0;
}
static int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "NCM Multi-Lane support available\n");
+ dev_dbg(&hci->master.dev, "NCM Multi-Lane support available\n");
return 0;
}
@@ -201,7 +200,7 @@ static const struct hci_ext_caps ext_capabilities[] = {
static int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base)
{
hci->vendor_data = (__force void *)base;
- dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4));
+ dev_dbg(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1 * 4));
/* reset the FPGA */
writel(0xdeadbeef, base + 1*4);
return 0;
@@ -239,9 +238,8 @@ static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base,
}
if (!vendor_cap_entry) {
- dev_notice(&hci->master.dev,
- "unknown ext_cap 0x%02x for vendor 0x%02x\n",
- cap_id, hci->vendor_mipi_id);
+ dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x for vendor 0x%02x\n",
+ cap_id, hci->vendor_mipi_id);
return 0;
}
if (cap_length < vendor_cap_entry->min_length) {
@@ -293,8 +291,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
}
}
if (!cap_entry) {
- dev_notice(&hci->master.dev,
- "unknown ext_cap 0x%02x\n", cap_id);
+ dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x\n", cap_id);
} else if (cap_length < cap_entry->min_length) {
dev_err(&hci->master.dev,
"ext_cap 0x%02x has size %d (expecting >= %d)\n",
diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
index d0272aa93599..d8ef51cea6e2 100644
--- a/drivers/i3c/master/mipi-i3c-hci/pio.c
+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
@@ -136,27 +136,14 @@ struct hci_pio_data {
u32 enabled_irqs;
};
-static int hci_pio_init(struct i3c_hci *hci)
+static void __hci_pio_init(struct i3c_hci *hci, u32 *size_val_ptr)
{
- struct hci_pio_data *pio;
u32 val, size_val, rx_thresh, tx_thresh, ibi_val;
-
- pio = kzalloc(sizeof(*pio), GFP_KERNEL);
- if (!pio)
- return -ENOMEM;
-
- hci->io_data = pio;
- spin_lock_init(&pio->lock);
+ struct hci_pio_data *pio = hci->io_data;
size_val = pio_reg_read(QUEUE_SIZE);
- dev_info(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
- FIELD_GET(CR_QUEUE_SIZE, size_val));
- dev_info(&hci->master.dev, "IBI FIFO = %ld bytes\n",
- 4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
- dev_info(&hci->master.dev, "RX data FIFO = %d bytes\n",
- 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
- dev_info(&hci->master.dev, "TX data FIFO = %d bytes\n",
- 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
+ if (size_val_ptr)
+ *size_val_ptr = size_val;
/*
* Let's initialize data thresholds to half of the actual FIFO size.
@@ -200,8 +187,35 @@ static int hci_pio_init(struct i3c_hci *hci)
pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
pio_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
- /* Always accept error interrupts (will be activated on first xfer) */
- pio->enabled_irqs = STAT_ALL_ERRORS;
+ /*
+ * Always accept error interrupts and IBI threshold interrupt
+ * (will be activated on first xfer).
+ */
+ pio->enabled_irqs = STAT_ALL_ERRORS | STAT_IBI_STATUS_THLD;
+}
+
+static int hci_pio_init(struct i3c_hci *hci)
+{
+ struct hci_pio_data *pio;
+ u32 size_val;
+
+ pio = devm_kzalloc(hci->master.dev.parent, sizeof(*pio), GFP_KERNEL);
+ if (!pio)
+ return -ENOMEM;
+
+ hci->io_data = pio;
+ spin_lock_init(&pio->lock);
+
+ __hci_pio_init(hci, &size_val);
+
+ dev_dbg(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
+ FIELD_GET(CR_QUEUE_SIZE, size_val));
+ dev_dbg(&hci->master.dev, "IBI FIFO = %ld bytes\n",
+ 4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
+ dev_dbg(&hci->master.dev, "RX data FIFO = %d bytes\n",
+ 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
+ dev_dbg(&hci->master.dev, "TX data FIFO = %d bytes\n",
+ 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
return 0;
}
@@ -219,8 +233,6 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
BUG_ON(pio->curr_rx);
BUG_ON(pio->curr_tx);
BUG_ON(pio->curr_resp);
- kfree(pio);
- hci->io_data = NULL;
}
}
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 94792f3559a0..c3c2c5518865 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -380,14 +380,22 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
buf = slot->data;
while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) &&
- slot->len < SVC_I3C_FIFO_SIZE) {
+ slot->len < dev->ibi->max_payload_len) {
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
+ count = min(count, dev->ibi->max_payload_len - slot->len);
readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
slot->len += count;
buf += count;
}
+ /*
+ * The device may have sent more than the requested payload. Drop the
+ * extra bytes so they do not leak into the next transfer.
+ */
+ if (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)))
+ writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL);
+
master->ibi.tbq_slot = slot;
return 0;
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
index fa98623de579..92aa0d94341d 100644
--- a/drivers/iio/accel/dmard09.c
+++ b/drivers/iio/accel/dmard09.c
@@ -8,6 +8,7 @@
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/units.h>
#include <linux/iio/iio.h>
#define DMARD09_DRV_NAME "dmard09"
@@ -79,6 +80,12 @@ static int dmard09_read_raw(struct iio_dev *indio_dev,
*val = accel;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ /* 1 g / 32 LSB, in m/s^2 */
+ *val2 = IIO_G_TO_M_S_2(NANO / 32);
+
+ return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c
index fb15bb216019..ef279e5635f2 100644
--- a/drivers/iio/chemical/atlas-sensor.c
+++ b/drivers/iio/chemical/atlas-sensor.c
@@ -13,7 +13,6 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/irq.h>
-#include <linux/irq_work.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
@@ -89,7 +88,6 @@ struct atlas_data {
struct iio_trigger *trig;
struct atlas_device *chip;
struct regmap *regmap;
- struct irq_work work;
unsigned int interrupt_enabled;
/* 96-bit data + 32-bit pad + 64-bit timestamp */
__be32 buffer[6] __aligned(8);
@@ -414,7 +412,11 @@ static int atlas_buffer_postenable(struct iio_dev *indio_dev)
if (ret)
return ret;
- return atlas_set_interrupt(data, true);
+ ret = atlas_set_interrupt(data, true);
+ if (ret)
+ pm_runtime_put_autosuspend(&data->client->dev);
+
+ return ret;
}
static int atlas_buffer_predisable(struct iio_dev *indio_dev)
@@ -439,13 +441,6 @@ static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = {
.predisable = atlas_buffer_predisable,
};
-static void atlas_work_handler(struct irq_work *work)
-{
- struct atlas_data *data = container_of(work, struct atlas_data, work);
-
- iio_trigger_poll(data->trig);
-}
-
static irqreturn_t atlas_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
@@ -471,7 +466,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private)
struct iio_dev *indio_dev = private;
struct atlas_data *data = iio_priv(indio_dev);
- irq_work_queue(&data->work);
+ iio_trigger_poll_nested(data->trig);
return IRQ_HANDLED;
}
@@ -673,8 +668,6 @@ static int atlas_probe(struct i2c_client *client)
goto unregister_trigger;
}
- init_irq_work(&data->work, atlas_work_handler);
-
if (client->irq > 0) {
/* interrupt pin toggles on new conversion */
ret = devm_request_threaded_irq(&client->dev, client->irq,
diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c
index b509cff9ce37..52229acf4921 100644
--- a/drivers/iio/chemical/sgp30.c
+++ b/drivers/iio/chemical/sgp30.c
@@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client)
data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
"%s-iaq", data->client->name);
+ if (IS_ERR(data->iaq_thread))
+ return dev_err_probe(dev, PTR_ERR(data->iaq_thread),
+ "failed to start IAQ thread\n");
return 0;
}
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
index ae53baccec91..61c35dd8f092 100644
--- a/drivers/iio/dac/m62332.c
+++ b/drivers/iio/dac/m62332.c
@@ -32,6 +32,7 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
{
struct m62332_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
+ bool enabling, disabling;
u8 outbuf[2];
int res;
@@ -43,7 +44,10 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
mutex_lock(&data->mutex);
- if (val) {
+ enabling = val && !data->raw[channel];
+ disabling = !val && data->raw[channel];
+
+ if (enabling) {
res = regulator_enable(data->vcc);
if (res)
goto out;
@@ -52,14 +56,17 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf));
if (res >= 0 && res != ARRAY_SIZE(outbuf))
res = -EIO;
- if (res < 0)
+ if (res < 0) {
+ if (enabling)
+ regulator_disable(data->vcc);
goto out;
+ }
- data->raw[channel] = val;
-
- if (!val)
+ if (disabling)
regulator_disable(data->vcc);
+ data->raw[channel] = val;
+
mutex_unlock(&data->mutex);
return 0;
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
index 695269359621..755bbe0ab26d 100644
--- a/drivers/iio/gyro/mpu3050-core.c
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -356,7 +356,7 @@ static int mpu3050_read_raw(struct iio_dev *indio_dev,
goto out_read_raw_unlock;
}
- *val = be16_to_cpu(raw_val);
+ *val = (s16)be16_to_cpu(raw_val);
ret = IIO_VAL_INT;
goto out_read_raw_unlock;
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index 9df85b3999fa..f708be3389bc 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -370,7 +370,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
cm32181->calibscale = val;
- return val;
+ return 0;
case IIO_CHAN_INFO_INT_TIME:
ret = cm32181_write_als_it(cm32181, val2);
return ret;
diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c
index 7e73fa2f9a81..4c9094937823 100644
--- a/drivers/iio/light/gp2ap002.c
+++ b/drivers/iio/light/gp2ap002.c
@@ -343,6 +343,10 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev,
int state)
{
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ int ret;
+
+ if (state == gp2ap002->enabled)
+ return 0;
if (state) {
/*
@@ -350,14 +354,17 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev,
* already) and reintialize the sensor by using runtime_pm
* callbacks.
*/
- pm_runtime_get_sync(gp2ap002->dev);
- gp2ap002->enabled = true;
+ ret = pm_runtime_resume_and_get(gp2ap002->dev);
+ if (ret)
+ return ret;
+
} else {
pm_runtime_mark_last_busy(gp2ap002->dev);
pm_runtime_put_autosuspend(gp2ap002->dev);
- gp2ap002->enabled = false;
}
+ gp2ap002->enabled = state;
+
return 0;
}
@@ -644,6 +651,7 @@ static int gp2ap002_runtime_suspend(struct device *dev)
/* Disable chip and IRQ, everything off */
ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
if (ret) {
+ enable_irq(gp2ap002->irq);
dev_err(gp2ap002->dev, "error setting up operation mode\n");
return ret;
}
@@ -671,7 +679,7 @@ static int gp2ap002_runtime_resume(struct device *dev)
ret = regulator_enable(gp2ap002->vio);
if (ret) {
dev_err(dev, "failed to enable VIO regulator in resume path\n");
- return ret;
+ goto out_disable_vdd;
}
msleep(20);
@@ -679,13 +687,19 @@ static int gp2ap002_runtime_resume(struct device *dev)
ret = gp2ap002_init(gp2ap002);
if (ret) {
dev_err(dev, "re-initialization failed\n");
- return ret;
+ goto out_disable_vio;
}
/* Re-activate the IRQ */
enable_irq(gp2ap002->irq);
return 0;
+
+out_disable_vio:
+ regulator_disable(gp2ap002->vio);
+out_disable_vdd:
+ regulator_disable(gp2ap002->vdd);
+ return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend,
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index 5694683389be..90099e467b70 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -414,7 +414,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- return ret;
+ return 0;
}
static int isl29028_read_raw(struct iio_dev *indio_dev,
diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c
index 502946bf9f94..9136df66b39e 100644
--- a/drivers/iio/light/opt4001.c
+++ b/drivers/iio/light/opt4001.c
@@ -39,7 +39,7 @@
#define OPT4001_CTRL_OPER_MODE_MASK GENMASK(5, 4)
#define OPT4001_CTRL_LATCH_MASK GENMASK(3, 3)
#define OPT4001_CTRL_INT_POL_MASK GENMASK(2, 2)
-#define OPT4001_CTRL_FAULT_COUNT GENMASK(0, 1)
+#define OPT4001_CTRL_FAULT_COUNT_MASK GENMASK(1, 0)
/* OPT4001 constants */
#define OPT4001_DEVICE_ID_VAL 0x121
@@ -173,6 +173,7 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
u8 crc;
u8 calc_crc;
u64 lux_raw;
+ u32 rem;
int ret;
ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1);
@@ -199,8 +200,8 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
lux_raw = lux_raw << exp;
lux_raw = lux_raw * chip->chip_info->mul;
- *val = div_u64_rem(lux_raw, chip->chip_info->div, val2);
- *val2 = *val2 * 100;
+ *val = div_u64_rem(lux_raw, chip->chip_info->div, &rem);
+ *val2 = rem * 100;
return IIO_VAL_INT_PLUS_NANO;
}
@@ -222,33 +223,14 @@ static int opt4001_set_conf(struct opt4001_chip *chip)
return ret;
}
-static int opt4001_power_down(struct opt4001_chip *chip)
-{
- struct device *dev = &chip->client->dev;
- int ret;
- unsigned int reg;
-
- ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, ®);
- if (ret) {
- dev_err(dev, "Failed to read configuration\n");
- return ret;
- }
-
- /* MODE_OFF is 0x0 so just set bits to 0 */
- reg &= ~OPT4001_CTRL_OPER_MODE_MASK;
-
- ret = regmap_write(chip->regmap, OPT4001_CTRL, reg);
- if (ret)
- dev_err(dev, "Failed to set configuration to power down\n");
-
- return ret;
-}
-
static void opt4001_chip_off_action(void *data)
{
struct opt4001_chip *chip = data;
+ int ret;
- opt4001_power_down(chip);
+ ret = regmap_clear_bits(chip->regmap, OPT4001_CTRL, OPT4001_CTRL_OPER_MODE_MASK);
+ if (ret)
+ dev_err(&chip->client->dev, "Failed to power down\n");
}
static const struct iio_chan_spec opt4001_channels[] = {
@@ -287,6 +269,9 @@ static int opt4001_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
+ if (val)
+ return -EINVAL;
+
int_time = opt4001_als_time_to_index(val2);
if (int_time < 0)
return int_time;
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index 02ad11611b9c..dfcb8228ada1 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -800,7 +800,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- return ret;
+ return 0;
}
static const struct iio_info tsl2583_info = {
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index cab468a82b61..fffed029c24d 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -1263,7 +1263,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev,
}
break;
case IIO_CHAN_INFO_CALIBSCALE:
- if (chan->type == IIO_LIGHT)
+ if (chan->type == IIO_INTENSITY)
*val = tsl2772_als_gain[chip->settings.als_gain];
else
*val = tsl2772_prox_gain[chip->settings.prox_gain];
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index f124062af383..0d2aca1d36ae 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -337,12 +337,6 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
rot_state->callbacks.send_event = dev_rot_proc_event;
rot_state->callbacks.capture_sample = dev_rot_capture_sample;
rot_state->callbacks.pdev = pdev;
@@ -350,13 +344,19 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
&rot_state->callbacks);
if (ret) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return 0;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
return ret;
@@ -369,8 +369,8 @@ static int hid_dev_rot_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct dev_rot_state *rot_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
return 0;
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index d0a516d56da4..1ad7dc907057 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -830,7 +830,6 @@ static const struct iio_info dps310_info = {
static int dps310_probe(struct i2c_client *client)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct dps310_data *data;
struct iio_dev *iio;
int rc;
@@ -843,7 +842,7 @@ static int dps310_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->lock);
- iio->name = id->name;
+ iio->name = DPS310_DEV_NAME;
iio->channels = dps310_channels;
iio->num_channels = ARRAY_SIZE(dps310_channels);
iio->info = &dps310_info;
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index e3eb154feb5f..1d12ecee34e5 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -205,9 +205,9 @@ int mpl115_probe(struct device *dev, const char *name,
if (data->shutdown) {
/* Enable runtime PM */
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
/*
* As the device takes 3 ms to come up with a fresh
@@ -217,7 +217,10 @@ int mpl115_probe(struct device *dev, const char *name,
*/
pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_use_autosuspend(dev);
- pm_runtime_put(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
dev_dbg(dev, "low-power mode enabled");
} else
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index faf2f806ce80..309fbf31b4a3 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -335,6 +335,7 @@ static int srf04_probe(struct platform_device *pdev)
if (ret) {
dev_err(data->dev, "pm_runtime_set_active: %d\n", ret);
iio_device_unregister(indio_dev);
+ return ret;
}
pm_runtime_enable(data->dev);
diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index d40f235af1d4..ba735d758cb4 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -243,7 +243,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
if (ret)
goto error_remove_trigger;
- ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+ ret = iio_device_register(indio_dev);
if (ret)
goto error_remove_callback;
@@ -263,6 +263,7 @@ static int hid_temperature_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct temperature_state *temp_st = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 87164dd745ec..286d55f517c5 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -3501,10 +3501,12 @@ static void addr_handler(int status, struct sockaddr *src_addr,
memcpy(addr, src_addr, rdma_addr_size(src_addr));
if (!status && !id_priv->cma_dev) {
status = cma_acquire_dev_by_src_ip(id_priv);
- if (status)
+ if (status) {
pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to acquire device. status %d\n",
status);
- rdma_restrack_add(&id_priv->res);
+ } else {
+ rdma_restrack_add(&id_priv->res);
+ }
} else if (status) {
pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to resolve IP. status %d\n", status);
}
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h
index 149dacf5b64d..a7bcf13ada64 100644
--- a/drivers/infiniband/core/core_priv.h
+++ b/drivers/infiniband/core/core_priv.h
@@ -376,4 +376,5 @@ void rdma_umap_priv_init(struct rdma_umap_priv *priv,
void ib_cq_pool_cleanup(struct ib_device *dev);
+bool rdma_nl_get_privileged_qkey(void);
#endif /* _CORE_PRIV_H */
diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c
index d41af5ab6403..8cbca897fc53 100644
--- a/drivers/infiniband/core/counters.c
+++ b/drivers/infiniband/core/counters.c
@@ -217,7 +217,6 @@ static void rdma_counter_free(struct rdma_counter *counter)
mutex_unlock(&port_counter->lock);
- rdma_restrack_del(&counter->res);
rdma_free_hw_stats_struct(counter->stats);
kfree(counter);
}
@@ -312,6 +311,7 @@ static void counter_release(struct kref *kref)
counter = container_of(kref, struct rdma_counter, kref);
counter_history_stat_update(counter);
+ rdma_restrack_del(&counter->res);
counter->device->ops.counter_dealloc(counter);
rdma_counter_free(counter);
}
@@ -472,7 +472,8 @@ static struct rdma_counter *rdma_get_counter_by_id(struct ib_device *dev,
return NULL;
counter = container_of(res, struct rdma_counter, res);
- kref_get(&counter->kref);
+ if (!kref_get_unless_zero(&counter->kref))
+ counter = NULL;
rdma_restrack_put(res);
return counter;
diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c
index a70876a0a231..4ea0dfcff95e 100644
--- a/drivers/infiniband/core/cq.c
+++ b/drivers/infiniband/core/cq.c
@@ -317,13 +317,19 @@ EXPORT_SYMBOL(__ib_alloc_cq_any);
*/
void ib_free_cq(struct ib_cq *cq)
{
- int ret;
+ int ret = 0;
if (WARN_ON_ONCE(atomic_read(&cq->usecnt)))
return;
if (WARN_ON_ONCE(cq->cqe_used))
return;
+ rdma_restrack_del(&cq->res);
+ if (cq->device->ops.pre_destroy_cq) {
+ ret = cq->device->ops.pre_destroy_cq(cq);
+ WARN_ONCE(ret, "Disable of kernel CQ shouldn't fail");
+ }
+
switch (cq->poll_ctx) {
case IB_POLL_DIRECT:
break;
@@ -340,9 +346,11 @@ void ib_free_cq(struct ib_cq *cq)
rdma_dim_destroy(cq);
trace_cq_free(cq);
- ret = cq->device->ops.destroy_cq(cq, NULL);
+ if (cq->device->ops.post_destroy_cq)
+ cq->device->ops.post_destroy_cq(cq);
+ else
+ ret = cq->device->ops.destroy_cq(cq, NULL);
WARN_ONCE(ret, "Destroy of kernel CQ shouldn't fail");
- rdma_restrack_del(&cq->res);
kfree(cq->wc);
kfree(cq);
}
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 1a241864b7b5..958ca2513623 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -2730,8 +2730,10 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, modify_srq);
SET_DEVICE_OP(dev_ops, modify_wq);
SET_DEVICE_OP(dev_ops, peek_cq);
+ SET_DEVICE_OP(dev_ops, pre_destroy_cq);
SET_DEVICE_OP(dev_ops, poll_cq);
SET_DEVICE_OP(dev_ops, port_groups);
+ SET_DEVICE_OP(dev_ops, post_destroy_cq);
SET_DEVICE_OP(dev_ops, post_recv);
SET_DEVICE_OP(dev_ops, post_send);
SET_DEVICE_OP(dev_ops, post_srq_recv);
@@ -2913,6 +2915,7 @@ static void __exit ib_core_cleanup(void)
/* Make sure that any pending umem accounting work is done. */
destroy_workqueue(ib_wq);
destroy_workqueue(ib_unreg_wq);
+ rcu_barrier();
WARN_ON(!xa_empty(&clients));
WARN_ON(!xa_empty(&devices));
}
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index a94723a12bb4..7607aee5994e 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -43,6 +43,13 @@
#include "restrack.h"
#include "uverbs.h"
+/*
+ * This determines whether a non-privileged user is allowed to specify a
+ * controlled QKEY or not, when true non-privileged user is allowed to specify
+ * a controlled QKEY.
+ */
+static bool privileged_qkey;
+
typedef int (*res_fill_func_t)(struct sk_buff*, bool,
struct rdma_restrack_entry*, uint32_t);
@@ -130,6 +137,8 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]= { .type = NLA_NUL_STRING,
.len = RDMA_NLDEV_ATTR_EMPTY_STRING },
[RDMA_NLDEV_ATTR_RES_TYPE] = { .type = NLA_U8 },
+ [RDMA_NLDEV_ATTR_RES_SUBTYPE] = { .type = NLA_NUL_STRING,
+ .len = RDMA_NLDEV_ATTR_EMPTY_STRING },
[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]= { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_RES_USECNT] = { .type = NLA_U64 },
[RDMA_NLDEV_ATTR_RES_SRQ] = { .type = NLA_NESTED },
@@ -156,6 +165,8 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK] = { .type = NLA_U8 },
[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC] = { .type = NLA_U8 },
+ [RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE] = { .type = NLA_U8 },
+ [RDMA_NLDEV_ATTR_DRIVER_DETAILS] = { .type = NLA_U8 },
};
static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
@@ -237,6 +248,12 @@ int rdma_nl_put_driver_u64_hex(struct sk_buff *msg, const char *name, u64 value)
}
EXPORT_SYMBOL(rdma_nl_put_driver_u64_hex);
+bool rdma_nl_get_privileged_qkey(void)
+{
+ return privileged_qkey || capable(CAP_NET_RAW);
+}
+EXPORT_SYMBOL(rdma_nl_get_privileged_qkey);
+
static int fill_nldev_handle(struct sk_buff *msg, struct ib_device *device)
{
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
@@ -386,7 +403,8 @@ static int fill_res_info_entry(struct sk_buff *msg,
return -EMSGSIZE;
}
-static int fill_res_info(struct sk_buff *msg, struct ib_device *device)
+static int fill_res_info(struct sk_buff *msg, struct ib_device *device,
+ bool show_details)
{
static const char * const names[RDMA_RESTRACK_MAX] = {
[RDMA_RESTRACK_PD] = "pd",
@@ -411,7 +429,7 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device)
for (i = 0; i < RDMA_RESTRACK_MAX; i++) {
if (!names[i])
continue;
- curr = rdma_restrack_count(device, i);
+ curr = rdma_restrack_count(device, i, show_details);
ret = fill_res_info_entry(msg, names[i], curr);
if (ret)
goto err;
@@ -644,7 +662,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_mr *mr = container_of(res, struct ib_mr, res);
- struct ib_device *dev = mr->pd->device;
+ struct ib_device *dev = mr->device;
if (has_cap_net_admin) {
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey))
@@ -660,9 +678,12 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
return -EMSGSIZE;
- if (!rdma_is_kernel_res(res) &&
- nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id))
- return -EMSGSIZE;
+ if (!rdma_is_kernel_res(res)) {
+ struct ib_pd *pd = READ_ONCE(mr->pd);
+
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, pd->res.id))
+ return -EMSGSIZE;
+ }
if (fill_res_name_pid(msg, res))
return -EMSGSIZE;
@@ -676,7 +697,7 @@ static int fill_res_mr_raw_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_mr *mr = container_of(res, struct ib_mr, res);
- struct ib_device *dev = mr->pd->device;
+ struct ib_device *dev = mr->device;
if (!dev->ops.fill_res_mr_entry_raw)
return -EINVAL;
@@ -948,7 +969,7 @@ static int fill_stat_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_mr *mr = container_of(res, struct ib_mr, res);
- struct ib_device *dev = mr->pd->device;
+ struct ib_device *dev = mr->device;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
goto err;
@@ -1274,6 +1295,7 @@ static int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+ bool show_details = false;
struct ib_device *device;
struct sk_buff *msg;
u32 index;
@@ -1289,6 +1311,9 @@ static int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!device)
return -EINVAL;
+ if (tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS])
+ show_details = nla_get_u8(tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg) {
ret = -ENOMEM;
@@ -1303,7 +1328,7 @@ static int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
goto err_free;
}
- ret = fill_res_info(msg, device);
+ ret = fill_res_info(msg, device, show_details);
if (ret)
goto err_free;
@@ -1333,7 +1358,7 @@ static int _nldev_res_get_dumpit(struct ib_device *device,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET),
0, NLM_F_MULTI);
- if (!nlh || fill_res_info(skb, device)) {
+ if (!nlh || fill_res_info(skb, device, false)) {
nlmsg_cancel(skb, nlh);
goto out;
}
@@ -1504,6 +1529,7 @@ static int res_get_common_dumpit(struct sk_buff *skb,
struct rdma_restrack_entry *res;
struct rdma_restrack_root *rt;
int err, ret = 0, idx = 0;
+ bool show_details = false;
struct nlattr *table_attr;
struct nlattr *entry_attr;
struct ib_device *device;
@@ -1532,6 +1558,9 @@ static int res_get_common_dumpit(struct sk_buff *skb,
if (!device)
return -EINVAL;
+ if (tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS])
+ show_details = nla_get_u8(tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]);
+
/*
* If no PORT_INDEX is supplied, we will return all QPs from that device
*/
@@ -1569,6 +1598,9 @@ static int res_get_common_dumpit(struct sk_buff *skb,
* objects.
*/
xa_for_each(&rt->xa, id, res) {
+ if (xa_get_mark(&rt->xa, res->id, RESTRACK_DD) && !show_details)
+ goto next;
+
if (idx < start || !rdma_restrack_get(res))
goto next;
@@ -1883,6 +1915,12 @@ static int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
}
+ err = nla_put_u8(msg, RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE,
+ (u8)privileged_qkey);
+ if (err) {
+ nlmsg_free(msg);
+ return err;
+ }
/*
* Copy-on-fork is supported.
* See commits:
@@ -1899,18 +1937,11 @@ static int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
}
-static int nldev_set_sys_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
- struct netlink_ext_ack *extack)
+static int nldev_set_sys_set_netns_doit(struct nlattr *tb[])
{
- struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
u8 enable;
int err;
- err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
- nldev_policy, extack);
- if (err || !tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE])
- return -EINVAL;
-
enable = nla_get_u8(tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]);
/* Only 0 and 1 are supported */
if (enable > 1)
@@ -1920,6 +1951,40 @@ static int nldev_set_sys_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
}
+static int nldev_set_sys_set_pqkey_doit(struct nlattr *tb[])
+{
+ u8 enable;
+
+ enable = nla_get_u8(tb[RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE]);
+ /* Only 0 and 1 are supported */
+ if (enable > 1)
+ return -EINVAL;
+
+ privileged_qkey = enable;
+ return 0;
+}
+
+static int nldev_set_sys_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+ int err;
+
+ err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+ nldev_policy, extack);
+ if (err)
+ return -EINVAL;
+
+ if (tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE])
+ return nldev_set_sys_set_netns_doit(tb);
+
+ if (tb[RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE])
+ return nldev_set_sys_set_pqkey_doit(tb);
+
+ return -EINVAL;
+}
+
+
static int nldev_stat_set_mode_doit(struct sk_buff *msg,
struct netlink_ext_ack *extack,
struct nlattr *tb[],
@@ -1990,6 +2055,11 @@ static int nldev_stat_set_counter_dynamic_doit(struct nlattr *tb[],
nla_for_each_nested(entry_attr, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS],
rem) {
+ if (nla_len(entry_attr) != sizeof(u32)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
index = nla_get_u32(entry_attr);
if ((index >= stats->num_counters) ||
!(stats->descs[index].flags & IB_STAT_FLAG_OPTIONAL)) {
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index 438ed3588175..c03042dc1ca5 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -59,8 +59,10 @@ void rdma_restrack_clean(struct ib_device *dev)
* rdma_restrack_count() - the current usage of specific object
* @dev: IB device
* @type: actual type of object to operate
+ * @show_details: count driver specific objects
*/
-int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type)
+int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
+ bool show_details)
{
struct rdma_restrack_root *rt = &dev->res[type];
struct rdma_restrack_entry *e;
@@ -68,8 +70,13 @@ int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type)
u32 cnt = 0;
xa_lock(&rt->xa);
- xas_for_each(&xas, e, U32_MAX)
+ xas_for_each(&xas, e, U32_MAX) {
+ if (xa_is_zero(e))
+ continue;
+ if (xa_get_mark(&rt->xa, e->id, RESTRACK_DD) && !show_details)
+ continue;
cnt++;
+ }
xa_unlock(&rt->xa);
return cnt;
}
@@ -120,6 +127,46 @@ static void rdma_restrack_attach_task(struct rdma_restrack_entry *res,
res->user = true;
}
+static struct rdma_restrack_root *res_to_rt(struct rdma_restrack_entry *res)
+{
+ struct ib_device *dev = res_to_dev(res);
+
+ if (WARN_ON(!dev))
+ return NULL;
+
+ return &dev->res[res->type];
+}
+
+static void restrack_drain_res(struct rdma_restrack_root *rt,
+ struct rdma_restrack_entry *res)
+{
+ if (rt) {
+ struct rdma_restrack_entry *old;
+
+ old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY,
+ GFP_KERNEL);
+ WARN_ON(old != res);
+ }
+
+ rdma_restrack_put(res);
+ wait_for_completion(&res->comp);
+}
+
+static void restrack_restore_res(struct rdma_restrack_root *rt,
+ struct rdma_restrack_entry *res)
+{
+ reinit_completion(&res->comp);
+ kref_init(&res->kref);
+
+ if (rt) {
+ struct rdma_restrack_entry *old;
+
+ old = xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res,
+ GFP_KERNEL);
+ WARN_ON(old);
+ }
+}
+
/**
* rdma_restrack_set_name() - set the task for this resource
* @res: resource entry
@@ -168,22 +215,23 @@ void rdma_restrack_new(struct rdma_restrack_entry *res,
EXPORT_SYMBOL(rdma_restrack_new);
/**
- * rdma_restrack_add() - add object to the reource tracking database
+ * rdma_restrack_add() - add object to the resource tracking database.
+ * If this resource reuses an ID of a resource that was already destroyed
+ * after calling rdma_restrack_begin() but didn't yet call
+ * rdma_restrack_commit_del() it can result in an untracked QP.
* @res: resource entry
*/
void rdma_restrack_add(struct rdma_restrack_entry *res)
{
- struct ib_device *dev = res_to_dev(res);
struct rdma_restrack_root *rt;
int ret = 0;
- if (!dev)
- return;
-
if (res->no_track)
goto out;
- rt = &dev->res[res->type];
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
if (res->type == RDMA_RESTRACK_QP) {
/* Special case to ensure that LQPN points to right QP */
@@ -198,6 +246,9 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)
ret = xa_insert(&rt->xa, res->id, res, GFP_KERNEL);
if (ret)
res->id = 0;
+
+ if (qp->qp_type >= IB_QPT_DRIVER)
+ xa_set_mark(&rt->xa, res->id, RESTRACK_DD);
} else if (res->type == RDMA_RESTRACK_COUNTER) {
/* Special case to ensure that cntn points to right counter */
struct rdma_counter *counter;
@@ -217,6 +268,28 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)
}
EXPORT_SYMBOL(rdma_restrack_add);
+/**
+ * rdma_restrack_abort_del() - re-add object to the resource tracking database
+ * it can only be used after rdma_restrack_begin_del().
+ * @res: resource entry
+ */
+void rdma_restrack_abort_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt = NULL;
+
+ if (!res->valid)
+ return;
+
+ if (!res->no_track) {
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+ }
+
+ restrack_restore_res(rt, res);
+}
+EXPORT_SYMBOL(rdma_restrack_abort_del);
+
int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
{
return kref_get_unless_zero(&res->kref);
@@ -253,7 +326,7 @@ static void restrack_release(struct kref *kref)
struct rdma_restrack_entry *res;
res = container_of(kref, struct rdma_restrack_entry, kref);
- if (res->task) {
+ if (res->task && !res->valid) {
put_task_struct(res->task);
res->task = NULL;
}
@@ -267,14 +340,43 @@ int rdma_restrack_put(struct rdma_restrack_entry *res)
EXPORT_SYMBOL(rdma_restrack_put);
/**
- * rdma_restrack_del() - delete object from the reource tracking database
+ * rdma_restrack_sync() - Fence concurrent netlink dumps on an entry
+ * @res: resource entry
+ *
+ * After this returns any concurrent netlink dump threads will see the current
+ * value of the object. This is useful if the object has to be changed and there
+ * is not locking to protect the nl side. Eg for mr->pd. This effectively
+ * destroys the object from a kref/xarray perspective and then immediately
+ * restores it. The kref is acting like a lock to barrier concurrent nl threads.
+ * Callers must ensure rdma_restrack_del() is not concurrently called.
+ */
+void rdma_restrack_sync(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt;
+
+ if (!res->valid || res->no_track)
+ return;
+
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+
+ if (WARN_ON(xa_get_mark(&rt->xa, res->id, RESTRACK_DD)))
+ return;
+
+ restrack_drain_res(rt, res);
+ restrack_restore_res(rt, res);
+}
+EXPORT_SYMBOL(rdma_restrack_sync);
+
+/**
+ * rdma_restrack_del() - delete object from the resource tracking database
* @res: resource entry
*/
void rdma_restrack_del(struct rdma_restrack_entry *res)
{
struct rdma_restrack_entry *old;
struct rdma_restrack_root *rt;
- struct ib_device *dev;
if (!res->valid) {
if (res->task) {
@@ -287,12 +389,10 @@ void rdma_restrack_del(struct rdma_restrack_entry *res)
if (res->no_track)
goto out;
- dev = res_to_dev(res);
- if (WARN_ON(!dev))
+ rt = res_to_rt(res);
+ if (!rt)
return;
- rt = &dev->res[res->type];
-
old = xa_erase(&rt->xa, res->id);
WARN_ON(old != res);
@@ -300,5 +400,61 @@ void rdma_restrack_del(struct rdma_restrack_entry *res)
res->valid = false;
rdma_restrack_put(res);
wait_for_completion(&res->comp);
+ if (res->task) {
+ put_task_struct(res->task);
+ res->task = NULL;
+ }
}
EXPORT_SYMBOL(rdma_restrack_del);
+
+/**
+ * rdma_restrack_begin_del() - invalidate the object from the resource tracking
+ * database but preserve its index in the array.
+ * Since this preserves the index in the array until rdma_restrack_commit_del()
+ * is called, if rdma_restrack_add() is called in between with an old QP ID it
+ * can result in an untracked QP.
+ * @res: resource entry
+ */
+void rdma_restrack_begin_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt = NULL;
+
+ if (!res->valid)
+ return;
+
+ if (!res->no_track) {
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+ }
+
+ restrack_drain_res(rt, res);
+}
+EXPORT_SYMBOL(rdma_restrack_begin_del);
+
+/**
+ * rdma_restrack_commit_del() - delete object from the resource tracking
+ * database and free the task.
+ * @res: resource entry
+ */
+void rdma_restrack_commit_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt;
+
+ if (!res->valid || res->no_track)
+ goto out;
+
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+
+ xa_erase(&rt->xa, res->id);
+
+out:
+ res->valid = false;
+ if (res->task) {
+ put_task_struct(res->task);
+ res->task = NULL;
+ }
+}
+EXPORT_SYMBOL(rdma_restrack_commit_del);
diff --git a/drivers/infiniband/core/restrack.h b/drivers/infiniband/core/restrack.h
index 6a04fc41f738..2df78e084e10 100644
--- a/drivers/infiniband/core/restrack.h
+++ b/drivers/infiniband/core/restrack.h
@@ -26,7 +26,11 @@ struct rdma_restrack_root {
int rdma_restrack_init(struct ib_device *dev);
void rdma_restrack_clean(struct ib_device *dev);
void rdma_restrack_add(struct rdma_restrack_entry *res);
+void rdma_restrack_abort_del(struct rdma_restrack_entry *res);
void rdma_restrack_del(struct rdma_restrack_entry *res);
+void rdma_restrack_sync(struct rdma_restrack_entry *res);
+void rdma_restrack_begin_del(struct rdma_restrack_entry *res);
+void rdma_restrack_commit_del(struct rdma_restrack_entry *res);
void rdma_restrack_new(struct rdma_restrack_entry *res,
enum rdma_restrack_type type);
void rdma_restrack_set_name(struct rdma_restrack_entry *res,
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index bf42650f125b..48d708edf0e7 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -923,7 +923,7 @@ static ssize_t ucma_query_path(struct ucma_context *ctx,
resp->num_paths = ctx->cm_id->route.num_pri_alt_paths;
for (i = 0, out_len -= sizeof(*resp);
- i < resp->num_paths && out_len > sizeof(struct ib_path_rec_data);
+ i < resp->num_paths && out_len >= sizeof(struct ib_path_rec_data);
i++, out_len -= sizeof(struct ib_path_rec_data)) {
struct sa_path_rec *rec = &ctx->cm_id->route.path_rec[i];
@@ -1336,7 +1336,10 @@ static int ucma_set_ib_path(struct ucma_context *ctx,
memset(&event, 0, sizeof event);
event.event = RDMA_CM_EVENT_ROUTE_RESOLVED;
- return ucma_event_handler(ctx->cm_id, &event);
+ rdma_lock_handler(ctx->cm_id);
+ ret = ucma_event_handler(ctx->cm_id, &event);
+ rdma_unlock_handler(ctx->cm_id);
+ return ret;
}
static int ucma_set_option_ib(struct ucma_context *ctx, int optname,
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 2e4265ba35b7..1c80ff41896d 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -46,6 +46,7 @@
#include "uverbs.h"
#include "core_priv.h"
+#include "restrack.h"
/*
* Copy a response to userspace. If the provided 'resp' is larger than the
@@ -813,6 +814,10 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs)
ret = PTR_ERR(new_pd);
goto put_uobjs;
}
+ if (new_pd == orig_pd) {
+ uobj_put_obj_read(new_pd);
+ cmd.flags &= ~IB_MR_REREG_PD;
+ }
} else {
new_pd = mr->pd;
}
@@ -858,9 +863,10 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs)
mr = new_mr;
} else {
if (cmd.flags & IB_MR_REREG_PD) {
- atomic_dec(&orig_pd->usecnt);
- mr->pd = new_pd;
atomic_inc(&new_pd->usecnt);
+ WRITE_ONCE(mr->pd, new_pd);
+ rdma_restrack_sync(&mr->res);
+ atomic_dec(&orig_pd->usecnt);
}
if (cmd.flags & IB_MR_REREG_TRANS) {
mr->iova = cmd.hca_va;
@@ -1859,7 +1865,8 @@ static int modify_qp(struct uverbs_attr_bundle *attrs,
if (cmd->base.attr_mask & IB_QP_PATH_MIG_STATE)
attr->path_mig_state = cmd->base.path_mig_state;
if (cmd->base.attr_mask & IB_QP_QKEY) {
- if (cmd->base.qkey & IB_QP_SET_QKEY && !capable(CAP_NET_RAW)) {
+ if (cmd->base.qkey & IB_QP_SET_QKEY &&
+ !rdma_nl_get_privileged_qkey()) {
ret = -EPERM;
goto release_qp;
}
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index ac00b070d320..d08aa155a9cf 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -335,6 +335,7 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata)
{
int ret;
+ rdma_restrack_begin_del(&pd->res);
if (pd->__internal_mr) {
ret = pd->device->ops.dereg_mr(pd->__internal_mr, NULL);
WARN_ON(ret);
@@ -342,10 +343,12 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata)
}
ret = pd->device->ops.dealloc_pd(pd, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&pd->res);
return ret;
+ }
- rdma_restrack_del(&pd->res);
+ rdma_restrack_commit_del(&pd->res);
kfree(pd);
return ret;
}
@@ -1083,16 +1086,20 @@ int ib_destroy_srq_user(struct ib_srq *srq, struct ib_udata *udata)
if (atomic_read(&srq->usecnt))
return -EBUSY;
+ rdma_restrack_begin_del(&srq->res);
+
ret = srq->device->ops.destroy_srq(srq, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&srq->res);
return ret;
+ }
atomic_dec(&srq->pd->usecnt);
if (srq->srq_type == IB_SRQT_XRC && srq->ext.xrc.xrcd)
atomic_dec(&srq->ext.xrc.xrcd->usecnt);
if (ib_srq_has_cq(srq->srq_type))
atomic_dec(&srq->ext.cq->usecnt);
- rdma_restrack_del(&srq->res);
+ rdma_restrack_commit_del(&srq->res);
kfree(srq);
return ret;
@@ -2086,6 +2093,8 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (qp->real_qp != qp)
return __ib_destroy_shared_qp(qp);
+ rdma_restrack_begin_del(&qp->res);
+
sec = qp->qp_sec;
if (sec)
ib_destroy_qp_security_begin(sec);
@@ -2098,6 +2107,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (ret) {
if (sec)
ib_destroy_qp_security_abort(sec);
+ rdma_restrack_abort_del(&qp->res);
return ret;
}
@@ -2110,7 +2120,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (sec)
ib_destroy_qp_security_end(sec);
- rdma_restrack_del(&qp->res);
+ rdma_restrack_commit_del(&qp->res);
kfree(qp);
return ret;
}
@@ -2175,11 +2185,15 @@ int ib_destroy_cq_user(struct ib_cq *cq, struct ib_udata *udata)
if (atomic_read(&cq->usecnt))
return -EBUSY;
+ rdma_restrack_begin_del(&cq->res);
+
ret = cq->device->ops.destroy_cq(cq, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&cq->res);
return ret;
+ }
- rdma_restrack_del(&cq->res);
+ rdma_restrack_commit_del(&cq->res);
kfree(cq);
return ret;
}
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
index 8f7e9a5285d0..7d6c296da3b6 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
@@ -4268,11 +4268,13 @@ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma)
break;
case BNXT_RE_MMAP_DBR_PAGE:
/* Driver doesn't expect write access for user space */
- if (vma->vm_flags & VM_WRITE)
+ if (vma->vm_flags & VM_WRITE) {
ret = -EFAULT;
- else
+ } else {
+ vm_flags_clear(vma, VM_MAYWRITE);
ret = vm_insert_page(vma, vma->vm_start,
virt_to_page((void *)bnxt_entry->mem_offset));
+ }
break;
default:
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 034b85c42255..d5101cabbd4b 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -935,6 +935,7 @@ static void c4iw_rdev_close(struct c4iw_rdev *rdev)
void c4iw_dealloc(struct uld_ctx *ctx)
{
+ debugfs_remove_recursive(ctx->dev->debugfs_root);
c4iw_rdev_close(&ctx->dev->rdev);
WARN_ON(!xa_empty(&ctx->dev->cqs));
WARN_ON(!xa_empty(&ctx->dev->qps));
@@ -953,7 +954,12 @@ void c4iw_dealloc(struct uld_ctx *ctx)
static void c4iw_remove(struct uld_ctx *ctx)
{
pr_debug("c4iw_dev %p\n", ctx->dev);
- debugfs_remove_recursive(ctx->dev->debugfs_root);
+
+ /* c4iw_register_device() may still be using ctx->dev. */
+ cancel_work_sync(&ctx->reg_work);
+ if (!ctx->dev)
+ return;
+
c4iw_unregister_device(ctx->dev);
c4iw_dealloc(ctx);
}
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 88db7e527728..9c24789bbb54 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -277,6 +277,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
int err;
struct fw_ri_tpte *tpt;
u32 stag_idx;
+ bool stag_idx_allocated = false;
static atomic_t key;
if (c4iw_fatal_error(rdev))
@@ -299,6 +300,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
return -ENOMEM;
}
mutex_lock(&rdev->stats.lock);
+ stag_idx_allocated = true;
rdev->stats.stag.cur += 32;
if (rdev->stats.stag.cur > rdev->stats.stag.max)
rdev->stats.stag.max = rdev->stats.stag.cur;
@@ -333,7 +335,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
(rdev->lldi.vr->stag.start >> 5),
sizeof(*tpt), tpt, skb, wr_waitp);
- if (reset_tpt_entry) {
+ if (reset_tpt_entry || (err && stag_idx_allocated)) {
c4iw_put_resource(&rdev->resource.tpt_table, stag_idx);
mutex_lock(&rdev->stats.lock);
rdev->stats.stag.cur -= 32;
diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c
index 7648d162a10d..6e9ca433d4b2 100644
--- a/drivers/infiniband/hw/efa/efa_verbs.c
+++ b/drivers/infiniband/hw/efa/efa_verbs.c
@@ -1296,9 +1296,11 @@ static int pbl_chunk_list_create(struct efa_dev *dev, struct pbl_context *pbl)
chunk_list->chunks[i].length = EFA_CHUNK_USED_SIZE;
}
- chunk_list->chunks[chunk_list_size - 1].length =
- ((page_cnt % EFA_PTRS_PER_CHUNK) * EFA_CHUNK_PAYLOAD_PTR_SIZE) +
- EFA_CHUNK_PTR_SIZE;
+
+ if (page_cnt % EFA_PTRS_PER_CHUNK != 0)
+ chunk_list->chunks[chunk_list_size - 1].length =
+ ((page_cnt % EFA_PTRS_PER_CHUNK) * EFA_CHUNK_PAYLOAD_PTR_SIZE) +
+ EFA_CHUNK_PTR_SIZE;
/* fill the dma addresses of sg list pages to chunks: */
chunk_idx = 0;
@@ -1310,9 +1312,12 @@ static int pbl_chunk_list_create(struct efa_dev *dev, struct pbl_context *pbl)
rdma_block_iter_dma_address(&biter);
if (payload_idx == EFA_PTRS_PER_CHUNK) {
+ payload_idx = 0;
chunk_idx++;
+ if (chunk_idx >= chunk_list_size)
+ break;
+
cur_chunk_buf = chunk_list->chunks[chunk_idx].buf;
- payload_idx = 0;
}
}
diff --git a/drivers/infiniband/hw/erdma/Kconfig b/drivers/infiniband/hw/erdma/Kconfig
index 169038e3ceb1..c90f2be1ea63 100644
--- a/drivers/infiniband/hw/erdma/Kconfig
+++ b/drivers/infiniband/hw/erdma/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config INFINIBAND_ERDMA
tristate "Alibaba Elastic RDMA Adapter (ERDMA) support"
- depends on PCI_MSI && 64BIT
+ depends on PCI_MSI && 64BIT && !CPU_BIG_ENDIAN
depends on INFINIBAND_ADDR_TRANS
depends on INFINIBAND_USER_ACCESS
help
diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c
index ea47cb21fdb8..3fec9cd18afa 100644
--- a/drivers/infiniband/hw/erdma/erdma_eq.c
+++ b/drivers/infiniband/hw/erdma/erdma_eq.c
@@ -52,7 +52,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
if (FIELD_GET(ERDMA_AEQE_HDR_TYPE_MASK,
le32_to_cpu(aeqe->hdr)) == ERDMA_AE_TYPE_CQ_ERR) {
cqn = le32_to_cpu(aeqe->event_data0);
- cq = find_cq_by_cqn(dev, cqn);
+ cq = erdma_cq_get_by_cqn(dev, cqn);
if (!cq)
continue;
@@ -62,6 +62,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
if (cq->ibcq.event_handler)
cq->ibcq.event_handler(&event,
cq->ibcq.cq_context);
+ erdma_cq_put(cq);
} else {
qpn = le32_to_cpu(aeqe->event_data0);
qp = find_qp_by_qpn(dev, qpn);
@@ -142,7 +143,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb)
poll_cnt++;
cqn = FIELD_GET(ERDMA_CEQE_HDR_CQN_MASK, READ_ONCE(*ceqe));
- cq = find_cq_by_cqn(dev, cqn);
+ cq = erdma_cq_get_by_cqn(dev, cqn);
if (!cq)
continue;
@@ -151,6 +152,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb)
if (cq->ibcq.comp_handler)
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
+ erdma_cq_put(cq);
}
notify_eq(&ceq_cb->eq);
@@ -204,6 +206,7 @@ static void erdma_free_ceq_irq(struct erdma_dev *dev, u16 ceqn)
irq_set_affinity_hint(eqc->irq.msix_vector, NULL);
free_irq(eqc->irq.msix_vector, eqc);
+ tasklet_kill(&eqc->tasklet);
}
static int create_eq_cmd(struct erdma_dev *dev, u32 eqn, struct erdma_eq *eq)
diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c
index 64faac3c99b1..066144ae3b24 100644
--- a/drivers/infiniband/hw/erdma/erdma_verbs.c
+++ b/drivers/infiniband/hw/erdma/erdma_verbs.c
@@ -1228,6 +1228,7 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
struct erdma_dev *dev = to_edev(ibcq->device);
struct erdma_ucontext *ctx = rdma_udata_to_drv_context(
udata, struct erdma_ucontext, ibucontext);
+ unsigned long flags;
int err;
struct erdma_cmdq_destroy_cq_req req;
@@ -1239,6 +1240,13 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
if (err)
return err;
+ xa_lock_irqsave(&dev->cq_xa, flags);
+ __xa_erase(&dev->cq_xa, cq->cqn);
+ xa_unlock_irqrestore(&dev->cq_xa, flags);
+
+ erdma_cq_put(cq);
+ wait_for_completion(&cq->free);
+
if (rdma_is_kernel_res(&cq->ibcq.res)) {
dma_free_coherent(&dev->pdev->dev,
WARPPED_BUFSIZE(cq->depth << CQE_SHIFT),
@@ -1248,8 +1256,6 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
put_mtt_entries(dev, &cq->user_cq.qbuf_mem);
}
- xa_erase(&dev->cq_xa, cq->cqn);
-
return 0;
}
@@ -1658,6 +1664,8 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
cq->ibcq.cqe = depth;
cq->depth = depth;
cq->assoc_eqn = attr->comp_vector + 1;
+ refcount_set(&cq->refcount, 1);
+ init_completion(&cq->free);
ret = xa_alloc_cyclic(&dev->cq_xa, &cq->cqn, cq,
XA_LIMIT(1, dev->attrs.max_cq - 1),
diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h
index eb9c0f92fb6f..10b9b8a5f6a8 100644
--- a/drivers/infiniband/hw/erdma/erdma_verbs.h
+++ b/drivers/infiniband/hw/erdma/erdma_verbs.h
@@ -7,6 +7,9 @@
#ifndef __ERDMA_VERBS_H__
#define __ERDMA_VERBS_H__
+#include <linux/completion.h>
+#include <linux/refcount.h>
+
#include "erdma.h"
/* RDMA Capability. */
@@ -261,6 +264,8 @@ struct erdma_cq {
u32 depth;
u32 assoc_eqn;
+ refcount_t refcount;
+ struct completion free;
union {
struct erdma_kcq_info kern_cq;
@@ -275,9 +280,25 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id)
return (struct erdma_qp *)xa_load(&dev->qp_xa, id);
}
-static inline struct erdma_cq *find_cq_by_cqn(struct erdma_dev *dev, int id)
+static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev,
+ int id)
+{
+ struct erdma_cq *cq;
+ unsigned long flags;
+
+ xa_lock_irqsave(&dev->cq_xa, flags);
+ cq = xa_load(&dev->cq_xa, id);
+ if (cq && !refcount_inc_not_zero(&cq->refcount))
+ cq = NULL;
+ xa_unlock_irqrestore(&dev->cq_xa, flags);
+
+ return cq;
+}
+
+static inline void erdma_cq_put(struct erdma_cq *cq)
{
- return (struct erdma_cq *)xa_load(&dev->cq_xa, id);
+ if (refcount_dec_and_test(&cq->refcount))
+ complete(&cq->free);
}
void erdma_qp_get(struct erdma_qp *qp);
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index 9b542f7c6c11..00a054007822 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -15065,7 +15065,7 @@ int hfi1_init_dd(struct hfi1_devdata *dd)
*/
ret = hfi1_pcie_ddinit(dd, pdev);
if (ret < 0)
- goto bail_free;
+ goto bail;
/* Save PCI space registers to rewrite after device reset */
ret = save_pci_variables(dd);
@@ -15320,8 +15320,6 @@ int hfi1_init_dd(struct hfi1_devdata *dd)
bail_cleanup:
hfi1_free_rx(dd);
hfi1_pcie_ddcleanup(dd);
-bail_free:
- hfi1_free_devdata(dd);
bail:
return ret;
}
diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h
index 38772e52d7ed..14d01f7af118 100644
--- a/drivers/infiniband/hw/hfi1/hfi.h
+++ b/drivers/infiniband/hw/hfi1/hfi.h
@@ -2044,9 +2044,7 @@ struct cc_state *get_cc_state_protected(struct hfi1_pportdata *ppd)
/* waiting for an urgent packet to arrive */
#define HFI1_CTXT_WAITING_URG 4
-/* free up any allocated data at closes */
int hfi1_init_dd(struct hfi1_devdata *dd);
-void hfi1_free_devdata(struct hfi1_devdata *dd);
/* LED beaconing functions */
void hfi1_start_led_override(struct hfi1_pportdata *ppd, unsigned int timeon,
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index 6de37c5d7d27..5c7e34773f4b 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -630,8 +630,6 @@ void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd,
ppd->sm_trap_qp = 0x0;
ppd->sa_qp = 0x1;
- ppd->hfi1_wq = NULL;
-
spin_lock_init(&ppd->cca_timer_lock);
for (i = 0; i < OPA_MAX_SLS; i++) {
@@ -1165,7 +1163,7 @@ static void finalize_asic_data(struct hfi1_devdata *dd,
* It cleans up and frees all data structures set up by
* by hfi1_alloc_devdata().
*/
-void hfi1_free_devdata(struct hfi1_devdata *dd)
+static void hfi1_free_devdata(struct hfi1_devdata *dd)
{
struct hfi1_asic_data *ad;
unsigned long flags;
@@ -1228,8 +1226,9 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev,
GFP_KERNEL);
if (ret < 0) {
dev_err(&pdev->dev,
- "Could not allocate unit ID: error %d\n", -ret);
- goto bail;
+ "Could not allocate unit ID: error %pe\n", ERR_PTR(ret));
+ rvt_dealloc_device(&dd->verbs_dev.rdi);
+ return ERR_PTR(ret);
}
rvt_set_ibdev_name(&dd->verbs_dev.rdi, "%s_%d", class_name(), dd->unit);
/*
@@ -1563,15 +1562,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* First, lock the non-writable module parameters */
HFI1_CAP_LOCK();
- /* Validate dev ids */
- if (!(ent->device == PCI_DEVICE_ID_INTEL0 ||
- ent->device == PCI_DEVICE_ID_INTEL1)) {
- dev_err(&pdev->dev, "Failing on unknown Intel deviceid 0x%x\n",
- ent->device);
- ret = -ENODEV;
- goto bail;
- }
-
/* Allocate the dd so we can get to work */
dd = hfi1_alloc_devdata(pdev, NUM_IB_PORTS *
sizeof(struct hfi1_pportdata));
@@ -1625,17 +1615,17 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto bail;
+ ret = create_workqueues(dd);
+ if (ret)
+ goto free_devdata;
+
/*
* Do device-specific initialization, function table setup, dd
* allocation, etc.
*/
ret = hfi1_init_dd(dd);
if (ret)
- goto clean_bail; /* error already printed */
-
- ret = create_workqueues(dd);
- if (ret)
- goto clean_bail;
+ goto destroy_workqueues; /* error already printed */
/* do the generic initialization */
initfail = hfi1_init(dd, 0);
@@ -1648,11 +1638,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
* we still create devices, so diags, etc. can be used
* to determine cause of problem.
*/
- if (!initfail && !ret) {
+ if (!initfail && !ret)
dd->flags |= HFI1_INITTED;
- /* create debufs files after init and ib register */
- hfi1_dbg_ibdev_init(&dd->verbs_dev);
- }
j = hfi1_device_create(dd);
if (j)
@@ -1661,7 +1648,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (initfail || ret) {
msix_clean_up_interrupts(dd);
stop_timers(dd);
- flush_workqueue(ib_wq);
for (pidx = 0; pidx < dd->num_pports; ++pidx) {
hfi1_quiet_serdes(dd->pport + pidx);
ppd = dd->pport + pidx;
@@ -1678,6 +1664,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
hfi1_device_remove(dd);
if (!ret)
hfi1_unregister_ib_device(dd);
+ hfi1_free_rx(dd);
postinit_cleanup(dd);
if (initfail)
ret = initfail;
@@ -1685,10 +1672,14 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
sdma_start(dd);
+ hfi1_dbg_ibdev_init(&dd->verbs_dev);
return 0;
-clean_bail:
+destroy_workqueues:
+ destroy_workqueues(dd);
+free_devdata:
+ hfi1_free_devdata(dd);
hfi1_pcie_cleanup(pdev);
bail:
return ret;
@@ -1734,9 +1725,6 @@ static void remove_one(struct pci_dev *pdev)
stop_timers(dd);
- /* wait until all of our (qsfp) queue_work() calls complete */
- flush_workqueue(ib_wq);
-
postinit_cleanup(dd);
}
diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c
index 29ae7beb9b03..511163fbd951 100644
--- a/drivers/infiniband/hw/hfi1/user_sdma.c
+++ b/drivers/infiniband/hw/hfi1/user_sdma.c
@@ -1028,6 +1028,7 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
struct user_sdma_txreq *tx, u32 datalen)
{
u32 ahg[AHG_KDETH_ARRAY_SIZE];
+ int ret;
int idx = 0;
u8 omfactor; /* KDETH.OM */
struct hfi1_user_sdma_pkt_q *pq = req->pq;
@@ -1132,11 +1133,13 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
trace_hfi1_sdma_user_header_ahg(pq->dd, pq->ctxt, pq->subctxt,
req->info.comp_idx, req->sde->this_idx,
req->ahg_idx, ahg, idx, tidval);
- sdma_txinit_ahg(&tx->txreq,
- SDMA_TXREQ_F_USE_AHG,
- datalen, req->ahg_idx, idx,
- ahg, sizeof(req->hdr),
- user_sdma_txreq_cb);
+ ret = sdma_txinit_ahg(&tx->txreq,
+ SDMA_TXREQ_F_USE_AHG,
+ datalen, req->ahg_idx, idx,
+ ahg, sizeof(req->hdr),
+ user_sdma_txreq_cb);
+ if (ret)
+ return ret;
return idx;
}
diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c
index a78a067e3ce7..e3c4b1c92816 100644
--- a/drivers/infiniband/hw/mlx5/cong.c
+++ b/drivers/infiniband/hw/mlx5/cong.c
@@ -361,7 +361,7 @@ static int mlx5_ib_set_cc_params(struct mlx5_ib_dev *dev, u32 port_num,
MLX5_SET(field_select_r_roce_rp, field, field_select_r_roce_rp,
attr_mask);
- err = mlx5_cmd_exec_in(dev->mdev, modify_cong_params, in);
+ err = mlx5_cmd_exec_in(mdev, modify_cong_params, in);
kvfree(in);
alloc_err:
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
@@ -399,15 +399,13 @@ static ssize_t get_param(struct file *filp, char __user *buf, size_t count,
int offset = param->offset;
u32 var = 0;
int ret;
- char lbuf[11];
+ char lbuf[12];
ret = mlx5_ib_get_cc_params(param->dev, param->port_num, offset, &var);
if (ret)
return ret;
- ret = snprintf(lbuf, sizeof(lbuf), "%d\n", var);
- if (ret < 0)
- return ret;
+ ret = scnprintf(lbuf, sizeof(lbuf), "%u\n", var);
return simple_read_from_buffer(buf, count, pos, lbuf, ret);
}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 10422dee6db8..57f0f9a6e8e5 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -4603,6 +4603,7 @@ static int __init mlx5_ib_init(void)
mp_err:
mlx5r_rep_cleanup();
rep_err:
+ rcu_barrier();
mlx5_ib_qp_event_cleanup();
qp_event_err:
destroy_workqueue(mlx5_ib_event_wq);
@@ -4615,6 +4616,7 @@ static void __exit mlx5_ib_cleanup(void)
auxiliary_driver_unregister(&mlx5r_driver);
auxiliary_driver_unregister(&mlx5r_mp_driver);
mlx5r_rep_cleanup();
+ rcu_barrier();
mlx5_ib_qp_event_cleanup();
destroy_workqueue(mlx5_ib_event_wq);
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 6bbf12732051..83661a393136 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -637,6 +637,7 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev,
struct ib_qp_init_attr *attr)
{
int desc_sz = 1 << qp->sq.wqe_shift;
+ int rq_buf_size, sq_buf_size;
if (desc_sz > MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq)) {
mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n",
@@ -661,11 +662,21 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev,
if (attr->qp_type == IB_QPT_RAW_PACKET ||
qp->flags & IB_QP_CREATE_SOURCE_QPN) {
- base->ubuffer.buf_size = qp->rq.wqe_cnt << qp->rq.wqe_shift;
- qp->raw_packet_qp.sq.ubuffer.buf_size = qp->sq.wqe_cnt << 6;
+ if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift,
+ &base->ubuffer.buf_size))
+ return -EINVAL;
+ if (check_shl_overflow(qp->sq.wqe_cnt, 6,
+ &qp->raw_packet_qp.sq.ubuffer.buf_size))
+ return -EINVAL;
} else {
- base->ubuffer.buf_size = (qp->rq.wqe_cnt << qp->rq.wqe_shift) +
- (qp->sq.wqe_cnt << 6);
+ if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift,
+ &rq_buf_size))
+ return -EINVAL;
+ if (check_shl_overflow(qp->sq.wqe_cnt, 6, &sq_buf_size))
+ return -EINVAL;
+ if (check_add_overflow(rq_buf_size, sq_buf_size,
+ &base->ubuffer.buf_size))
+ return -EINVAL;
}
return 0;
@@ -989,7 +1000,11 @@ static int _create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
qp->rq.offset = 0;
qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
- qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift;
+ if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift,
+ &qp->sq.offset)) {
+ err = -EINVAL;
+ goto err_bfreg;
+ }
err = set_user_buf_size(dev, qp, ucmd, base, attr);
if (err)
diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c
index 4d550ac0dac5..bccf1f28467a 100644
--- a/drivers/infiniband/sw/rxe/rxe_req.c
+++ b/drivers/infiniband/sw/rxe/rxe_req.c
@@ -732,6 +732,21 @@ int rxe_requester(struct rxe_qp *qp)
if (unlikely(!wqe))
goto exit;
+ /*
+ * Don't trust user space data: a user QP's WQE comes from an mmap'd
+ * ring, so num_sge/cur_sge are attacker-controlled. Bound num_sge like
+ * get_srq_wqe(); bound cur_sge only when payload exists (dma.resid),
+ * since copy_data() skips dma->sge[] on a zero-length copy (all a
+ * max_sge == 0 QP can post).
+ */
+ if (unlikely(wqe->dma.num_sge > qp->sq.max_sge ||
+ (wqe->dma.resid &&
+ wqe->dma.cur_sge >= qp->sq.max_sge))) {
+ rxe_dbg_qp(qp, "invalid num_sge/cur_sge in send wqe\n");
+ wqe->status = IB_WC_LOC_QP_OP_ERR;
+ goto err;
+ }
+
if (rxe_wqe_is_fenced(qp, wqe)) {
qp->req.wait_fence = 1;
goto exit;
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 539a30ec9821..e03082db4243 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -1198,7 +1198,14 @@ static enum resp_states do_complete(struct rxe_qp *qp,
spin_lock_irqsave(&qp->state_lock, flags);
if (unlikely(qp_state(qp) == IB_QPS_ERR)) {
spin_unlock_irqrestore(&qp->state_lock, flags);
- return RESPST_CHK_RESOURCE;
+ /* The packet was executed and completed before the QP
+ * moved to ERROR; it must be consumed exactly once.
+ * Re-entering the request chain with the stale packet
+ * would copy it into every remaining recv WQE as a new
+ * completion. Remaining WQEs are flushed by the drain
+ * path at rxe_receiver() entry.
+ */
+ return pkt ? RESPST_CLEANUP : RESPST_CHK_RESOURCE;
}
spin_unlock_irqrestore(&qp->state_lock, flags);
diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c
index 43e776073f49..a04c1fcabfcc 100644
--- a/drivers/infiniband/sw/siw/siw_cm.c
+++ b/drivers/infiniband/sw/siw/siw_cm.c
@@ -364,6 +364,15 @@ static int siw_cm_upcall(struct siw_cep *cep, enum iw_cm_event_type reason,
return id->event_handler(id, &event);
}
+static void siw_free_cm_id(struct siw_cep *cep)
+{
+ if (!cep->cm_id)
+ return;
+
+ cep->cm_id->rem_ref(cep->cm_id);
+ cep->cm_id = NULL;
+}
+
/*
* siw_qp_cm_drop()
*
@@ -416,8 +425,7 @@ void siw_qp_cm_drop(struct siw_qp *qp, int schedule)
default:
break;
}
- cep->cm_id->rem_ref(cep->cm_id);
- cep->cm_id = NULL;
+ siw_free_cm_id(cep);
siw_cep_put(cep);
}
cep->state = SIW_EPSTATE_CLOSED;
@@ -1175,8 +1183,7 @@ static void siw_cm_work_handler(struct work_struct *w)
cep->sock = NULL;
}
if (cep->cm_id) {
- cep->cm_id->rem_ref(cep->cm_id);
- cep->cm_id = NULL;
+ siw_free_cm_id(cep);
siw_cep_put(cep);
}
}
@@ -1709,11 +1716,8 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params)
cep->state = SIW_EPSTATE_CLOSED;
- if (cep->cm_id) {
- cep->cm_id->rem_ref(id);
- cep->cm_id = NULL;
- }
- if (qp->cep) {
+ siw_free_cm_id(cep);
+ if (qp->cep == cep) {
siw_cep_put(cep);
qp->cep = NULL;
}
@@ -1890,10 +1894,7 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
if (cep) {
siw_cep_set_inuse(cep);
- if (cep->cm_id) {
- cep->cm_id->rem_ref(cep->cm_id);
- cep->cm_id = NULL;
- }
+ siw_free_cm_id(cep);
cep->sock = NULL;
siw_socket_disassoc(s);
cep->state = SIW_EPSTATE_CLOSED;
@@ -1923,10 +1924,7 @@ static void siw_drop_listeners(struct iw_cm_id *id)
siw_cep_set_inuse(cep);
- if (cep->cm_id) {
- cep->cm_id->rem_ref(cep->cm_id);
- cep->cm_id = NULL;
- }
+ siw_free_cm_id(cep);
if (cep->sock) {
siw_socket_disassoc(cep->sock);
sock_release(cep->sock);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index cf8b0822f5c8..3d44528fc916 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -2675,6 +2675,7 @@ static int __init ipoib_init_module(void)
err_sa:
ib_sa_unregister_client(&ipoib_sa_client);
destroy_workqueue(ipoib_workqueue);
+ rcu_barrier();
err_fs:
ipoib_unregister_debugfs();
@@ -2692,6 +2693,7 @@ static void __exit ipoib_cleanup_module(void)
ib_sa_unregister_client(&ipoib_sa_client);
ipoib_unregister_debugfs();
destroy_workqueue(ipoib_workqueue);
+ rcu_barrier();
}
module_init(ipoib_init_module);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index a0dabbbe6635..3298928551b8 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -59,6 +59,8 @@ static void isert_recv_done(struct ib_cq *cq, struct ib_wc *wc);
static void isert_send_done(struct ib_cq *cq, struct ib_wc *wc);
static void isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc);
static void isert_login_send_done(struct ib_cq *cq, struct ib_wc *wc);
+static void isert_unmap_tx_desc(struct iser_tx_desc *tx_desc,
+ struct ib_device *ib_dev);
static int isert_sg_tablesize_set(const char *val, const struct kernel_param *kp)
{
@@ -501,6 +503,8 @@ isert_connect_release(struct isert_conn *isert_conn)
if (isert_conn->qp)
isert_destroy_qp(isert_conn);
+ isert_unmap_tx_desc(&isert_conn->login_tx_desc, device->ib_device);
+
if (isert_conn->login_desc)
isert_free_login_buf(isert_conn);
@@ -948,27 +952,16 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login,
}
if (!login->login_failed) {
if (login->login_complete) {
- ret = isert_alloc_rx_descriptors(isert_conn);
- if (ret)
- return ret;
-
- ret = isert_post_recvm(isert_conn,
- ISERT_QP_MAX_RECV_DTOS);
- if (ret)
- return ret;
-
- /* Now we are in FULL_FEATURE phase */
- mutex_lock(&isert_conn->mutex);
- isert_conn->state = ISER_CONN_FULL_FEATURE;
- mutex_unlock(&isert_conn->mutex);
- goto post_send;
+ /* Posted and sent from isert_get_rx_pdu(). */
+ isert_conn->login_rsp_pending = true;
+ return 0;
}
ret = isert_login_post_recv(isert_conn);
if (ret)
return ret;
}
-post_send:
+
ret = isert_login_post_send(isert_conn, tx_desc);
if (ret)
return ret;
@@ -976,6 +969,21 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login,
return 0;
}
+static int
+isert_check_login_req(struct isert_conn *isert_conn)
+{
+ struct iscsi_hdr *hdr = isert_get_iscsi_hdr(isert_conn->login_desc);
+ u32 dlength = ntoh24(hdr->dlength);
+
+ if (unlikely(dlength > (u32)isert_conn->login_req_len)) {
+ isert_dbg("login PDU declares %u data bytes but only %d were received\n",
+ dlength, isert_conn->login_req_len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static void
isert_rx_login_req(struct isert_conn *isert_conn)
{
@@ -1338,6 +1346,21 @@ isert_recv_done(struct ib_cq *cq, struct ib_wc *wc)
ib_dma_sync_single_for_cpu(ib_dev, rx_desc->dma_addr,
ISER_RX_SIZE, DMA_FROM_DEVICE);
+ /*
+ * The data segment length declared in the BHS is attacker controlled
+ * and is used further down to read that many bytes out of the fixed
+ * size receive descriptor, so it has to be checked against the number
+ * of bytes that were actually received. Comparing without subtracting
+ * also rejects PDUs shorter than the iSER and iSCSI headers, which
+ * would otherwise be parsed out of stale descriptor contents.
+ */
+ if (unlikely(wc->byte_len < ISER_HEADERS_LEN + ntoh24(hdr->dlength))) {
+ isert_err("PDU declares %u data bytes but only %u bytes were received\n",
+ ntoh24(hdr->dlength), wc->byte_len);
+ iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+ return;
+ }
+
isert_dbg("DMA: 0x%llx, iSCSI opcode: 0x%02x, ITT: 0x%08x, flags: 0x%02x dlen: %d\n",
rx_desc->dma_addr, hdr->opcode, hdr->itt, hdr->flags,
(int)(wc->byte_len - ISER_HEADERS_LEN));
@@ -1399,8 +1422,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc)
if (isert_conn->conn) {
struct iscsi_login *login = isert_conn->conn->conn_login;
- if (login && !login->first_request)
+ if (login && !login->first_request) {
+ if (isert_check_login_req(isert_conn))
+ return;
+
isert_rx_login_req(isert_conn);
+ }
}
mutex_lock(&isert_conn->mutex);
@@ -2365,6 +2392,10 @@ isert_get_login_rx(struct iscsit_conn *conn, struct iscsi_login *login)
if (!login->first_request)
return 0;
+ ret = isert_check_login_req(isert_conn);
+ if (ret)
+ return ret;
+
isert_rx_login_req(isert_conn);
isert_info("before login_comp conn: %p\n", conn);
@@ -2590,8 +2621,30 @@ static void isert_free_conn(struct iscsit_conn *conn)
static void isert_get_rx_pdu(struct iscsit_conn *conn)
{
+ struct isert_conn *isert_conn = conn->context;
struct completion comp;
+ /* The login timeout timer can fail the login after isert_put_login_tx(). */
+ if (!isert_conn->login_rsp_pending)
+ return;
+
+ isert_conn->login_rsp_pending = false;
+
+ /* The session is registered by now; see isert_put_login_tx(). */
+ if (isert_alloc_rx_descriptors(isert_conn))
+ return;
+
+ if (isert_post_recvm(isert_conn, ISERT_QP_MAX_RECV_DTOS))
+ return;
+
+ /* Now we are in FULL_FEATURE phase */
+ mutex_lock(&isert_conn->mutex);
+ isert_conn->state = ISER_CONN_FULL_FEATURE;
+ mutex_unlock(&isert_conn->mutex);
+
+ if (isert_login_post_send(isert_conn, &isert_conn->login_tx_desc))
+ return;
+
init_completion(&comp);
wait_for_completion_interruptible(&comp);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index 0b2dfd6e7e27..0bac5aa66c80 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -178,6 +178,7 @@ struct isert_conn {
struct completion login_comp;
struct completion login_req_comp;
struct iser_tx_desc login_tx_desc;
+ bool login_rsp_pending;
struct rdma_cm_id *cm_id;
struct ib_qp *qp;
struct ib_cq *cq;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index ce1505a8ee8d..13eddbe0841a 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1943,7 +1943,8 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp,
ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
if (rsp->tag == ch->tsk_mgmt_tag) {
ch->tsk_mgmt_status = -1;
- if (be32_to_cpu(rsp->resp_data_len) >= 4)
+ if (be32_to_cpu(rsp->resp_data_len) >= 4 &&
+ byte_len >= sizeof(*rsp) + 4)
ch->tsk_mgmt_status = rsp->data[3];
complete(&ch->tsk_mgmt_done);
} else {
@@ -2043,13 +2044,20 @@ static int srp_response_common(struct srp_rdma_ch *ch, s32 req_delta,
}
static void srp_process_cred_req(struct srp_rdma_ch *ch,
- struct srp_cred_req *req)
+ struct srp_cred_req *req, u32 byte_len)
{
- struct srp_cred_rsp rsp = {
- .opcode = SRP_CRED_RSP,
- .tag = req->tag,
- };
- s32 delta = be32_to_cpu(req->req_lim_delta);
+ struct srp_cred_rsp rsp = { .opcode = SRP_CRED_RSP };
+ s32 delta;
+
+ if (byte_len < sizeof(*req)) {
+ shost_printk(KERN_ERR, ch->target->scsi_host, PFX
+ "dropping truncated SRP_CRED_REQ (%u bytes received, %zu expected)\n",
+ byte_len, sizeof(*req));
+ return;
+ }
+
+ rsp.tag = req->tag;
+ delta = be32_to_cpu(req->req_lim_delta);
if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
shost_printk(KERN_ERR, ch->target->scsi_host, PFX
@@ -2057,14 +2065,21 @@ static void srp_process_cred_req(struct srp_rdma_ch *ch,
}
static void srp_process_aer_req(struct srp_rdma_ch *ch,
- struct srp_aer_req *req)
+ struct srp_aer_req *req, u32 byte_len)
{
struct srp_target_port *target = ch->target;
- struct srp_aer_rsp rsp = {
- .opcode = SRP_AER_RSP,
- .tag = req->tag,
- };
- s32 delta = be32_to_cpu(req->req_lim_delta);
+ struct srp_aer_rsp rsp = { .opcode = SRP_AER_RSP };
+ s32 delta;
+
+ if (byte_len < sizeof(*req)) {
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "dropping truncated SRP_AER_REQ (%u bytes received, %zu expected)\n",
+ byte_len, sizeof(*req));
+ return;
+ }
+
+ rsp.tag = req->tag;
+ delta = be32_to_cpu(req->req_lim_delta);
shost_printk(KERN_ERR, target->scsi_host, PFX
"ignoring AER for LUN %llu\n", scsilun_to_int(&req->lun));
@@ -2106,11 +2121,11 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc)
break;
case SRP_CRED_REQ:
- srp_process_cred_req(ch, iu->buf);
+ srp_process_cred_req(ch, iu->buf, wc->byte_len);
break;
case SRP_AER_REQ:
- srp_process_aer_req(ch, iu->buf);
+ srp_process_aer_req(ch, iu->buf, wc->byte_len);
break;
case SRP_T_LOGOUT:
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 13e1fc5e2759..81df2acc1566 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -959,6 +959,7 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx,
struct srpt_rdma_ch *ch = ioctx->ch;
struct scatterlist *prev = NULL;
unsigned prev_nents;
+ u8 n_rdma, n_rw_ctx;
int ret, i;
if (nbufs == 1) {
@@ -970,6 +971,9 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx,
return -ENOMEM;
}
+ n_rw_ctx = ioctx->n_rw_ctx;
+ n_rdma = ioctx->n_rdma;
+
for (i = ioctx->n_rw_ctx; i < nbufs; i++, db++) {
struct srpt_rw_ctx *ctx = &ioctx->rw_ctxs[i];
u64 remote_addr = be64_to_cpu(db->va);
@@ -1016,6 +1020,9 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx,
}
if (ioctx->rw_ctxs != &ioctx->s_rw_ctx)
kfree(ioctx->rw_ctxs);
+ ioctx->rw_ctxs = NULL;
+ ioctx->n_rw_ctx = n_rw_ctx;
+ ioctx->n_rdma = n_rdma;
return ret;
}
@@ -1596,7 +1603,7 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch,
rc = target_init_cmd(cmd, ch->sess, &send_ioctx->sense_data[0],
scsilun_to_int(&srp_cmd->lun), data_len,
- TCM_SIMPLE_TAG, dir, TARGET_SCF_ACK_KREF);
+ cmd->sam_task_attr, dir, TARGET_SCF_ACK_KREF);
if (rc != 0) {
pr_debug("target_submit_cmd() returned %d for tag %#llx\n", rc,
srp_cmd->tag);
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index baabc51547b8..6210cd99d629 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -658,6 +658,8 @@ static void aiptek_irq(struct urb *urb)
pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1;
+ if (macro >= ARRAY_SIZE(macroKeyEvents))
+ macro = -1;
z = get_unaligned_le16(data + 4);
if (dv) {
@@ -699,7 +701,9 @@ static void aiptek_irq(struct urb *urb)
left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
- macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0;
+ macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : -1;
+ if (macro >= ARRAY_SIZE(macroKeyEvents))
+ macro = -1;
if (dv) {
/* If the selected tool changed, reset the old
@@ -737,11 +741,11 @@ static void aiptek_irq(struct urb *urb)
*/
else if (data[0] == 6) {
macro = get_unaligned_le16(data + 1);
- if (macro > 0) {
+ if (macro > 0 && macro - 1 < ARRAY_SIZE(macroKeyEvents)) {
input_report_key(inputdev, macroKeyEvents[macro - 1],
0);
}
- if (macro < 25) {
+ if (macro + 1 < ARRAY_SIZE(macroKeyEvents)) {
input_report_key(inputdev, macroKeyEvents[macro + 1],
0);
}
@@ -760,7 +764,8 @@ static void aiptek_irq(struct urb *urb)
aiptek->curSetting.toolMode;
}
- input_report_key(inputdev, macroKeyEvents[macro], 1);
+ if (macro < ARRAY_SIZE(macroKeyEvents))
+ input_report_key(inputdev, macroKeyEvents[macro], 1);
input_report_abs(inputdev, ABS_MISC,
1 | AIPTEK_REPORT_TOOL_UNKNOWN);
input_sync(inputdev);
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 68edb07d4443..76af46eb6b3c 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -526,7 +526,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
path->name = kasprintf(GFP_KERNEL, "%s-%s",
src_data->node->name, dst_data->node->name);
if (!path->name) {
- kfree(path);
+ icc_put(path);
path = ERR_PTR(-ENOMEM);
}
@@ -624,8 +624,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst)
path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name);
if (!path->name) {
- kfree(path);
- path = ERR_PTR(-ENOMEM);
+ mutex_unlock(&icc_lock);
+ icc_put(path);
+ return ERR_PTR(-ENOMEM);
}
out:
mutex_unlock(&icc_lock);
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index d3dc03fa93c6..7a9119f99e98 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -3022,14 +3022,31 @@ static void __init free_iommu_resources(void)
/* SB IOAPIC is always on this device in AMD systems */
#define IOAPIC_SB_DEVID ((0x00 << 8) | PCI_DEVFN(0x14, 0))
+/* SB IOAPIC for Hygon family 18h model 4h is on the device 0xb */
+#define IOAPIC_SB_DEVID_FAM18H_M4H ((0x00 << 8) | PCI_DEVFN(0xb, 0))
+
+/*
+ * The Southbridge IOAPIC is assigned a GSI Base of 0 (handling interrupts
+ * 0 through 23).
+ */
+static int __init get_sb_ioapic_id(void)
+{
+ int idx = mp_find_ioapic(0);
+
+ if (idx < 0)
+ return -ENODEV;
+
+ return mpc_ioapic_id(idx);
+}
+
static bool __init check_ioapic_information(void)
{
const char *fw_bug = FW_BUG;
bool ret, has_sb_ioapic;
- int idx;
+ int idx, sb_apicid;
has_sb_ioapic = false;
- ret = false;
+ ret = true;
/*
* If we have map overrides on the kernel command line the
@@ -3039,6 +3056,16 @@ static bool __init check_ioapic_information(void)
if (cmdline_maps)
fw_bug = "";
+ sb_apicid = get_sb_ioapic_id();
+ if (sb_apicid < 0) {
+ /*
+ * Lack of SB IOAPIC registration is not a firmware bug,
+ * e.g. kernel booted with noapic or noacpi.
+ */
+ fw_bug = "";
+ goto out;
+ }
+
for (idx = 0; idx < nr_ioapics; idx++) {
int devid, id = mpc_ioapic_id(idx);
@@ -3047,12 +3074,16 @@ static bool __init check_ioapic_information(void)
pr_err("%s: IOAPIC[%d] not in IVRS table\n",
fw_bug, id);
ret = false;
- } else if (devid == IOAPIC_SB_DEVID) {
+ } else if (id == sb_apicid && (devid == IOAPIC_SB_DEVID ||
+ (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
+ boot_cpu_data.x86 == 0x18 &&
+ boot_cpu_data.x86_model >= 0x4 &&
+ boot_cpu_data.x86_model <= 0xf &&
+ devid == IOAPIC_SB_DEVID_FAM18H_M4H))) {
has_sb_ioapic = true;
- ret = true;
}
}
-
+out:
if (!has_sb_ioapic) {
/*
* We expect the SB IOAPIC to be listed in the IVRS
@@ -3063,6 +3094,7 @@ static bool __init check_ioapic_information(void)
* device id for the IOAPIC in the system.
*/
pr_err("%s: No southbridge IOAPIC found\n", fw_bug);
+ ret = false;
}
if (!ret)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index bb7365b43219..70e5d7cda0c9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3128,8 +3128,9 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
u32 reg;
writel_relaxed(val, smmu->base + reg_off);
- return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
- 1, ARM_SMMU_POLL_TIMEOUT_US);
+ return readl_relaxed_poll_timeout_atomic(smmu->base + ack_off, reg,
+ reg == val, 1,
+ ARM_SMMU_POLL_TIMEOUT_US);
}
/* GBPA is "special" */
diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index 2c76b306ca56..e134c877c16e 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -210,7 +210,7 @@ static irqreturn_t qcom_iommu_fault(int irq, void *dev)
fsynr = iommu_readl(ctx, ARM_SMMU_CB_FSYNR0);
iova = iommu_readq(ctx, ARM_SMMU_CB_FAR);
- if (!report_iommu_fault(ctx->domain, ctx->dev, iova, 0)) {
+ if (report_iommu_fault(ctx->domain, ctx->dev, iova, 0)) {
dev_err_ratelimited(ctx->dev,
"Unhandled context fault: fsr=0x%x, "
"iova=0x%016llx, fsynr=0x%x, cb=%d\n",
@@ -832,7 +832,7 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
ret = iommu_device_register(&qcom_iommu->iommu, &qcom_iommu_ops, dev);
if (ret) {
dev_err(dev, "Failed to register iommu\n");
- goto err_pm_disable;
+ goto err_sysfs_remove;
}
if (qcom_iommu->local_base) {
@@ -843,6 +843,8 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
return 0;
+err_sysfs_remove:
+ iommu_device_sysfs_remove(&qcom_iommu->iommu);
err_pm_disable:
pm_runtime_disable(dev);
return ret;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 3f7fcf1801a9..69c6d904cc6e 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1514,13 +1514,16 @@ static void *iommu_dma_alloc(struct device *dev, size_t size,
}
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
- !gfpflags_allow_blocking(gfp) && !coherent)
+ !gfpflags_allow_blocking(gfp) && !coherent) {
page = dma_alloc_from_pool(dev, PAGE_ALIGN(size), &cpu_addr,
- gfp, NULL);
- else
+ gfp, NULL);
+ if (!page)
+ return NULL;
+ } else {
cpu_addr = iommu_dma_alloc_pages(dev, size, &page, gfp, attrs);
- if (!cpu_addr)
- return NULL;
+ if (!cpu_addr)
+ return NULL;
+ }
*handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
dev->coherent_dma_mask);
diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 8358869e03d6..4ad1ac43ed0d 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -914,6 +914,18 @@ dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
return 0;
}
+static bool dmar_required(void)
+{
+ /* tboot supersedes any user/platform opt */
+ if (!intel_iommu_tboot_noforce && tboot_enabled())
+ return true;
+
+ if (!no_iommu && (!dmar_disabled || dmar_platform_optin()))
+ return true;
+
+ return false;
+}
+
void __init detect_intel_iommu(void)
{
int ret;
@@ -927,8 +939,7 @@ void __init detect_intel_iommu(void)
if (!ret)
ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
&validate_drhd_cb);
- if (!ret && !no_iommu && !iommu_detected &&
- (!dmar_disabled || dmar_platform_optin())) {
+ if (!ret && !iommu_detected && dmar_required()) {
iommu_detected = 1;
/* Make sure ACS will be enabled */
pci_request_acs();
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 682d84d86739..d68bf9231bd5 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -139,7 +139,7 @@ static int rwbf_quirk;
* (used when kernel is launched w/ TXT)
*/
static int force_on = 0;
-static int intel_iommu_tboot_noforce;
+int intel_iommu_tboot_noforce;
static int no_platform_optin;
#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
@@ -2607,7 +2607,7 @@ static int copy_context_table(struct intel_iommu *iommu,
struct context_entry **tbl,
int bus, bool ext)
{
- int tbl_idx, pos = 0, idx, devfn, ret = 0, did;
+ int tbl_idx, tbl_slot = 0, idx, devfn, ret = 0, did;
struct context_entry *new_ce = NULL, ce;
struct context_entry *old_ce = NULL;
struct root_entry re;
@@ -2623,10 +2623,9 @@ static int copy_context_table(struct intel_iommu *iommu,
if (idx == 0) {
/* First save what we may have and clean up */
if (new_ce) {
- tbl[tbl_idx] = new_ce;
+ tbl[tbl_idx + tbl_slot] = new_ce;
__iommu_flush_cache(iommu, new_ce,
VTD_PAGE_SIZE);
- pos = 1;
}
if (old_ce)
@@ -2648,6 +2647,9 @@ static int copy_context_table(struct intel_iommu *iommu,
}
}
+ /* Track if saving UCTP or LCTP entries in scalable mode */
+ tbl_slot = ext && devfn >= 0x80 ? 1 : 0;
+
ret = -ENOMEM;
old_ce = memremap(old_ce_phys, PAGE_SIZE,
MEMREMAP_WB);
@@ -2675,7 +2677,7 @@ static int copy_context_table(struct intel_iommu *iommu,
new_ce[idx] = ce;
}
- tbl[tbl_idx + pos] = new_ce;
+ tbl[tbl_idx + tbl_slot] = new_ce;
__iommu_flush_cache(iommu, new_ce, VTD_PAGE_SIZE);
@@ -3723,10 +3725,11 @@ static inline bool has_external_pci(void)
static int __init platform_optin_force_iommu(void)
{
- if (!dmar_platform_optin() || no_platform_optin || !has_external_pci())
+ if (no_iommu || !dmar_platform_optin() || no_platform_optin ||
+ !has_external_pci())
return 0;
- if (no_iommu || dmar_disabled)
+ if (dmar_disabled)
pr_info("Intel-IOMMU force enabled due to platform opt in\n");
/*
@@ -3737,7 +3740,6 @@ static int __init platform_optin_force_iommu(void)
iommu_set_default_passthrough(false);
dmar_disabled = 0;
- no_iommu = 0;
return 1;
}
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index e6a3e7065616..d1a5078ab8c4 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -910,6 +910,7 @@ static inline bool ecmd_has_pmu_essential(struct intel_iommu *iommu)
extern int dmar_disabled;
extern int intel_iommu_enabled;
+extern int intel_iommu_tboot_noforce;
#else
static inline int iommu_calculate_agaw(struct intel_iommu *iommu)
{
@@ -922,6 +923,7 @@ static inline int iommu_calculate_max_sagaw(struct intel_iommu *iommu)
#define dmar_disabled (1)
#define intel_iommu_enabled (0)
#define intel_iommu_sm (0)
+#define intel_iommu_tboot_noforce (0)
#endif
static inline const char *decode_prq_descriptor(char *str, size_t size,
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 79d89bad5132..be94bb16f627 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -707,7 +707,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
iommu = devm_kzalloc(&pdev->dev, sizeof(*iommu), GFP_KERNEL);
if (!iommu)
- return -ENODEV;
+ return -ENOMEM;
iommu->dev = &pdev->dev;
INIT_LIST_HEAD(&iommu->ctx_list);
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 5cd32a7c9d36..e462080f8c9f 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -78,7 +78,7 @@
/* MTK generation one iommu HW only support 4K size mapping */
#define MT2701_IOMMU_PAGE_SHIFT 12
#define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT)
-#define MT2701_LARB_NR_MAX 3
+#define MT2701_LARB_NR_MAX 4
/*
* MTK m4u support 4GB iova address space, and only support 4K page
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c
index da308be6c487..474b58858dca 100644
--- a/drivers/ipack/devices/ipoctal.c
+++ b/drivers/ipack/devices/ipoctal.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/kref.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/serial.h>
@@ -25,6 +26,8 @@
static const struct tty_operations ipoctal_fops;
+static void ipoctal_release(struct kref *kref);
+
struct ipoctal_channel {
struct ipoctal_stats stats;
unsigned int nb_bytes;
@@ -49,6 +52,9 @@ struct ipoctal {
struct tty_driver *tty_drv;
u8 __iomem *mem8_space;
u8 __iomem *int_space;
+ struct kref kref;
+ struct module *carrier_owner;
+ bool removed;
};
static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan,
@@ -70,8 +76,14 @@ static void ipoctal_reset_channel(struct ipoctal_channel *channel)
static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
{
struct ipoctal_channel *channel;
+ struct ipoctal *ipoctal;
channel = dev_get_drvdata(tty->dev);
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+
+
+ if (ipoctal->removed)
+ return -ENODEV;
/*
* Enable RX. TX will be enabled when
@@ -95,6 +107,7 @@ static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty)
if (res)
goto err_put_carrier;
+ kref_get(&ipoctal->kref);
tty->driver_data = channel;
return 0;
@@ -158,9 +171,7 @@ static int ipoctal_get_icount(struct tty_struct *tty,
static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
{
struct tty_port *port = &channel->tty_port;
- unsigned char value;
- unsigned char flag;
- u8 isr;
+ u8 isr, value, flag;
do {
value = ioread8(&channel->regs->r.rhr);
@@ -202,8 +213,8 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
static void ipoctal_irq_tx(struct ipoctal_channel *channel)
{
- unsigned char value;
unsigned int *pointer_write = &channel->pointer_write;
+ u8 value;
if (channel->nb_bytes == 0)
return;
@@ -436,11 +447,11 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
return res;
}
-static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
- const u8 *buf, int count)
+static inline size_t ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
+ const u8 *buf, size_t count)
{
unsigned long flags;
- int i;
+ size_t i;
unsigned int *pointer_read = &channel->pointer_read;
/* Copy the bytes from the user buffer to the internal one */
@@ -462,7 +473,12 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf,
size_t count)
{
struct ipoctal_channel *channel = tty->driver_data;
- unsigned int char_copied;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
+ size_t char_copied;
+
+
+ if (ipoctal->removed || !channel->tty_port.xmit_buf)
+ return 0;
char_copied = ipoctal_copy_write_buffer(channel, buf, count);
@@ -503,8 +519,13 @@ static void ipoctal_set_termios(struct tty_struct *tty,
unsigned char mr2 = 0;
unsigned char csr = 0;
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
speed_t baud;
+
+ if (ipoctal->removed)
+ return;
+
cflag = tty->termios.c_cflag;
/* Disable and reset everything before change the setup */
@@ -633,10 +654,16 @@ static void ipoctal_hangup(struct tty_struct *tty)
{
unsigned long flags;
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
if (channel == NULL)
return;
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+
+ if (ipoctal->removed)
+ return;
+
spin_lock_irqsave(&channel->lock, flags);
channel->nb_bytes = 0;
channel->pointer_read = 0;
@@ -653,10 +680,16 @@ static void ipoctal_hangup(struct tty_struct *tty)
static void ipoctal_shutdown(struct tty_struct *tty)
{
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
if (channel == NULL)
return;
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+
+ if (ipoctal->removed)
+ return;
+
ipoctal_reset_channel(channel);
tty_port_set_initialized(&channel->tty_port, false);
}
@@ -666,8 +699,9 @@ static void ipoctal_cleanup(struct tty_struct *tty)
struct ipoctal_channel *channel = tty->driver_data;
struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
- /* release the carrier driver */
- ipack_put_carrier(ipoctal->dev);
+ /* release the carrier driver via cached owner */
+ module_put(ipoctal->carrier_owner);
+ kref_put(&ipoctal->kref, ipoctal_release);
}
static const struct tty_operations ipoctal_fops = {
@@ -685,6 +719,13 @@ static const struct tty_operations ipoctal_fops = {
.cleanup = ipoctal_cleanup,
};
+static void ipoctal_release(struct kref *kref)
+{
+ struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref);
+
+ kfree(ipoctal);
+}
+
static int ipoctal_probe(struct ipack_device *dev)
{
int res;
@@ -694,7 +735,10 @@ static int ipoctal_probe(struct ipack_device *dev)
if (ipoctal == NULL)
return -ENOMEM;
+ kref_init(&ipoctal->kref);
+
ipoctal->dev = dev;
+ ipoctal->carrier_owner = dev->bus->owner;
res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot);
if (res)
goto out_uninst;
@@ -703,7 +747,7 @@ static int ipoctal_probe(struct ipack_device *dev)
return 0;
out_uninst:
- kfree(ipoctal);
+ kref_put(&ipoctal->kref, ipoctal_release);
return res;
}
@@ -711,6 +755,8 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
{
int i;
+ ipoctal->removed = true;
+
ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
for (i = 0; i < NR_CHANNELS; i++) {
@@ -727,7 +773,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
tty_unregister_driver(ipoctal->tty_drv);
kfree(ipoctal->tty_drv->name);
tty_driver_kref_put(ipoctal->tty_drv);
- kfree(ipoctal);
+ kref_put(&ipoctal->kref, ipoctal_release);
}
static void ipoctal_remove(struct ipack_device *idev)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index d3ff3ab57d4d..53ca99b9d261 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -4474,6 +4474,13 @@ static int its_vpe_init(struct its_vpe *vpe)
static void its_vpe_teardown(struct its_vpe *vpe)
{
+ /*
+ * If vpt_page is NULL, then its_vpe_init() has failed, and
+ * there is nothing to do as no resource has been allocated.
+ */
+ if (vpe->vpt_page == NULL)
+ return;
+
its_vpe_db_proxy_unmap(vpe);
its_vpe_id_free(vpe->vpe_id);
its_free_pending_table(vpe->vpt_page);
@@ -4553,8 +4560,10 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
irqd_set_resend_when_in_progress(irq_get_irq_data(virq + i));
}
- if (err)
+ if (err) {
+ its_vpe_teardown(vm->vpes[i]);
its_vpe_irq_domain_free(domain, virq, i);
+ }
return err;
}
@@ -5168,7 +5177,7 @@ static int __init its_probe_one(struct its_node *its)
err = its_init_domain(its);
if (err)
- goto out_free_tables;
+ goto out_free_collection;
raw_spin_lock(&its_lock);
list_add(&its->entry, &its_nodes);
@@ -5176,6 +5185,8 @@ static int __init its_probe_one(struct its_node *its)
return 0;
+out_free_collection:
+ kfree(its->collections);
out_free_tables:
its_free_tables(its);
out_free_cmd:
@@ -5584,9 +5595,13 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
}
err = its_probe_one(its);
- if (!err)
- return 0;
+ if (err)
+ goto probe_err;
+
+ return 0;
+probe_err:
+ its_node_destroy(its);
node_err:
iort_deregister_domain_token(its_entry->translation_id);
dom_err:
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index 244a8d489cac..58881d313979 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -64,20 +64,6 @@ struct mbigen_device {
void __iomem *base;
};
-static inline unsigned int get_mbigen_node_offset(unsigned int nid)
-{
- unsigned int offset = nid * MBIGEN_NODE_OFFSET;
-
- /*
- * To avoid touched clear register in unexpected way, we need to directly
- * skip clear register when access to more than 10 mbigen nodes.
- */
- if (nid >= (REG_MBIGEN_CLEAR_OFFSET / MBIGEN_NODE_OFFSET))
- offset += MBIGEN_NODE_OFFSET;
-
- return offset;
-}
-
static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
{
unsigned int nid, pin;
@@ -86,7 +72,8 @@ static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
pin = hwirq % IRQS_PER_MBIGEN_NODE;
- return pin * 4 + get_mbigen_node_offset(nid) + REG_MBIGEN_VEC_OFFSET;
+ return pin * 4 + nid * MBIGEN_NODE_OFFSET
+ + REG_MBIGEN_VEC_OFFSET;
}
static inline void get_mbigen_type_reg(irq_hw_number_t hwirq,
@@ -101,7 +88,8 @@ static inline void get_mbigen_type_reg(irq_hw_number_t hwirq,
*mask = 1 << (irq_ofst % 32);
ofst = irq_ofst / 32 * 4;
- *addr = ofst + get_mbigen_node_offset(nid) + REG_MBIGEN_TYPE_OFFSET;
+ *addr = ofst + nid * MBIGEN_NODE_OFFSET
+ + REG_MBIGEN_TYPE_OFFSET;
}
static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 49b446b396f9..b7913a224a74 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -176,6 +176,7 @@ static int irqc_probe(struct platform_device *pdev)
goto err_runtime_pm_disable;
}
+ p->irq_domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC;
ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs,
1, "irqc", handle_level_irq,
0, 0, IRQ_GC_INIT_NESTED_LOCK);
diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c
index 5a7836186fd4..05b364d500bd 100644
--- a/drivers/irqchip/irq-renesas-rzg2l.c
+++ b/drivers/irqchip/irq-renesas-rzg2l.c
@@ -100,7 +100,7 @@ static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq
* falling/rising-edge.
*/
if ((iscr & bit) && (iitsr & IITSR_IITSEL_MASK(hw_irq))) {
- writel_relaxed(iscr & ~bit, priv->base + ISCR);
+ writel_relaxed(~bit, priv->base + ISCR);
/*
* Enforce that the posted write is flushed to prevent that the
* just handled interrupt is raised again.
@@ -116,7 +116,7 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir
reg = readl_relaxed(priv->base + TSCR);
if (reg & bit) {
- writel_relaxed(reg & ~bit, priv->base + TSCR);
+ writel_relaxed(~bit, priv->base + TSCR);
/*
* Enforce that the posted write is flushed to prevent that the
* just handled interrupt is raised again.
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index bf8bb8fc007c..9f8a7511ec3f 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -283,9 +283,9 @@ static void pca9532_gpio_set_value(struct gpio_chip *gc, unsigned offset, int va
struct pca9532_led *led = &data->leds[offset];
if (val)
- led->state = PCA9532_ON;
- else
led->state = PCA9532_OFF;
+ else
+ led->state = PCA9532_ON;
pca9532_setled(led);
}
@@ -303,7 +303,7 @@ static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset)
static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
/* To use as input ensure pin is not driven */
- pca9532_gpio_set_value(gc, offset, 0);
+ pca9532_gpio_set_value(gc, offset, 1);
return 0;
}
@@ -353,10 +353,14 @@ static int pca9532_configure(struct i2c_client *client,
for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
- i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
- data->pwm[i]);
- i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
- data->psc[i]);
+ err = i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
+ data->pwm[i]);
+ if (err < 0)
+ return err;
+ err = i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
+ data->psc[i]);
+ if (err < 0)
+ return err;
}
for (i = 0; i < data->chip_info->num_leds; i++) {
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 1d4191cb9138..6efeadf89db2 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -93,12 +93,11 @@ struct pcc_chan_reg {
* @plat_irq: platform interrupt
* @type: PCC subspace type
* @plat_irq_flags: platform interrupt flags
- * @chan_in_use: this flag is used just to check if the interrupt needs
- * handling when it is shared. Since only one transfer can occur
- * at a time and mailbox takes care of locking, this flag can be
- * accessed without a lock. Note: the type only support the
- * communication from OSPM to Platform, like type3, use it, and
- * other types completely ignore it.
+ * @chan_in_use: lockless flag used by type 3 initiator subspaces to filter
+ * platform interrupts. Only one transfer can occur at a time, but
+ * the interrupt handler may sample the flag on another CPU, so all
+ * accesses must use READ_ONCE() or WRITE_ONCE(). Other subspace
+ * types do not test it.
*/
struct pcc_chan_info {
struct pcc_mbox_chan chan;
@@ -333,8 +332,13 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))
return IRQ_NONE;
+ /*
+ * Initiator subspaces use this flag to filter shared interrupts. Use
+ * READ_ONCE() to sample the lockless flag written by pcc_send_data()
+ * on another CPU.
+ */
if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&
- !pchan->chan_in_use)
+ !READ_ONCE(pchan->chan_in_use))
return IRQ_NONE;
if (!pcc_mbox_cmd_complete_check(pchan))
@@ -344,12 +348,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
return IRQ_NONE;
/*
- * Clear this flag after updating interrupt ack register and just
- * before mbox_chan_received_data() which might call pcc_send_data()
- * where the flag is set again to start new transfer. This is
- * required to avoid any possible race in updatation of this flag.
+ * Clear this flag after updating the interrupt ack register and before
+ * notifying the client and mailbox core. mbox_chan_txdone() may submit
+ * the next queued transfer and set the flag again. Use WRITE_ONCE() for
+ * the lockless update observed by the send and interrupt paths.
*/
- pchan->chan_in_use = false;
+ WRITE_ONCE(pchan->chan_in_use, false);
mbox_chan_received_data(chan, NULL);
check_and_ack(pchan, chan);
@@ -372,6 +376,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{
+ struct pcc_mbox_chan *pcc_mchan;
struct pcc_chan_info *pchan;
struct mbox_chan *chan;
int rc;
@@ -390,7 +395,14 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
if (rc)
return ERR_PTR(rc);
- return &pchan->chan;
+ pcc_mchan = &pchan->chan;
+ pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
+ pcc_mchan->shmem_size);
+ if (pcc_mchan->shmem)
+ return pcc_mchan;
+
+ mbox_free_channel(chan);
+ return ERR_PTR(-ENXIO);
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
@@ -419,25 +431,6 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
-int pcc_mbox_ioremap(struct mbox_chan *chan)
-{
- struct pcc_chan_info *pchan_info;
- struct pcc_mbox_chan *pcc_mbox_chan;
-
- if (!chan || !chan->cl)
- return -1;
- pchan_info = chan->con_priv;
- pcc_mbox_chan = &pchan_info->chan;
-
- pcc_mbox_chan->shmem = acpi_os_ioremap(pcc_mbox_chan->shmem_base_addr,
- pcc_mbox_chan->shmem_size);
- if (!pcc_mbox_chan->shmem)
- return -ENXIO;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcc_mbox_ioremap);
-
/**
* pcc_send_data - Called from Mailbox Controller code. Used
* here only to ring the channel doorbell. The PCC client
@@ -459,9 +452,18 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
if (ret)
return ret;
+ /*
+ * Set chan_in_use before ringing the doorbell so a fast completion
+ * interrupt is not mistaken for a shared interrupt from another
+ * subspace. Use WRITE_ONCE() for the lockless flag update. The
+ * ordered I/O accessor used to ring the doorbell orders this store
+ * before the platform is notified.
+ */
+ if (pchan->plat_irq > 0)
+ WRITE_ONCE(pchan->chan_in_use, true);
ret = pcc_chan_reg_read_modify_write(&pchan->db);
- if (!ret && pchan->plat_irq > 0)
- pchan->chan_in_use = true;
+ if (ret && pchan->plat_irq > 0)
+ WRITE_ONCE(pchan->chan_in_use, false);
return ret;
}
diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
index f597a1bd5684..6fbf98ddb73e 100644
--- a/drivers/mailbox/qcom-ipcc.c
+++ b/drivers/mailbox/qcom-ipcc.c
@@ -165,7 +165,7 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
{
struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);
struct qcom_ipcc_chan_info *mchan;
- struct mbox_chan *chan;
+ struct mbox_chan *chan, *free_chan = NULL;
struct device *dev;
int chan_id;
@@ -178,16 +178,21 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
chan = &ipcc->chans[chan_id];
mchan = chan->con_priv;
- if (!mchan)
- break;
- else if (mchan->client_id == ph->args[0] &&
- mchan->signal_id == ph->args[1])
+ if (!mchan) {
+ /* Keep scanning past holes to reject duplicate channel requests. */
+ if (!free_chan)
+ free_chan = chan;
+ } else if (mchan->client_id == ph->args[0] &&
+ mchan->signal_id == ph->args[1]) {
return ERR_PTR(-EBUSY);
+ }
}
- if (chan_id >= mbox->num_chans)
+ if (!free_chan)
return ERR_PTR(-EBUSY);
+ chan = free_chan;
+
mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL);
if (!mchan)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c
index 4d966cb2ed03..5775362de508 100644
--- a/drivers/mailbox/rockchip-mailbox.c
+++ b/drivers/mailbox/rockchip-mailbox.c
@@ -201,19 +201,10 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
/* Each channel has two buffers for A2B and B2A */
mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2);
- mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox");
- if (IS_ERR(mb->pclk)) {
- ret = PTR_ERR(mb->pclk);
- dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n",
- ret);
- return ret;
- }
-
- ret = clk_prepare_enable(mb->pclk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret);
- return ret;
- }
+ mb->pclk = devm_clk_get_enabled(&pdev->dev, "pclk_mailbox");
+ if (IS_ERR(mb->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mb->pclk),
+ "failed to get and enable pclk_mailbox clock\n");
for (i = 0; i < mb->mbox.num_chans; i++) {
irq = platform_get_irq(pdev, i);
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index b7770e2aa77b..2fe173a9880b 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct era_metadata *md)
static int metadata_take_snap(struct era_metadata *md)
{
int r, inc;
+ dm_block_t location;
struct dm_block *clone;
if (md->metadata_snap != SUPERBLOCK_LOCATION) {
@@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era_metadata *md)
r = dm_sm_inc_block(md->sm, md->writeset_tree_root);
if (r) {
DMERR("%s: couldn't inc writeset tree root", __func__);
+ location = dm_block_location(clone);
dm_tm_unlock(md->tm, clone);
+ dm_sm_dec_block(md->sm, location);
return r;
}
@@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct era_metadata *md)
if (r) {
DMERR("%s: couldn't inc era tree root", __func__);
dm_sm_dec_block(md->sm, md->writeset_tree_root);
+ location = dm_block_location(clone);
dm_tm_unlock(md->tm, clone);
+ dm_sm_dec_block(md->sm, location);
return r;
}
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index 5aace6ee6d47..23302526b9d7 100644
--- a/drivers/md/dm-log-userspace-base.c
+++ b/drivers/md/dm-log-userspace-base.c
@@ -139,6 +139,7 @@ static int build_constructor_string(struct dm_target *ti,
str_size += strlen(argv[i]) + 1; /* +1 for space between args */
str_size += 20; /* Max number of chars in a printed u64 number */
+ str_size++; /* For NUL-terminator */
str = kzalloc(str_size, GFP_KERNEL);
if (!str) {
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index 6b1a554eb7d4..7ec8171ac929 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -175,8 +175,10 @@ static void dm_stat_free(struct rcu_head *head)
kfree(s->program_id);
kfree(s->aux_data);
for_each_possible_cpu(cpu) {
- dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
- dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+ if (s->stat_percpu[cpu]) {
+ dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
+ dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+ }
}
dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size);
dm_kvfree(s, s->shared_alloc_size);
diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c
index dfd9fb52a6f3..50e1c7211c37 100644
--- a/drivers/md/dm-switch.c
+++ b/drivers/md/dm-switch.c
@@ -184,7 +184,7 @@ static void switch_region_table_write(struct switch_ctx *sctx, unsigned long reg
pte = sctx->region_table[region_index];
pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit);
pte |= (region_table_slot_t)value << bit;
- sctx->region_table[region_index] = pte;
+ WRITE_ONCE(sctx->region_table[region_index], pte);
}
/*
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 1d03536fded0..0512d8bdf7e0 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2369,9 +2369,10 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t)
*/
mutex_lock(&md->table_devices_lock);
r = add_disk(md->disk);
- mutex_unlock(&md->table_devices_lock);
- if (r)
+ if (r) {
+ mutex_unlock(&md->table_devices_lock);
return r;
+ }
/*
* Register the holder relationship for devices added before the disk
@@ -2382,18 +2383,21 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t)
if (r)
goto out_undo_holders;
}
+ mutex_unlock(&md->table_devices_lock);
r = dm_sysfs_init(md);
if (r)
- goto out_undo_holders;
+ goto lock_out_undo_holders;
md->type = type;
+
return 0;
+lock_out_undo_holders:
+ mutex_lock(&md->table_devices_lock);
out_undo_holders:
list_for_each_entry_continue_reverse(td, &md->table_devices, list)
bd_unlink_disk_holder(td->dm_dev.bdev, md->disk);
- mutex_lock(&md->table_devices_lock);
del_gendisk(md->disk);
mutex_unlock(&md->table_devices_lock);
return r;
@@ -2869,7 +2873,7 @@ int dm_resume(struct mapped_device *md)
r = -EINVAL;
mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING);
- if (!dm_suspended_md(md))
+ if (!dm_suspended_md(md) || test_bit(DMF_FREEING, &md->flags))
goto out;
if (dm_suspended_internally_md(md)) {
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index ace28eb81111..03be53313e3c 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -227,8 +227,8 @@ static int __write_sb_page(struct md_rdev *rdev, struct bitmap *bitmap,
struct block_device *bdev;
struct mddev *mddev = bitmap->mddev;
struct bitmap_storage *store = &bitmap->storage;
- unsigned int bitmap_limit = (bitmap->storage.file_pages - pg_index) <<
- PAGE_SHIFT;
+ unsigned long num_pages = bitmap->storage.file_pages;
+ unsigned int bitmap_limit = (num_pages - pg_index % num_pages) << PAGE_SHIFT;
loff_t sboff, offset = mddev->bitmap_info.offset;
sector_t ps = pg_index * PAGE_SIZE / SECTOR_SIZE;
unsigned int size = PAGE_SIZE;
@@ -237,7 +237,7 @@ static int __write_sb_page(struct md_rdev *rdev, struct bitmap *bitmap,
bdev = (rdev->meta_bdev) ? rdev->meta_bdev : rdev->bdev;
/* we compare length (page numbers), not page offset. */
- if ((pg_index - store->sb_index) == store->file_pages - 1) {
+ if ((pg_index - store->sb_index) == num_pages - 1) {
unsigned int last_page_size = store->bytes & (PAGE_SIZE - 1);
if (last_page_size == 0)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index b086cbf24086..3c605ba3801a 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1726,6 +1726,13 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
rdev->bb_page, REQ_OP_READ, true))
return -EIO;
bbp = (__le64 *)page_address(rdev->bb_page);
+
+ /* check for badblocks api. */
+ if (sb->bblog_shift >= BITS_PER_TYPE(sector_t)) {
+ pr_err("md: %pg: bogus bblog_shift %u for badblocks.\n",
+ rdev->bdev, sb->bblog_shift);
+ return -EINVAL;
+ }
rdev->badblocks.shift = sb->bblog_shift;
for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
u64 bb = le64_to_cpu(*bbp);
@@ -8764,6 +8771,8 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
md_io_clone->mddev = mddev;
if (blk_queue_io_stat(bdev->bd_disk->queue))
md_io_clone->start_time = bio_start_io_acct(*bio);
+ else
+ md_io_clone->start_time = 0;
if (bio_data_dir(*bio) == WRITE && mddev->bitmap) {
md_io_clone->offset = (*bio)->bi_iter.bi_sector;
diff --git a/drivers/md/persistent-data/dm-array.c b/drivers/md/persistent-data/dm-array.c
index b1fdfe53e937..5e9c17307671 100644
--- a/drivers/md/persistent-data/dm-array.c
+++ b/drivers/md/persistent-data/dm-array.c
@@ -225,6 +225,14 @@ static int get_ablock(struct dm_array_info *info, dm_block_t b,
return r;
*ab = dm_block_data(*block);
+ if (le32_to_cpu((*ab)->value_size) != info->value_type.size) {
+ DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__,
+ le32_to_cpu((*ab)->value_size),
+ info->value_type.size);
+ dm_tm_unlock(info->btree_info.tm, *block);
+ return -EILSEQ;
+ }
+
return 0;
}
@@ -287,6 +295,14 @@ static int __shadow_ablock(struct dm_array_info *info, dm_block_t b,
return r;
*ab = dm_block_data(*block);
+ if (le32_to_cpu((*ab)->value_size) != info->value_type.size) {
+ DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__,
+ le32_to_cpu((*ab)->value_size),
+ info->value_type.size);
+ dm_tm_unlock(info->btree_info.tm, *block);
+ return -EILSEQ;
+ }
+
if (inc)
inc_ablock_entries(info, *ab);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 836123f2929a..bd57fc5ad5dd 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -3357,8 +3357,6 @@ static void *raid1_takeover(struct mddev *mddev)
mddev->new_chunk_sectors = 0;
conf = setup_conf(mddev);
if (!IS_ERR(conf)) {
- /* Array must appear to be quiesced */
- conf->array_frozen = 1;
mddev_clear_unsupported_flags(mddev,
UNSUPPORTED_MDDEV_FLAGS);
}
diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c
index eaea57aee602..caea1c5ffee6 100644
--- a/drivers/md/raid5-ppl.c
+++ b/drivers/md/raid5-ppl.c
@@ -645,8 +645,10 @@ static void ppl_do_flush(struct ppl_io_unit *io)
log->disk_flush_bitmap = 0;
for (i = flushed_disks ; i < raid_disks; i++) {
- if (atomic_dec_and_test(&io->pending_flushes))
+ if (atomic_dec_and_test(&io->pending_flushes)) {
ppl_io_unit_finished(io);
+ break;
+ }
}
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 6eb94e466f90..18716bf08306 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3587,7 +3587,7 @@ static void __add_stripe_bio(struct stripe_head *sh, struct bio *bi,
sh->dev[dd_idx].sector);
if (conf->mddev->bitmap && firstwrite && !sh->batch_head) {
- sh->bm_seq = conf->seq_flush+1;
+ sh->bm_seq = READ_ONCE(conf->seq_flush) + 1;
set_bit(STRIPE_BIT_DELAY, &sh->state);
}
}
@@ -5807,7 +5807,7 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
}
spin_unlock_irq(&sh->stripe_lock);
if (conf->mddev->bitmap) {
- sh->bm_seq = conf->seq_flush + 1;
+ sh->bm_seq = READ_ONCE(conf->seq_flush) + 1;
set_bit(STRIPE_BIT_DELAY, &sh->state);
}
@@ -5971,8 +5971,11 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
sectors_per_chunk = conf->chunk_sectors *
(conf->raid_disks - conf->max_degraded);
- start = round_down(start, sectors_per_chunk);
- end = round_up(end, sectors_per_chunk);
+ sector_div(start, sectors_per_chunk);
+ start *= sectors_per_chunk;
+ if (sector_div(end, sectors_per_chunk))
+ end++;
+ end *= sectors_per_chunk;
start = raid5_compute_sector(conf, start, 0, &dd_idx, NULL);
end = raid5_compute_sector(conf, end, 0, &dd_idx, NULL);
@@ -5990,8 +5993,10 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
sectors_per_chunk = conf->prev_chunk_sectors *
(conf->previous_raid_disks - conf->max_degraded);
- prev_start = round_down(prev_start, sectors_per_chunk);
- prev_end = round_down(prev_end, sectors_per_chunk);
+ sector_div(prev_start, sectors_per_chunk);
+ prev_start *= sectors_per_chunk;
+ sector_div(prev_end, sectors_per_chunk);
+ prev_end *= sectors_per_chunk;
prev_start = raid5_compute_sector(conf, prev_start, 1, &dd_idx, NULL);
prev_end = raid5_compute_sector(conf, prev_end, 1, &dd_idx, NULL);
@@ -6847,11 +6852,13 @@ static void raid5d(struct md_thread *thread)
if (
!list_empty(&conf->bitmap_list)) {
/* Now is a good time to flush some bitmap updates */
- conf->seq_flush++;
+ int seq = conf->seq_flush + 1;
+
+ WRITE_ONCE(conf->seq_flush, seq);
spin_unlock_irq(&conf->device_lock);
md_bitmap_unplug(mddev->bitmap);
spin_lock_irq(&conf->device_lock);
- conf->seq_write = conf->seq_flush;
+ conf->seq_write = seq;
activate_bit_delay(conf, conf->temp_inactive_list);
}
raid5_activate_delayed(conf);
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 3b67e922f981..c7be3c812b8d 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -2155,9 +2155,13 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
* Unprocessed messages are aborted if userspace isn't doing
* any processing either.
*/
+ mutex_lock(&adap->lock);
if (!is_broadcast && !is_reply && !adap->follower_cnt &&
- !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+ !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) {
+ mutex_unlock(&adap->lock);
return cec_feature_abort(adap, msg);
+ }
+ mutex_unlock(&adap->lock);
break;
}
@@ -2170,10 +2174,12 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
* Send to the exclusive follower if there is one, otherwise send
* to all followers.
*/
+ mutex_lock(&adap->lock);
if (adap->cec_follower)
cec_queue_msg_fh(adap->cec_follower, msg);
else
cec_queue_msg_followers(adap, msg);
+ mutex_unlock(&adap->lock);
return 0;
}
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 74dff5f8d336..db07bc64cd67 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -399,9 +399,7 @@ void cec_unregister_adapter(struct cec_adapter *adap)
return;
#ifdef CONFIG_MEDIA_CEC_RC
- /* Note: rc_unregister also calls rc_free */
rc_unregister_device(adap->rc);
- adap->rc = NULL;
#endif
debugfs_remove_recursive(adap->cec_dir);
#ifdef CONFIG_CEC_NOTIFIER
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
index 330d5d5d86ab..4c91f96351ca 100644
--- a/drivers/media/cec/core/cec-pin.c
+++ b/drivers/media/cec/core/cec-pin.c
@@ -114,7 +114,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
return;
pin->adap->cec_pin_is_high = v;
- if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
+ if (atomic_read_acquire(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
u8 ev = v;
if (pin->work_pin_events_dropped) {
@@ -125,7 +125,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
pin->work_pin_events_wr =
(pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS;
- atomic_inc(&pin->work_pin_num_events);
+ atomic_inc_return_release(&pin->work_pin_num_events);
} else {
pin->work_pin_events_dropped = true;
pin->work_pin_events_dropped_cnt++;
@@ -1074,7 +1074,7 @@ static int cec_pin_thread_func(void *_adap)
pin->work_tx_ts);
}
- while (atomic_read(&pin->work_pin_num_events)) {
+ while (atomic_read_acquire(&pin->work_pin_num_events)) {
unsigned int idx = pin->work_pin_events_rd;
u8 v = pin->work_pin_events[idx];
@@ -1083,7 +1083,7 @@ static int cec_pin_thread_func(void *_adap)
v & CEC_PIN_EVENT_FL_DROPPED,
pin->work_pin_ts[idx]);
pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
- atomic_dec(&pin->work_pin_num_events);
+ atomic_dec_return_release(&pin->work_pin_num_events);
}
switch (atomic_xchg(&pin->work_irq_change,
diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c
index 51294b9b6cd5..42dab3ca8774 100644
--- a/drivers/media/cec/platform/meson/ao-cec-g12a.c
+++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c
@@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr,
}
static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
+ .name = "core",
.reg_bits = 8,
.val_bits = 8,
.reg_read = meson_ao_cec_g12a_read,
diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c
index bda9d254041a..35f5ea42a802 100644
--- a/drivers/media/cec/platform/stm32/stm32-cec.c
+++ b/drivers/media/cec/platform/stm32/stm32-cec.c
@@ -132,7 +132,8 @@ static void stm32_rx_done(struct stm32_cec *cec, u32 status)
u32 val;
regmap_read(cec->regmap, CEC_RXDR, &val);
- cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
+ if (cec->rx_msg.len < CEC_MAX_MSG_SIZE)
+ cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
}
if (cec->irq_status & RXEND) {
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index ebe95f028033..9bbd8f5596cf 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -905,9 +905,12 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
goto err;
mutex_unlock(&dev->v4l2_lock);
+
return 0;
err:
+ rtl2832_sdr_free_urbs(dev);
+ rtl2832_sdr_free_stream_bufs(dev);
rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
mutex_unlock(&dev->v4l2_lock);
@@ -1478,14 +1481,22 @@ static void rtl2832_sdr_remove(struct platform_device *pdev)
dev_dbg(&pdev->dev, "\n");
- mutex_lock(&dev->vb_queue_lock);
+ /*
+ * vb2_video_unregister_device() releases the vb2 queue, which
+ * triggers rtl2832_sdr_stop_streaming() if streaming is active.
+ * stop_streaming() uses dev->udev to free URBs and coherent DMA
+ * stream buffers via usb_free_coherent(), so it must run before
+ * dev->udev is cleared. vb2_video_unregister_device() locks
+ * vb_queue_lock internally and stop_streaming() locks v4l2_lock,
+ * so neither may be held by the caller.
+ */
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ vb2_video_unregister_device(&dev->vdev);
+
mutex_lock(&dev->v4l2_lock);
- /* No need to keep the urbs around after disconnection */
dev->udev = NULL;
- v4l2_device_disconnect(&dev->v4l2_dev);
- video_unregister_device(&dev->vdev);
mutex_unlock(&dev->v4l2_lock);
- mutex_unlock(&dev->vb_queue_lock);
+
v4l2_device_put(&dev->v4l2_dev);
module_put(pdev->dev.parent->driver->owner);
}
diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c
index 741d977a76f3..410223d9cf6e 100644
--- a/drivers/media/i2c/ov02a10.c
+++ b/drivers/media/i2c/ov02a10.c
@@ -821,18 +821,16 @@ static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10)
if (!ep)
return -ENXIO;
+ /* Optional indication of MIPI clock voltage unit */
+ if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
+ &clk_volt))
+ ov02a10->mipi_clock_voltage = clk_volt;
+
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
return ret;
- /* Optional indication of MIPI clock voltage unit */
- ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
- &clk_volt);
-
- if (!ret)
- ov02a10->mipi_clock_voltage = clk_volt;
-
for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
if (link_freq_menu_items[i] ==
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index dffdb475e433..ce47f5054c6a 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -1158,10 +1158,8 @@ static void ov7740_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
- mutex_destroy(&ov7740->mutex);
- v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
- media_entity_cleanup(&ov7740->subdev.entity);
v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&ov7740->subdev.entity);
ov7740_free_controls(ov7740);
pm_runtime_get_sync(&client->dev);
diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
index 3e22df36354f..e8196ea73739 100644
--- a/drivers/media/i2c/rdacm21.c
+++ b/drivers/media/i2c/rdacm21.c
@@ -589,10 +589,12 @@ static int rdacm21_probe(struct i2c_client *client)
ret = v4l2_async_register_subdev(&dev->sd);
if (ret)
- goto error_free_ctrls;
+ goto error_entity_cleanup;
return 0;
+error_entity_cleanup:
+ media_entity_cleanup(&dev->sd.entity);
error_free_ctrls:
v4l2_ctrl_handler_free(&dev->ctrls);
error:
@@ -607,6 +609,7 @@ static void rdacm21_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(&dev->sd);
v4l2_ctrl_handler_free(&dev->ctrls);
+ media_entity_cleanup(&dev->sd.entity);
i2c_unregister_device(dev->isp);
}
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 537ebd9fa8d7..15a7fcf0c3a7 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -532,8 +532,12 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
- if (!ret)
- return 0;
+ if (ret) {
+ data->kthread_vid_cap = NULL;
+ goto error_rpm_put;
+ }
+
+ return 0;
error_rpm_put:
pm_runtime_mark_last_busy(dev);
diff --git a/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c
index c57f87a68269..1356353d931c 100644
--- a/drivers/media/pci/cobalt/cobalt-alsa-main.c
+++ b/drivers/media/pci/cobalt/cobalt-alsa-main.c
@@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_stream *s)
err_exit_free:
if (sc != NULL)
snd_card_free(sc);
- kfree(cobsc);
err_exit:
return ret;
}
diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c
index 06e41f92092d..a4a319087238 100644
--- a/drivers/media/pci/cx23885/cimax2.c
+++ b/drivers/media/pci/cx23885/cimax2.c
@@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport *port)
if (NULL == state->ca.data)
return;
+ cancel_work_sync(&state->work);
dvb_ca_en50221_release(&state->ca);
kfree(state);
}
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index ac290f546413..49dca57181d8 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -878,6 +878,9 @@ static int get_resources(struct saa7164_dev *dev)
if (request_mem_region(pci_resource_start(dev->pci, 2),
pci_resource_len(dev->pci, 2), dev->name))
return 0;
+
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
}
printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
@@ -1000,8 +1003,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
dev->name, dev->pci->subsystem_vendor,
dev->pci->subsystem_device);
- saa7164_devcount--;
- return -ENODEV;
+ goto err_devlist;
}
/* PCI/e allocations */
@@ -1039,7 +1041,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
iounmap(dev->lmmio);
err_ioremap_bar0:
release_resources(dev);
-
+err_devlist:
scoped_guard(mutex, &devlist) {
list_del(&dev->devlist);
}
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
index 38a083ffe6c3..16af0e74b4dc 100644
--- a/drivers/media/pci/zoran/zoran_card.c
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -889,7 +889,7 @@ static int zoran_init_video_device(struct zoran *zr, struct video_device *video_
static void zoran_exit_video_devices(struct zoran *zr)
{
video_unregister_device(zr->video_dev);
- kfree(zr->video_dev);
+ zr->video_dev = NULL;
}
static int zoran_init_video_devices(struct zoran *zr)
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
index 05c3cd161aee..0b4b0f92a96e 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -107,7 +107,7 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
return ret;
/* The memory input can be routed to the first pipeline only. */
- for_each_active_route(&state->routing, route) {
+ for_each_active_route(routing, route) {
if (route->sink_pad == xbar->num_sinks - 1 &&
route->source_pad != xbar->num_sinks) {
dev_dbg(xbar->isi->dev,
@@ -145,10 +145,10 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar,
*/
for_each_active_route(&state->routing, route) {
if (route->source_pad != source_pad ||
- !(source_streams & BIT(route->source_stream)))
+ !(source_streams & BIT_ULL(route->source_stream)))
continue;
- sink_streams |= BIT(route->sink_stream);
+ sink_streams |= BIT_ULL(route->sink_stream);
sink_pad = route->sink_pad;
}
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
index f4a9f130e3cf..cb032738ef95 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
@@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
.fourcc = V4L2_PIX_FMT_XBGR32,
.type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
| MXC_ISI_VIDEO_M2M_CAP,
- .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
+ .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
.isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888,
.mem_planes = 1,
.color_planes = 1,
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 8eb9e32031f7..62d3bf1bddca 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
type++;
}
- return sizeof(*mode);
+ return mode->num_entries * sizeof(u32) + sizeof(*mode);
}
static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_caps, caps_arr, num_caps);
- return sizeof(*caps);
+ return num_caps * sizeof(*cap) + sizeof(u32);
}
static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
@@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
u32 entries = fmt->format_entries;
unsigned int i = 0;
u32 num_planes = 0;
- u32 size;
+ u32 size = 2 * sizeof(u32);
while (entries) {
num_planes = pinfo->num_planes;
@@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
if (pinfo->num_planes > MAX_PLANES)
break;
+ size += sizeof(*constr) * num_planes + 2 * sizeof(u32);
pinfo = (void *)pinfo + sizeof(*constr) * num_planes +
2 * sizeof(u32);
entries--;
@@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_raw_fmts, rawfmts, i);
- size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
- + 2 * sizeof(u32);
return size;
}
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index e2477e724637..6b1245343064 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -343,22 +343,25 @@ static int sunxi_ir_probe(struct platform_device *pdev)
ir->irq = platform_get_irq(pdev, 0);
if (ir->irq < 0) {
ret = ir->irq;
- goto exit_free_dev;
+ goto exit_unregister_dev;
}
ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
if (ret) {
dev_err(dev, "failed request irq\n");
- goto exit_free_dev;
+ goto exit_unregister_dev;
}
ret = sunxi_ir_hw_init(dev);
if (ret)
- goto exit_free_dev;
+ goto exit_unregister_dev;
dev_info(dev, "initialized sunXi IR driver\n");
return 0;
+exit_unregister_dev:
+ rc_unregister_device(ir->rc);
+
exit_free_dev:
rc_free_device(ir->rc);
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index 6f0e20df74e9..2f15fb433e7a 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -61,11 +61,11 @@ struct pixfmt_info {
};
static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = {
- V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1
+ V4L2_PIX_FMT_FWHT, 0, 4, 1, 1, 1, 1, 1, 0, 1
};
static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = {
- V4L2_PIX_FMT_FWHT_STATELESS, 0, 3, 1, 1, 1, 1, 1, 0, 1
+ V4L2_PIX_FMT_FWHT_STATELESS, 0, 4, 1, 1, 1, 1, 1, 0, 1
};
static void vicodec_dev_release(struct device *dev)
diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index aa944270e716..00e9b5603d66 100644
--- a/drivers/media/test-drivers/vimc/vimc-capture.c
+++ b/drivers/media/test-drivers/vimc/vimc-capture.c
@@ -177,8 +177,8 @@ static int vimc_capture_enum_framesizes(struct file *file, void *fh,
if (fsize->index)
return -EINVAL;
- /* Only accept code in the pix map table */
- vpix = vimc_pix_map_by_code(fsize->pixel_format);
+ /* Only accept pixel_format in the pix map table */
+ vpix = vimc_pix_map_by_pixelformat(fsize->pixel_format);
if (!vpix)
return -EINVAL;
diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c
index 32ea473f3f49..c6c1d1cdaa4c 100644
--- a/drivers/media/tuners/tda18250.c
+++ b/drivers/media/tuners/tda18250.c
@@ -440,8 +440,8 @@ static int tda18250_pll_calc(struct dvb_frontend *fe, u8 *rdiv,
goto err;
exp = (uval & 0x70) >> 4;
- if (exp > 5)
- exp = 0;
+ if (exp == 0 || exp > 5)
+ exp = 1;
lopd = 1 << (exp - 1);
scale = uval & 0x0f;
fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency);
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index 3c5c0b163708..6d5256017b6f 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -464,14 +464,21 @@ static void airspy_disconnect(struct usb_interface *intf)
dev_dbg(s->dev, "\n");
- mutex_lock(&s->vb_queue_lock);
+ /*
+ * vb2_video_unregister_device() releases the vb2 queue, which
+ * triggers airspy_stop_streaming() if streaming is active.
+ * stop_streaming() dereferences s->udev via airspy_ctrl_msg() and
+ * airspy_free_stream_bufs(), so it must run before s->udev is
+ * cleared. vb2_video_unregister_device() locks vb_queue_lock
+ * internally and stop_streaming() locks v4l2_lock, so neither may
+ * be held by the caller.
+ */
+ v4l2_device_disconnect(&s->v4l2_dev);
+ vb2_video_unregister_device(&s->vdev);
+
mutex_lock(&s->v4l2_lock);
- /* No need to keep the urbs around after disconnection */
s->udev = NULL;
- v4l2_device_disconnect(&s->v4l2_dev);
- video_unregister_device(&s->vdev);
mutex_unlock(&s->v4l2_lock);
- mutex_unlock(&s->vb_queue_lock);
v4l2_device_put(&s->v4l2_dev);
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index e23b8ccd79d4..8a2d45b5392b 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -899,7 +899,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
if (rc)
return rc;
- if (vb2_is_busy(&dev->vidq)) {
+ if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) {
dev_err(dev->dev, "%s: queue busy\n", __func__);
return -EBUSY;
}
@@ -934,7 +934,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
if (dev->norm == norm)
return 0;
- if (vb2_is_busy(&dev->vidq))
+ if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq))
return -EBUSY;
dev->norm = norm;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index bae76023cf71..62887cf6afa5 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -3396,6 +3396,7 @@ static void request_module_async(struct work_struct *work)
* intf. Don't register extensions twice on those devices.
*/
if (dev->is_audio_only) {
+ em28xx_init_extension(dev);
#if defined(CONFIG_MODULES) && defined(MODULE)
request_module("em28xx-alsa");
#endif
@@ -3624,8 +3625,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
retval = -ENODEV;
goto err_deinit_media;
}
- em28xx_init_extension(dev);
-
return 0;
}
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 61d7bf701d57..560d525033ff 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -1133,6 +1133,8 @@ void em28xx_close_extension(struct em28xx *dev)
ops->fini(dev);
}
}
+ if (dev->dev_next)
+ list_del(&dev->dev_next->devlist);
list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
}
diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c
index 9a6bd87fce03..64bf668b7ca0 100644
--- a/drivers/media/usb/go7007/snd-go7007.c
+++ b/drivers/media/usb/go7007/snd-go7007.c
@@ -195,6 +195,13 @@ static const struct snd_device_ops go7007_snd_device_ops = {
.dev_free = go7007_snd_free,
};
+static void go7007_snd_card_free(struct snd_card *card)
+{
+ struct go7007 *go = card->private_data;
+
+ v4l2_device_put(&go->v4l2_dev);
+}
+
int go7007_snd_init(struct go7007 *go)
{
static int dev;
@@ -245,6 +252,8 @@ int go7007_snd_init(struct go7007 *go)
gosnd->substream = NULL;
go->snd_context = gosnd;
v4l2_device_get(&go->v4l2_dev);
+ gosnd->card->private_data = go;
+ gosnd->card->private_free = go7007_snd_card_free;
++dev;
return 0;
@@ -263,7 +272,6 @@ int go7007_snd_remove(struct go7007 *go)
snd_card_disconnect(gosnd->card);
snd_card_free_when_closed(gosnd->card);
- v4l2_device_put(&go->v4l2_dev);
return 0;
}
EXPORT_SYMBOL(go7007_snd_remove);
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 6f78be3c42d6..faf7d0f28bd4 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -617,6 +617,12 @@ static void s2255_fillbuff(struct s2255_vc *vc,
break;
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
+ if (jpgsize < 0 ||
+ jpgsize > vb2_plane_size(&buf->vb.vb2_buf, 0)) {
+ dprintk(dev, 1, "bad JPEG frame size %d\n",
+ jpgsize);
+ break;
+ }
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
memcpy(vbuf, tmpbuf, jpgsize);
break;
@@ -2282,6 +2288,11 @@ static int s2255_probe(struct usb_interface *interface,
}
/* check the firmware is valid */
fw_size = dev->fw_data->fw->size;
+ if (fw_size < 8) {
+ dev_err(&interface->dev, "Firmware invalid: too small.\n");
+ retval = -ENODEV;
+ goto errorFWMARKER;
+ }
pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
if (*pdata != S2255_FW_MARKER) {
diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c
index 333bd305a4f9..ae0a14e5ed2a 100644
--- a/drivers/media/usb/usbtv/usbtv-audio.c
+++ b/drivers/media/usb/usbtv/usbtv-audio.c
@@ -317,6 +317,13 @@ static const struct snd_pcm_ops snd_usbtv_pcm_ops = {
.pointer = snd_usbtv_pointer,
};
+static void usbtv_audio_card_free(struct snd_card *card)
+{
+ struct usbtv *usbtv = card->private_data;
+
+ v4l2_device_put(&usbtv->v4l2_dev);
+}
+
int usbtv_audio_init(struct usbtv *usbtv)
{
int rv;
@@ -331,6 +338,10 @@ int usbtv_audio_init(struct usbtv *usbtv)
if (rv < 0)
return rv;
+ v4l2_device_get(&usbtv->v4l2_dev);
+ card->private_data = usbtv;
+ card->private_free = usbtv_audio_card_free;
+
strscpy(card->driver, usbtv->dev->driver->name, sizeof(card->driver));
strscpy(card->shortname, "usbtv", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 36df65475dad..9f2f98c627d7 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -388,7 +388,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
err_call_unbind:
v4l2_async_nf_call_unbind(notifier, sd, asc);
- list_del(&asc->asc_subdev_entry);
err_unregister_subdev:
if (registered)
@@ -894,12 +893,18 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
sd->subdev_notifier = NULL;
if (sd->asc_list.next) {
- list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
- asc_subdev_entry) {
- list_move(&asc->asc_entry,
- &asc->notifier->waiting_list);
-
- v4l2_async_unbind_subdev_one(asc->notifier, asc);
+ if (list_empty(&sd->asc_list)) {
+ /*
+ * If the sub-device was registered through other means
+ * than v4l2-async, there are no async connections but
+ * the sub-device may still well be registered.
+ * Unregister it now.
+ */
+ v4l2_device_unregister_subdev(sd);
+ } else {
+ list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
+ asc_subdev_entry)
+ v4l2_async_unbind_subdev_one(asc->notifier, asc);
}
}
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index c8d58604deea..5b6a5b37c196 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -1130,24 +1130,41 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
return -EINVAL;
}
- if (p_hdr10_mastering->white_point_x <
- V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW ||
- p_hdr10_mastering->white_point_x >
- V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH ||
- p_hdr10_mastering->white_point_y <
- V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW ||
- p_hdr10_mastering->white_point_y >
- V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH)
+ /*
+ * SMPTE ST 2086 Annex A documents that CTA 861-G uses
+ * (0, 0) to indicate that the white point chromaticity
+ * is unknown.
+ */
+ if (p_hdr10_mastering->white_point_x ||
+ p_hdr10_mastering->white_point_y) {
+ if (p_hdr10_mastering->white_point_x <
+ V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW ||
+ p_hdr10_mastering->white_point_x >
+ V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH ||
+ p_hdr10_mastering->white_point_y <
+ V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW ||
+ p_hdr10_mastering->white_point_y >
+ V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH)
+ return -EINVAL;
+ }
+
+ /*
+ * SMPTE ST 2086 Annex A documents that CTA 861-G uses zero
+ * maximum and minimum luminance values to indicate that
+ * the corresponding values are unknown.
+ */
+ if (p_hdr10_mastering->max_display_mastering_luminance &&
+ (p_hdr10_mastering->max_display_mastering_luminance <
+ V4L2_HDR10_MASTERING_MAX_LUMA_LOW ||
+ p_hdr10_mastering->max_display_mastering_luminance >
+ V4L2_HDR10_MASTERING_MAX_LUMA_HIGH))
return -EINVAL;
- if (p_hdr10_mastering->max_display_mastering_luminance <
- V4L2_HDR10_MASTERING_MAX_LUMA_LOW ||
- p_hdr10_mastering->max_display_mastering_luminance >
- V4L2_HDR10_MASTERING_MAX_LUMA_HIGH ||
- p_hdr10_mastering->min_display_mastering_luminance <
- V4L2_HDR10_MASTERING_MIN_LUMA_LOW ||
- p_hdr10_mastering->min_display_mastering_luminance >
- V4L2_HDR10_MASTERING_MIN_LUMA_HIGH)
+ if (p_hdr10_mastering->min_display_mastering_luminance &&
+ (p_hdr10_mastering->min_display_mastering_luminance <
+ V4L2_HDR10_MASTERING_MIN_LUMA_LOW ||
+ p_hdr10_mastering->min_display_mastering_luminance >
+ V4L2_HDR10_MASTERING_MIN_LUMA_HIGH))
return -EINVAL;
/* The following restriction comes from ITU-T Rec. H.265 spec */
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index c4d3d110c500..77bbf276ae89 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -1033,25 +1033,25 @@ int __video_register_device(struct video_device *vdev,
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
- vdev->dev.release = v4l2_device_release;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
-
- /* Increase v4l2_device refcount */
- v4l2_device_get(vdev->v4l2_dev);
-
mutex_lock(&videodev_lock);
ret = device_register(&vdev->dev);
if (ret < 0) {
mutex_unlock(&videodev_lock);
pr_err("%s: device_register failed\n", __func__);
- put_device(&vdev->dev);
- return ret;
+ goto cleanup;
}
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
pr_warn("%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
+ /* Increase v4l2_device refcount */
+ v4l2_device_get(vdev->v4l2_dev);
+
/* Part 5: Register the entity. */
ret = video_register_media_controller(vdev);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 519eaad0af5f..7a9456240fe5 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -582,6 +582,8 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
if (!link->remote_node)
goto err_put_remote_endpoint;
+ fwnode_handle_put(fwnode);
+
return 0;
err_put_remote_endpoint:
diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c
index e03b4d38fbb0..70ac09ed120c 100644
--- a/drivers/mfd/iqs62x.c
+++ b/drivers/mfd/iqs62x.c
@@ -237,7 +237,7 @@ static int iqs62x_firmware_parse(struct iqs62x_core *iqs62x,
fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos);
pos += sizeof(*fw_rec);
- if (pos + fw_rec->len - 1 > fw->size) {
+ if (!fw_rec->len || fw_rec->len - 1 > fw->size - pos) {
ret = -EINVAL;
break;
}
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
index da50eba10014..458b7334b323 100644
--- a/drivers/mfd/rave-sp.c
+++ b/drivers/mfd/rave-sp.c
@@ -63,6 +63,12 @@
#define RAVE_SP_TX_BUFFER_SIZE \
(RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE)
+enum rave_sp_frame_offset {
+ RAVE_SP_FRAME_CODE_OFFSET,
+ RAVE_SP_FRAME_ACK_ID_OFFSET,
+ RAVE_SP_FRAME_DATA_OFFSET,
+};
+
/**
* enum rave_sp_deframer_state - Possible state for de-framer
*
@@ -352,7 +358,7 @@ int rave_sp_exec(struct rave_sp *sp,
int command, ret = 0;
u8 ackid;
- command = sp->variant->cmd.translate(data[0]);
+ command = sp->variant->cmd.translate(data[RAVE_SP_FRAME_CODE_OFFSET]);
if (command < 0)
return command;
@@ -366,8 +372,8 @@ int rave_sp_exec(struct rave_sp *sp,
sp->reply = &reply;
mutex_unlock(&sp->reply_lock);
- data[0] = command;
- data[1] = ackid;
+ data[RAVE_SP_FRAME_CODE_OFFSET] = command;
+ data[RAVE_SP_FRAME_ACK_ID_OFFSET] = ackid;
rave_sp_write(sp, data, data_size);
@@ -388,16 +394,23 @@ EXPORT_SYMBOL_GPL(rave_sp_exec);
static void rave_sp_receive_event(struct rave_sp *sp,
const unsigned char *data, size_t length)
{
- u8 cmd[] = {
- [0] = rave_sp_reply_code(data[0]),
- [1] = data[1],
- };
+ unsigned long action;
+ u8 cmd[RAVE_SP_FRAME_DATA_OFFSET];
+
+ if (length < RAVE_SP_FRAME_DATA_OFFSET + 1) {
+ dev_warn(&sp->serdev->dev, "Dropping short event frame\n");
+ return;
+ }
+
+ cmd[RAVE_SP_FRAME_CODE_OFFSET] =
+ rave_sp_reply_code(data[RAVE_SP_FRAME_CODE_OFFSET]);
+ cmd[RAVE_SP_FRAME_ACK_ID_OFFSET] = data[RAVE_SP_FRAME_ACK_ID_OFFSET];
rave_sp_write(sp, cmd, sizeof(cmd));
- blocking_notifier_call_chain(&sp->event_notifier_list,
- rave_sp_action_pack(data[0], data[2]),
- NULL);
+ action = rave_sp_action_pack(data[RAVE_SP_FRAME_CODE_OFFSET],
+ data[RAVE_SP_FRAME_DATA_OFFSET]);
+ blocking_notifier_call_chain(&sp->event_notifier_list, action, NULL);
}
static void rave_sp_receive_reply(struct rave_sp *sp,
@@ -405,27 +418,35 @@ static void rave_sp_receive_reply(struct rave_sp *sp,
{
struct device *dev = &sp->serdev->dev;
struct rave_sp_reply *reply;
- const size_t payload_length = length - 2;
+ size_t payload_length;
+
+ if (length < RAVE_SP_FRAME_DATA_OFFSET) {
+ dev_warn(dev, "Dropping short reply frame\n");
+ return;
+ }
+ payload_length = length - RAVE_SP_FRAME_DATA_OFFSET;
mutex_lock(&sp->reply_lock);
reply = sp->reply;
if (reply) {
- if (reply->code == data[0] && reply->ackid == data[1] &&
+ if (reply->code == data[RAVE_SP_FRAME_CODE_OFFSET] &&
+ reply->ackid == data[RAVE_SP_FRAME_ACK_ID_OFFSET] &&
payload_length >= reply->length) {
/*
* We are relying on memcpy(dst, src, 0) to be a no-op
* when handling commands that have a no-payload reply
*/
- memcpy(reply->data, &data[2], reply->length);
+ memcpy(reply->data, &data[RAVE_SP_FRAME_DATA_OFFSET],
+ reply->length);
complete(&reply->received);
sp->reply = NULL;
} else {
dev_err(dev, "Ignoring incorrect reply\n");
dev_dbg(dev, "Code: expected = 0x%08x received = 0x%08x\n",
- reply->code, data[0]);
+ reply->code, data[RAVE_SP_FRAME_CODE_OFFSET]);
dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n",
- reply->ackid, data[1]);
+ reply->ackid, data[RAVE_SP_FRAME_ACK_ID_OFFSET]);
dev_dbg(dev, "Length: expected = %zu received = %zu\n",
reply->length, payload_length);
}
@@ -439,10 +460,10 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
size_t length)
{
const size_t checksum_length = sp->variant->checksum->length;
- const size_t payload_length = length - checksum_length;
- const u8 *crc_reported = &data[payload_length];
struct device *dev = &sp->serdev->dev;
u8 crc_calculated[RAVE_SP_CHECKSUM_SIZE];
+ const u8 *crc_reported;
+ size_t payload_length;
if (unlikely(checksum_length > sizeof(crc_calculated))) {
dev_warn(dev, "Checksum too long, dropping\n");
@@ -457,6 +478,9 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
return;
}
+ payload_length = length - checksum_length;
+ crc_reported = &data[payload_length];
+
sp->variant->checksum->subroutine(data, payload_length,
crc_calculated);
@@ -465,10 +489,10 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
return;
}
- if (rave_sp_id_is_event(data[0]))
- rave_sp_receive_event(sp, data, length);
+ if (rave_sp_id_is_event(data[RAVE_SP_FRAME_CODE_OFFSET]))
+ rave_sp_receive_event(sp, data, payload_length);
else
- rave_sp_receive_reply(sp, data, length);
+ rave_sp_receive_reply(sp, data, payload_length);
}
static int rave_sp_receive_buf(struct serdev_device *serdev,
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 469478f7a1d3..896ad61bb9e1 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -105,6 +105,7 @@ MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
static struct i2c_driver ad_dpot_i2c_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_i2c_probe,
.remove = ad_dpot_i2c_remove,
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
index 263055bda48b..1ebe629715a8 100644
--- a/drivers/misc/ad525x_dpot-spi.c
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -131,6 +131,7 @@ MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id);
static struct spi_driver ad_dpot_spi_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_spi_probe,
.remove = ad_dpot_spi_remove,
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index 756ef6912b5a..3ceb1f1caa38 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -631,66 +631,132 @@ static struct attribute *ad525x_attributes_commands[] = {
NULL
};
-static const struct attribute_group ad525x_group_commands = {
- .attrs = ad525x_attributes_commands,
+static struct attribute *ad525x_attributes[] = {
+ &dev_attr_rdac0.attr,
+ &dev_attr_rdac1.attr,
+ &dev_attr_rdac2.attr,
+ &dev_attr_rdac3.attr,
+ &dev_attr_rdac4.attr,
+ &dev_attr_rdac5.attr,
+ &dev_attr_eeprom0.attr,
+ &dev_attr_eeprom1.attr,
+ &dev_attr_eeprom2.attr,
+ &dev_attr_eeprom3.attr,
+ &dev_attr_eeprom4.attr,
+ &dev_attr_eeprom5.attr,
+ &dev_attr_tolerance0.attr,
+ &dev_attr_tolerance1.attr,
+ &dev_attr_tolerance2.attr,
+ &dev_attr_tolerance3.attr,
+ &dev_attr_tolerance4.attr,
+ &dev_attr_tolerance5.attr,
+ &dev_attr_otp0.attr,
+ &dev_attr_otp1.attr,
+ &dev_attr_otp2.attr,
+ &dev_attr_otp3.attr,
+ &dev_attr_otp4.attr,
+ &dev_attr_otp5.attr,
+ &dev_attr_otp0en.attr,
+ &dev_attr_otp1en.attr,
+ &dev_attr_otp2en.attr,
+ &dev_attr_otp3en.attr,
+ &dev_attr_otp4en.attr,
+ &dev_attr_otp5en.attr,
+ &dev_attr_inc_all.attr,
+ &dev_attr_dec_all.attr,
+ &dev_attr_inc_all_6db.attr,
+ &dev_attr_dec_all_6db.attr,
+ NULL
};
-static int ad_dpot_add_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static int ad525x_attr_index(struct attribute *attr,
+ const struct attribute * const *attrs)
{
- int err = sysfs_create_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
- }
+ int i;
- if (err)
- dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
- rdac);
+ for (i = 0; attrs[i]; i++)
+ if (attr == attrs[i])
+ return i;
- return err;
+ return -ENOENT;
}
-static inline void ad_dpot_remove_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static bool ad525x_is_command_attr(struct attribute *attr)
{
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
+ int i;
+
+ for (i = 0; ad525x_attributes_commands[i]; i++) {
+ if (attr == ad525x_attributes_commands[i])
+ return true;
}
+
+ return false;
+}
+
+static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr,
+ int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct dpot_data *data = dev_get_drvdata(dev);
+ int rdac;
+
+ if (!data)
+ return 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_wipers);
+ if (rdac >= 0)
+ return data->wipers & BIT(rdac) ? attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_eeprom);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_tolerance);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp_en);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ if (ad525x_is_command_attr(attr))
+ return data->feat & F_CMD_INC ? attr->mode : 0;
+
+ return attr->mode;
}
+static const struct attribute_group ad525x_group = {
+ .attrs = ad525x_attributes,
+ .is_visible = ad525x_is_visible,
+};
+
+const struct attribute_group *ad_dpot_groups[] = {
+ &ad525x_group,
+ NULL
+};
+EXPORT_SYMBOL(ad_dpot_groups);
+
int ad_dpot_probe(struct device *dev,
struct ad_dpot_bus_data *bdata, unsigned long devid,
const char *name)
{
struct dpot_data *data;
- int i, err = 0;
+ int i;
data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
if (!data) {
- err = -ENOMEM;
- goto exit;
+ dev_err(dev, "failed to create client for %s ID 0x%lX\n",
+ name, devid);
+ return -ENOMEM;
}
dev_set_drvdata(dev, data);
@@ -706,51 +772,22 @@ int ad_dpot_probe(struct device *dev,
data->wipers = DPOT_WIPERS(devid);
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i)) {
- err = ad_dpot_add_files(dev, data->feat, i);
- if (err)
- goto exit_remove_files;
+ if (data->wipers & BIT(i)) {
/* power-up midscale */
if (data->feat & F_RDACS_WONLY)
data->rdac_cache[i] = data->max_pos / 2;
}
- if (data->feat & F_CMD_INC)
- err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-
- if (err) {
- dev_err(dev, "failed to register sysfs hooks\n");
- goto exit_free;
- }
-
dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
name, data->max_pos);
return 0;
-
-exit_remove_files:
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
-
-exit_free:
- kfree(data);
- dev_set_drvdata(dev, NULL);
-exit:
- dev_err(dev, "failed to create client for %s ID 0x%lX\n",
- name, devid);
- return err;
}
EXPORT_SYMBOL(ad_dpot_probe);
void ad_dpot_remove(struct device *dev)
{
struct dpot_data *data = dev_get_drvdata(dev);
- int i;
-
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
kfree(data);
}
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
index 72a9d6801937..2e877c89523b 100644
--- a/drivers/misc/ad525x_dpot.h
+++ b/drivers/misc/ad525x_dpot.h
@@ -10,6 +10,8 @@
#include <linux/types.h>
+struct attribute_group;
+
#define DPOT_CONF(features, wipers, max_pos, uid) \
(((features) << 18) | (((wipers) & 0xFF) << 10) | \
((max_pos & 0xF) << 6) | (uid & 0x3F))
@@ -210,5 +212,6 @@ struct ad_dpot_bus_data {
int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata,
unsigned long devid, const char *name);
void ad_dpot_remove(struct device *dev);
+extern const struct attribute_group *ad_dpot_groups[];
#endif
diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c
index 54e25e9bc51b..c339a83e4f4f 100644
--- a/drivers/misc/bcm-vk/bcm_vk_msg.c
+++ b/drivers/misc/bcm-vk/bcm_vk_msg.c
@@ -108,7 +108,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq,
bool bcm_vk_drv_access_ok(struct bcm_vk *vk)
{
- return (!!atomic_read(&vk->msgq_inited));
+ /* Pair with the release store after message queue initialization. */
+ return !!atomic_read_acquire(&vk->msgq_inited);
}
void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask)
@@ -501,7 +502,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync)
msgq++;
}
}
- atomic_set(&vk->msgq_inited, 1);
+ /* Publish message queue info before allowing driver access. */
+ atomic_set_release(&vk->msgq_inited, 1);
return ret;
}
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index a30751ad3733..7979ec7d7333 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -1230,6 +1230,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
/* Gating real mcu clock */
err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1,
RTS5261_MCU_CLOCK_GATING, 0);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, 0);
} else {
diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c
index f546b050cb49..0a585be7f086 100644
--- a/drivers/misc/cardreader/rtsx_usb.c
+++ b/drivers/misc/cardreader/rtsx_usb.c
@@ -312,6 +312,9 @@ int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
if (ret < 0)
return ret;
+ ucr->card_status_cache = *status;
+ ucr->card_status_valid = true;
+
return 0;
}
EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status);
@@ -619,6 +622,7 @@ static int rtsx_usb_probe(struct usb_interface *intf,
{
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct rtsx_ucr *ucr;
+ u16 status;
int ret;
dev_dbg(&intf->dev,
@@ -655,6 +659,9 @@ static int rtsx_usb_probe(struct usb_interface *intf,
if (ret)
goto out_init_fail;
+ /* Prime cached status for runtime autosuspend decisions. */
+ rtsx_usb_get_card_status(ucr, &status);
+
/* initialize USB SG transfer timer */
timer_setup(&ucr->sg_timer, rtsx_usb_sg_timed_out, 0);
@@ -709,17 +716,27 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
struct rtsx_ucr *ucr =
(struct rtsx_ucr *)usb_get_intfdata(intf);
u16 val = 0;
+ bool valid = false;
dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n",
__func__, message.event);
if (PMSG_IS_AUTO(message)) {
if (mutex_trylock(&ucr->dev_mutex)) {
- rtsx_usb_get_card_status(ucr, &val);
+ valid = ucr->card_status_valid;
+ if (valid)
+ val = ucr->card_status_cache;
mutex_unlock(&ucr->dev_mutex);
- /* Defer the autosuspend if card exists */
- if (val & (SD_CD | MS_CD)) {
+ /*
+ * Do not issue USB commands from runtime autosuspend.
+ * Raw SD_CD is not authoritative on tray-based readers,
+ * while a real SD card is protected by the SD/MMC child
+ * runtime-PM reference once the card is powered. Keep
+ * the historical Memory Stick autosuspend deferral when
+ * the cached status says MS media is present.
+ */
+ if (valid && (val & MS_CD)) {
device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child);
return -EAGAIN;
}
diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c
index 9a41ab65378d..89ab9f05b88c 100644
--- a/drivers/misc/vmw_vmci/vmci_event.c
+++ b/drivers/misc/vmw_vmci/vmci_event.c
@@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event,
}
}
+ *new_subscription_id = sub->id;
if (have_new_id) {
list_add_rcu(&sub->node, &subscriber_array[event]);
retval = VMCI_SUCCESS;
} else {
+ kfree(sub);
retval = VMCI_ERROR_NO_RESOURCES;
}
mutex_unlock(&subscriber_mutex);
-
- *new_subscription_id = sub->id;
return retval;
}
EXPORT_SYMBOL_GPL(vmci_event_subscribe);
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index ba6044b16e07..cac4b894f5f8 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1153,10 +1153,16 @@ static int via_sd_probe(struct pci_dev *pcidev,
ret = mmc_add_host(mmc);
if (ret)
- goto unmap;
+ goto free_irq;
return 0;
+free_irq:
+ writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL);
+ free_irq(pcidev->irq, sdhost);
+ cancel_work_sync(&sdhost->carddet_work);
+ /* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */
+ writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL);
unmap:
iounmap(sdhost->mmiobase);
free_mmc_host:
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 2f11585b5613..ad3cc63d212a 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -393,6 +393,9 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
cxt->mtd = NULL;
flush_work(&cxt->work_erase);
flush_work(&cxt->work_write);
+ vfree(cxt->oops_page_used);
+ cxt->oops_page_used = NULL;
+ cxt->oops_pages = 0;
}
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 23483db8f30c..5c809a12aff5 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -254,7 +254,8 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
/* the direct offset is expected */
if (offset == MTDPART_OFS_APPEND ||
- offset == MTDPART_OFS_NXTBLK)
+ offset == MTDPART_OFS_NXTBLK ||
+ offset == MTDPART_OFS_RETAIN)
return -EINVAL;
if (length == MTDPART_SIZ_FULL)
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index 4d695875ea1b..b7d5fe7eef4b 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -1452,6 +1452,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
debugfs_failed:
del_mtd_blktrans_dev(mbd_dev);
+ mbd_dev = NULL;
cleanup:
mtdswap_cleanup(d);
diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c
index 861975e44b55..f03553eb1324 100644
--- a/drivers/mtd/nand/raw/nand_onfi.c
+++ b/drivers/mtd/nand/raw/nand_onfi.c
@@ -35,16 +35,21 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
struct nand_onfi_params *p)
{
struct nand_device *base = &chip->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_props requirements;
struct onfi_ext_param_page *ep;
struct onfi_ext_section *s;
struct onfi_ext_ecc_info *ecc;
+ size_t remaining, section_len;
uint8_t *cursor;
int ret;
int len;
int i;
len = le16_to_cpu(p->ext_param_page_length) * 16;
+ if (len < sizeof(*ep))
+ return -EINVAL;
+
ep = kmalloc(len, GFP_KERNEL);
if (!ep)
return -ENOMEM;
@@ -77,11 +82,29 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
/* find the ECC section. */
cursor = (uint8_t *)(ep + 1);
+ remaining = len - sizeof(*ep);
for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
s = ep->sections + i;
- if (s->type == ONFI_SECTION_TYPE_2)
+ section_len = s->length * 16;
+ if (section_len > remaining) {
+ dev_dbg(&mtd->dev,
+ "ONFI extended parameter section %d exceeds page\n",
+ i);
+ goto ext_out;
+ }
+
+ if (s->type == ONFI_SECTION_TYPE_2) {
+ if (section_len < sizeof(*ecc)) {
+ dev_dbg(&mtd->dev,
+ "ONFI extended parameter ECC section %d is too short\n",
+ i);
+ goto ext_out;
+ }
break;
- cursor += s->length * 16;
+ }
+
+ cursor += section_len;
+ remaining -= section_len;
}
if (i == ONFI_EXT_SECTION_MAX) {
pr_debug("We can not find the ECC section.\n");
diff --git a/drivers/mtd/parsers/afs.c b/drivers/mtd/parsers/afs.c
index 26116694c821..7ab3d50f565e 100644
--- a/drivers/mtd/parsers/afs.c
+++ b/drivers/mtd/parsers/afs.c
@@ -235,6 +235,9 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
pr_debug("Parsing v2 partition @%08x-%08x\n",
off, off + mtd->erasesize);
+ if (mtd->erasesize < sizeof(footer))
+ return -EINVAL;
+
/* First read the footer */
ptr = off + mtd->erasesize - sizeof(footer);
ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
@@ -245,6 +248,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
}
name = (char *) &footer[0];
version = footer[9];
+ if (footer[8] > mtd->erasesize - sizeof(footer))
+ return -EINVAL;
ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
pr_debug("found image \"%s\", version %08x, info @%08x\n",
@@ -278,6 +283,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
entrypoint = imginfo[pad];
attributes = imginfo[pad+1];
region_count = imginfo[pad+2];
+ if (region_count > (ARRAY_SIZE(imginfo) - pad - 3) / 4)
+ return -EINVAL;
block_start = imginfo[20];
block_end = imginfo[21];
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index adc47b87b38a..3946b4aab316 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -771,7 +771,7 @@ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)
static int early_erase_peb(struct ubi_device *ubi,
const struct ubi_attach_info *ai, int pnum, int ec)
{
- int err;
+ int err, torture = 0;
struct ubi_ec_hdr *ec_hdr;
if ((long long)ec >= UBI_MAX_ERASECOUNTER) {
@@ -790,7 +790,7 @@ static int early_erase_peb(struct ubi_device *ubi,
ec_hdr->ec = cpu_to_be64(ec);
- err = ubi_io_sync_erase(ubi, pnum, 0);
+ err = ubi_io_sync_erase(ubi, pnum, &torture);
if (err < 0)
goto out_free;
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 144aa76d13e9..bb25d50a2c7b 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -35,7 +35,7 @@
#define MTD_PARAM_LEN_MAX 64
/* Maximum number of comma-separated items in the 'mtd=' parameter */
-#define MTD_PARAM_MAX_COUNT 5
+#define MTD_PARAM_MAX_COUNT 6
/* Maximum value for the number of bad PEBs per 1024 PEBs */
#define MAX_MTD_UBI_BEB_LIMIT 768
@@ -54,6 +54,7 @@
* @vid_hdr_offs: VID header offset
* @max_beb_per1024: maximum expected number of bad PEBs per 1024 PEBs
* @enable_fm: enable fastmap when value is non-zero
+ * @need_resv_pool: reserve pool->max_size pebs when value is none-zero
*/
struct mtd_dev_param {
char name[MTD_PARAM_LEN_MAX];
@@ -61,6 +62,7 @@ struct mtd_dev_param {
int vid_hdr_offs;
int max_beb_per1024;
int enable_fm;
+ int need_resv_pool;
};
/* Numbers of elements set in the @mtd_dev_param array */
@@ -825,6 +827,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
* @vid_hdr_offset: VID header offset
* @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs
* @disable_fm: whether disable fastmap
+ * @need_resv_pool: whether reserve pebs to fill fm_pool
*
* This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
* to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
@@ -840,7 +843,8 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
* @ubi_devices_mutex.
*/
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
- int vid_hdr_offset, int max_beb_per1024, bool disable_fm)
+ int vid_hdr_offset, int max_beb_per1024, bool disable_fm,
+ bool need_resv_pool)
{
struct ubi_device *ubi;
int i, err;
@@ -951,6 +955,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
UBI_FM_MIN_POOL_SIZE);
ubi->fm_wl_pool.max_size = ubi->fm_pool.max_size / 2;
+ ubi->fm_pool_rsv_cnt = need_resv_pool ? ubi->fm_pool.max_size : 0;
ubi->fm_disabled = (!fm_autoconvert || disable_fm) ? 1 : 0;
if (fm_debug)
ubi_enable_dbg_chk_fastmap(ubi);
@@ -1273,7 +1278,8 @@ static int __init ubi_init(void)
mutex_lock(&ubi_devices_mutex);
err = ubi_attach_mtd_dev(mtd, p->ubi_num,
p->vid_hdr_offs, p->max_beb_per1024,
- p->enable_fm == 0);
+ p->enable_fm == 0,
+ p->need_resv_pool != 0);
mutex_unlock(&ubi_devices_mutex);
if (err < 0) {
pr_err("UBI error: cannot attach mtd%d\n",
@@ -1310,10 +1316,10 @@ static int __init ubi_init(void)
return 0;
out_detach:
- for (k = 0; k < i; k++)
+ for (k = 0; k < UBI_MAX_DEVICES; k++)
if (ubi_devices[k]) {
mutex_lock(&ubi_devices_mutex);
- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
+ ubi_detach_mtd_dev(k, 1);
mutex_unlock(&ubi_devices_mutex);
}
ubi_debugfs_exit();
@@ -1482,12 +1488,24 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
} else
p->enable_fm = 0;
+ token = tokens[5];
+ if (token) {
+ int err = kstrtoint(token, 10, &p->need_resv_pool);
+
+ if (err) {
+ pr_err("UBI error: bad value for need_resv_pool parameter: %s\n",
+ token);
+ return -EINVAL;
+ }
+ } else
+ p->need_resv_pool = 0;
+
mtd_devs += 1;
return 0;
}
module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 0400);
-MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num]]].\n"
+MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num[,enable_fm[,need_resv_pool]]]]].\n"
"Multiple \"mtd\" parameters may be specified.\n"
"MTD devices may be specified by their number, name, or path to the MTD character device node.\n"
"Optional \"vid_hdr_offs\" parameter specifies UBI VID header position to be used by UBI. (default value if 0)\n"
@@ -1495,13 +1513,14 @@ MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|pa
__stringify(CONFIG_MTD_UBI_BEB_LIMIT) ") if 0)\n"
"Optional \"ubi_num\" parameter specifies UBI device number which have to be assigned to the newly created UBI device (assigned automatically by default)\n"
"Optional \"enable_fm\" parameter determines whether to enable fastmap during attach. If the value is non-zero, fastmap is enabled. Default value is 0.\n"
+ "Optional \"need_resv_pool\" parameter determines whether to reserve pool->max_size pebs during attach. If the value is non-zero, peb reservation is enabled. Default value is 0.\n"
"\n"
"Example 1: mtd=/dev/mtd0 - attach MTD device /dev/mtd0.\n"
"Example 2: mtd=content,1984 mtd=4 - attach MTD device with name \"content\" using VID header offset 1984, and MTD device number 4 with default VID header offset.\n"
"Example 3: mtd=/dev/mtd1,0,25 - attach MTD device /dev/mtd1 using default VID header offset and reserve 25*nand_size_in_blocks/1024 erase blocks for bad block handling.\n"
+ "\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device).\n"
"Example 4: mtd=/dev/mtd1,0,0,5 - attach MTD device /dev/mtd1 to UBI 5 and using default values for the other fields.\n"
- "example 5: mtd=1,0,0,5 mtd=2,0,0,6,1 - attach MTD device /dev/mtd1 to UBI 5 and disable fastmap; attach MTD device /dev/mtd2 to UBI 6 and enable fastmap.(only works when fastmap is enabled and fm_autoconvert=Y).\n"
- "\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device).");
+ "example 5: mtd=1,0,0,5 mtd=2,0,0,6,1 - attach MTD device /dev/mtd1 to UBI 5 and disable fastmap; attach MTD device /dev/mtd2 to UBI 6 and enable fastmap.(only works when fastmap is enabled and fm_autoconvert=Y).\n");
#ifdef CONFIG_MTD_UBI_FASTMAP
module_param(fm_autoconvert, bool, 0644);
MODULE_PARM_DESC(fm_autoconvert, "Set this parameter to enable fastmap automatically on images without a fastmap.");
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index f43430b9c1e6..98aa00344b29 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -1041,7 +1041,8 @@ static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd,
*/
mutex_lock(&ubi_devices_mutex);
err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset,
- req.max_beb_per1024, !!req.disable_fm);
+ req.max_beb_per1024, !!req.disable_fm,
+ false);
mutex_unlock(&ubi_devices_mutex);
if (err < 0)
put_mtd_device(mtd);
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 3b71924f4920..6f5eadb1598d 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -33,9 +33,6 @@
#include <linux/err.h>
#include "ubi.h"
-/* Number of physical eraseblocks reserved for atomic LEB change operation */
-#define EBA_RESERVED_PEBS 1
-
/**
* struct ubi_eba_entry - structure encoding a single LEB -> PEB association
* @pnum: the physical eraseblock number attached to the LEB
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 79733163ab7d..16e3c8af8545 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -97,6 +97,46 @@ struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor)
return e;
}
+/*
+ * wait_free_pebs_for_pool - wait until there enough free pebs
+ * @ubi: UBI device description object
+ *
+ * Wait and execute do_work until there are enough free pebs, fill pool
+ * as much as we can. This will reduce pool refilling times, which can
+ * reduce the fastmap updating frequency.
+ */
+static void wait_free_pebs_for_pool(struct ubi_device *ubi)
+{
+ struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
+ struct ubi_fm_pool *pool = &ubi->fm_pool;
+ int free, expect_free, executed;
+ /*
+ * There are at least following free pebs which reserved by UBI:
+ * 1. WL_RESERVED_PEBS[1]
+ * 2. EBA_RESERVED_PEBS[1]
+ * 3. fm pebs - 1: Twice fastmap size deducted by fastmap and fm_anchor
+ * 4. beb_rsvd_pebs: This value should be get under lock ubi->wl_lock
+ */
+ int reserved = WL_RESERVED_PEBS + EBA_RESERVED_PEBS +
+ ubi->fm_size / ubi->leb_size - 1 + ubi->fm_pool_rsv_cnt;
+
+ do {
+ spin_lock(&ubi->wl_lock);
+ free = ubi->free_count;
+ free += pool->size - pool->used + wl_pool->size - wl_pool->used;
+ expect_free = reserved + ubi->beb_rsvd_pebs;
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Break out if there are no works or work is executed failure,
+ * given the fact that erase_worker will schedule itself when
+ * -EBUSY is returned from mtd layer caused by system shutdown.
+ */
+ if (do_work(ubi, &executed) || !executed)
+ break;
+ } while (free < expect_free);
+}
+
/*
* has_enough_free_count - whether ubi has enough free pebs to fill fm pools
* @ubi: UBI device description object
@@ -125,16 +165,23 @@ static bool has_enough_free_count(struct ubi_device *ubi, bool is_wl_pool)
}
/**
- * ubi_refill_pools - refills all fastmap PEB pools.
+ * ubi_refill_pools_and_lock - refills all fastmap PEB pools and takes fm locks.
* @ubi: UBI device description object
*/
-void ubi_refill_pools(struct ubi_device *ubi)
+void ubi_refill_pools_and_lock(struct ubi_device *ubi)
{
struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool;
struct ubi_fm_pool *pool = &ubi->fm_pool;
struct ubi_wl_entry *e;
int enough;
+ if (!ubi->ro_mode && !ubi->fm_disabled)
+ wait_free_pebs_for_pool(ubi);
+
+ down_write(&ubi->fm_protect);
+ down_write(&ubi->work_sem);
+ down_write(&ubi->fm_eba_sem);
+
spin_lock(&ubi->wl_lock);
return_unused_pool_pebs(ubi, wl_pool);
@@ -210,7 +257,7 @@ static int produce_free_peb(struct ubi_device *ubi)
while (!ubi->free.rb_node && ubi->works_count) {
dbg_wl("do one work synchronously");
- err = do_work(ubi);
+ err = do_work(ubi, NULL);
if (err)
return err;
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 2cdc29483aee..12dd05fd3778 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -20,7 +20,7 @@ static inline unsigned long *init_seen(struct ubi_device *ubi)
if (!ubi_dbg_chk_fastmap(ubi))
return NULL;
- ret = bitmap_zalloc(ubi->peb_count, GFP_KERNEL);
+ ret = bitmap_zalloc(ubi->peb_count, GFP_NOFS);
if (!ret)
return ERR_PTR(-ENOMEM);
@@ -106,7 +106,7 @@ static struct ubi_vid_io_buf *new_fm_vbuf(struct ubi_device *ubi, int vol_id)
struct ubi_vid_io_buf *new;
struct ubi_vid_hdr *vh;
- new = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
+ new = ubi_alloc_vid_buf(ubi, GFP_NOFS);
if (!new)
goto out;
@@ -1390,53 +1390,6 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
return ret;
}
-/**
- * erase_block - Manually erase a PEB.
- * @ubi: UBI device object
- * @pnum: PEB to be erased
- *
- * Returns the new EC value on success, < 0 indicates an internal error.
- */
-static int erase_block(struct ubi_device *ubi, int pnum)
-{
- int ret;
- struct ubi_ec_hdr *ec_hdr;
- long long ec;
-
- ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
- if (!ec_hdr)
- return -ENOMEM;
-
- ret = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
- if (ret < 0)
- goto out;
- else if (ret && ret != UBI_IO_BITFLIPS) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = ubi_io_sync_erase(ubi, pnum, 0);
- if (ret < 0)
- goto out;
-
- ec = be64_to_cpu(ec_hdr->ec);
- ec += ret;
- if (ec > UBI_MAX_ERASECOUNTER) {
- ret = -EINVAL;
- goto out;
- }
-
- ec_hdr->ec = cpu_to_be64(ec);
- ret = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
- if (ret < 0)
- goto out;
-
- ret = ec;
-out:
- kfree(ec_hdr);
- return ret;
-}
-
/**
* invalidate_fastmap - destroys a fastmap.
* @ubi: UBI device object
@@ -1463,7 +1416,7 @@ static int invalidate_fastmap(struct ubi_device *ubi)
ubi->fm = NULL;
ret = -ENOMEM;
- fm = kzalloc(sizeof(*fm), GFP_KERNEL);
+ fm = kzalloc(sizeof(*fm), GFP_NOFS);
if (!fm)
goto out;
@@ -1539,11 +1492,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
struct ubi_fastmap_layout *new_fm, *old_fm;
struct ubi_wl_entry *tmp_e;
- down_write(&ubi->fm_protect);
- down_write(&ubi->work_sem);
- down_write(&ubi->fm_eba_sem);
-
- ubi_refill_pools(ubi);
+ ubi_refill_pools_and_lock(ubi);
if (ubi->ro_mode || ubi->fm_disabled) {
up_write(&ubi->fm_eba_sem);
@@ -1552,7 +1501,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)
return 0;
}
- new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL);
+ new_fm = kzalloc(sizeof(*new_fm), GFP_NOFS);
if (!new_fm) {
up_write(&ubi->fm_eba_sem);
up_write(&ubi->work_sem);
@@ -1577,7 +1526,8 @@ int ubi_update_fastmap(struct ubi_device *ubi)
if (!tmp_e) {
if (old_fm && old_fm->e[i]) {
- ret = erase_block(ubi, old_fm->e[i]->pnum);
+ ret = ubi_sync_erase(ubi, old_fm->e[i],
+ &old_fm->to_be_tortured[i]);
if (ret < 0) {
ubi_err(ubi, "could not erase old fastmap PEB");
@@ -1629,7 +1579,8 @@ int ubi_update_fastmap(struct ubi_device *ubi)
if (old_fm) {
/* no fresh anchor PEB was found, reuse the old one */
if (!tmp_e) {
- ret = erase_block(ubi, old_fm->e[0]->pnum);
+ ret = ubi_sync_erase(ubi, old_fm->e[0],
+ &old_fm->to_be_tortured[0]);
if (ret < 0) {
ubi_err(ubi, "could not erase old anchor PEB");
@@ -1641,7 +1592,6 @@ int ubi_update_fastmap(struct ubi_device *ubi)
goto err;
}
new_fm->e[0] = old_fm->e[0];
- new_fm->e[0]->ec = ret;
old_fm->e[0] = NULL;
} else {
/* we've got a new anchor PEB, return the old one */
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 01b644861253..2af724bd6726 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -507,8 +507,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
/**
* ubi_io_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object
- * @pnum: physical eraseblock number to erase
- * @torture: if this physical eraseblock has to be tortured
+ * @pnum: the physical eraseblock number to erase
+ * @torture: if this physical eraseblock has to be tortured; cleared to zero
+ * once the torture test has completed successfully so that a retry
+ * of the erase does not torture the physical eraseblock again
*
* This function synchronously erases physical eraseblock @pnum. If @torture
* flag is not zero, the physical eraseblock is checked by means of writing
@@ -520,7 +522,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
* codes in case of other errors. Note, %-EIO means that the physical
* eraseblock is bad.
*/
-int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
+int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int *torture)
{
int err, ret = 0;
@@ -548,10 +550,11 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
return err;
}
- if (torture) {
+ if (*torture) {
ret = torture_peb(ubi, pnum);
if (ret < 0)
return ret;
+ *torture = 0;
}
err = do_sync_erase(ubi, pnum);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index c8f1bd4fa100..5cd4f30edd57 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -82,6 +82,9 @@ void ubi_err(const struct ubi_device *ubi, const char *fmt, ...);
#define UBI_DFS_DIR_NAME "ubi%d"
#define UBI_DFS_DIR_LEN (3 + 2 + 1)
+/* Number of physical eraseblocks reserved for atomic LEB change operation */
+#define EBA_RESERVED_PEBS 1
+
/*
* Error codes returned by the I/O sub-system.
*
@@ -491,6 +494,7 @@ struct ubi_debug_info {
* @fast_attach: non-zero if UBI was attached by fastmap
* @fm_anchor: The next anchor PEB to use for fastmap
* @fm_do_produce_anchor: If true produce an anchor PEB in wl
+ * @fm_pool_rsv_cnt: Number of reserved PEBs for filling pool/wl_pool
*
* @used: RB-tree of used physical eraseblocks
* @erroneous: RB-tree of erroneous used physical eraseblocks
@@ -601,6 +605,7 @@ struct ubi_device {
int fast_attach;
struct ubi_wl_entry *fm_anchor;
int fm_do_produce_anchor;
+ int fm_pool_rsv_cnt;
/* Wear-leveling sub-system's stuff */
struct rb_root used;
@@ -902,6 +907,7 @@ int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
struct ubi_attach_info *ai_scan);
/* wl.c */
+int ubi_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int *torture);
int ubi_wl_get_peb(struct ubi_device *ubi);
int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
int pnum, int torture);
@@ -914,7 +920,7 @@ struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor);
int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e,
int lnum, int torture);
int ubi_is_erase_work(struct ubi_work *wrk);
-void ubi_refill_pools(struct ubi_device *ubi);
+void ubi_refill_pools_and_lock(struct ubi_device *ubi);
int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub);
@@ -923,7 +929,7 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
int len);
int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
int len);
-int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture);
+int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int *torture);
int ubi_io_is_bad(const struct ubi_device *ubi, int pnum);
int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum);
int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
@@ -938,7 +944,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
/* build.c */
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
int vid_hdr_offset, int max_beb_per1024,
- bool disable_fm);
+ bool disable_fm, bool need_resv_pool);
int ubi_detach_mtd_dev(int ubi_num, int anyway);
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 886d44019401..a595970a439c 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -181,11 +181,13 @@ static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e)
/**
* do_work - do one pending work.
* @ubi: UBI device description object
+ * @executed: whether there is one work is executed
*
* This function returns zero in case of success and a negative error code in
- * case of failure.
+ * case of failure. If @executed is not NULL and there is one work executed,
+ * @executed is set as %1, otherwise @executed is set as %0.
*/
-static int do_work(struct ubi_device *ubi)
+static int do_work(struct ubi_device *ubi, int *executed)
{
int err;
struct ubi_work *wrk;
@@ -203,9 +205,13 @@ static int do_work(struct ubi_device *ubi)
if (list_empty(&ubi->works)) {
spin_unlock(&ubi->wl_lock);
up_read(&ubi->work_sem);
+ if (executed)
+ *executed = 0;
return 0;
}
+ if (executed)
+ *executed = 1;
wrk = list_entry(ubi->works.next, struct ubi_work, list);
list_del(&wrk->list);
ubi->works_count -= 1;
@@ -427,16 +433,17 @@ static int prot_queue_del(struct ubi_device *ubi, int pnum)
}
/**
- * sync_erase - synchronously erase a physical eraseblock.
+ * ubi_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object
* @e: the physical eraseblock to erase
- * @torture: if the physical eraseblock has to be tortured
+ * @torture: if the physical eraseblock has to be tortured; cleared to zero
+ * once the torture test has completed successfully so that a retry
+ * of the erase does not torture the physical eraseblock again
*
* This function returns zero in case of success and a negative error code in
* case of failure.
*/
-static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
- int torture)
+int ubi_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int *torture)
{
int err;
struct ubi_ec_hdr *ec_hdr;
@@ -1101,7 +1108,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
dbg_wl("erase PEB %d EC %d LEB %d:%d",
pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum);
- err = sync_erase(ubi, e, wl_wrk->torture);
+ err = ubi_sync_erase(ubi, e, &wl_wrk->torture);
if (!err) {
spin_lock(&ubi->wl_lock);
@@ -1138,7 +1145,8 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
int err1;
/* Re-schedule the LEB for erasure */
- err1 = schedule_erase(ubi, e, vol_id, lnum, 0, true);
+ err1 = schedule_erase(ubi, e, vol_id, lnum, wl_wrk->torture,
+ true);
if (err1) {
spin_lock(&ubi->wl_lock);
wl_entry_destroy(ubi, e);
@@ -1693,7 +1701,7 @@ int ubi_thread(void *u)
}
spin_unlock(&ubi->wl_lock);
- err = do_work(ubi);
+ err = do_work(ubi, NULL);
if (err) {
ubi_err(ubi, "%s: work failed with error code %d",
ubi->bgt_name, err);
@@ -1745,7 +1753,7 @@ static void shutdown_work(struct ubi_device *ubi)
static int erase_aeb(struct ubi_device *ubi, struct ubi_ainf_peb *aeb, bool sync)
{
struct ubi_wl_entry *e;
- int err;
+ int err, torture = 0;
e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
if (!e)
@@ -1756,7 +1764,7 @@ static int erase_aeb(struct ubi_device *ubi, struct ubi_ainf_peb *aeb, bool sync
ubi->lookuptbl[e->pnum] = e;
if (sync) {
- err = sync_erase(ubi, e, false);
+ err = ubi_sync_erase(ubi, e, &torture);
if (err)
goto out_free;
@@ -2104,7 +2112,7 @@ static int produce_free_peb(struct ubi_device *ubi)
spin_unlock(&ubi->wl_lock);
dbg_wl("do one work synchronously");
- err = do_work(ubi);
+ err = do_work(ubi, NULL);
spin_lock(&ubi->wl_lock);
if (err)
diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h
index 1d83e552533a..a69169c35e31 100644
--- a/drivers/mtd/ubi/wl.h
+++ b/drivers/mtd/ubi/wl.h
@@ -11,8 +11,10 @@ static bool need_wear_leveling(struct ubi_device *ubi);
static void ubi_fastmap_close(struct ubi_device *ubi);
static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count)
{
- /* Reserve enough LEBs to store two fastmaps. */
- *count += (ubi->fm_size / ubi->leb_size) * 2;
+ if (ubi->fm_disabled)
+ ubi->fm_pool_rsv_cnt = 0;
+ /* Reserve enough LEBs to store two fastmaps and to fill pools. */
+ *count += (ubi->fm_size / ubi->leb_size) * 2 + ubi->fm_pool_rsv_cnt;
INIT_WORK(&ubi->fm_work, update_fastmap_work_fn);
}
static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi,
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index f771d569524f..749d51160e03 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -217,7 +217,7 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
struct bonding *bond = netdev_priv(bond_dev);
struct bond_opt_value newval;
int miimon = 0;
- int err;
+ int err = 0;
if (!data)
return 0;
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 8fd064db1717..3a13aecf7eb7 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -1142,7 +1142,7 @@ static void __exit peak_usb_exit(void)
int err;
/* last chance do send any synchronous commands here */
- err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL,
+ err = driver_for_each_device(&peak_usb_driver.driver, NULL,
NULL, peak_usb_do_device_exit);
if (err)
pr_err("%s: failed to stop all can devices (err %d)\n",
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index ee20f6487a02..f8b34fa8b99e 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1894,7 +1894,7 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
mutex_unlock(&priv->arl_mutex);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(b53_fdb_dump);
diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6352.c b/drivers/net/dsa/mv88e6xxx/pcs-6352.c
index 88f624b65470..f0b42786d587 100644
--- a/drivers/net/dsa/mv88e6xxx/pcs-6352.c
+++ b/drivers/net/dsa/mv88e6xxx/pcs-6352.c
@@ -305,13 +305,16 @@ static bool mv88e6352_pcs_link_check(struct marvell_c22_pcs *mpcs)
struct mv88e6xxx_port *port = mpcs->port;
struct mv88e6xxx_chip *chip = port->chip;
u8 cmode;
+ int err;
/* Port 4 can be in auto-media mode. Check that the port is
* associated with the mpcs.
*/
mv88e6xxx_reg_lock(chip);
- chip->info->ops->port_get_cmode(chip, port->port, &cmode);
+ err = chip->info->ops->port_get_cmode(chip, port->port, &cmode);
mv88e6xxx_reg_unlock(chip);
+ if (err)
+ return false;
return cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX ||
cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
index a94c62956eed..9a6948cde240 100644
--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -996,9 +996,9 @@ static int emac_probe(struct platform_device *pdev)
/* fill in parameters for net-dev structure */
ndev->base_addr = (unsigned long)db->membase;
ndev->irq = irq_of_parse_and_map(np, 0);
- if (ndev->irq == -ENXIO) {
+ if (!ndev->irq) {
netdev_err(ndev, "No irq resource\n");
- ret = ndev->irq;
+ ret = -ENXIO;
goto out_iounmap;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 0d8e61c63c7c..805a7ac5f862 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -13475,10 +13475,13 @@ static int bnx2x_init_firmware(struct bnx2x *bp)
iro_alloc_err:
kfree(bp->init_ops_offsets);
+ bp->init_ops_offsets = NULL;
init_offsets_alloc_err:
kfree(bp->init_ops);
+ bp->init_ops = NULL;
init_ops_alloc_err:
kfree(bp->init_data);
+ bp->init_data = NULL;
request_firmware_exit:
release_firmware(bp->firmware);
bp->firmware = NULL;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index ec7fe943e568..dd977b45f6d0 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -4604,7 +4604,8 @@ fec_probe(struct platform_device *pdev)
failed_irq:
fec_enet_deinit(ndev);
failed_init:
- fec_ptp_stop(pdev);
+ if (fep->bufdesc_ex)
+ fec_ptp_stop(pdev);
failed_reset:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -4646,7 +4647,8 @@ fec_drv_remove(struct platform_device *pdev)
ERR_PTR(ret));
cancel_work_sync(&fep->tx_timeout_work);
- fec_ptp_stop(pdev);
+ if (fep->bufdesc_ex)
+ fec_ptp_stop(pdev);
unregister_netdev(ndev);
fec_enet_mii_remove(fep);
if (fep->reg_phy)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 04c58928585c..5c66eb21bb7e 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -6,6 +6,7 @@
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
@@ -3713,7 +3714,7 @@ static int hclge_misc_irq_init(struct hclge_dev *hdev)
snprintf(hdev->misc_vector.name, HNAE3_INT_NAME_LEN, "%s-misc-%s",
HCLGE_NAME, pci_name(hdev->pdev));
ret = request_irq(hdev->misc_vector.vector_irq, hclge_misc_irq_handle,
- 0, hdev->misc_vector.name, hdev);
+ IRQF_NO_AUTOEN, hdev->misc_vector.name, hdev);
if (ret) {
hclge_free_vector(hdev, 0);
dev_err(&hdev->pdev->dev, "request misc irq(%d) fail\n",
@@ -11815,9 +11816,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
hclge_init_rxd_adv_layout(hdev);
- /* Enable MISC vector(vector0) */
- hclge_enable_vector(&hdev->misc_vector, true);
-
ret = hclge_init_wol(hdev);
if (ret)
dev_warn(&pdev->dev,
@@ -11830,6 +11828,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
hclge_state_init(hdev);
hdev->last_reset_time = jiffies;
+ /* Enable MISC vector(vector0) */
+ enable_irq(hdev->misc_vector.vector_irq);
+ hclge_enable_vector(&hdev->misc_vector, true);
+
dev_info(&hdev->pdev->dev, "%s driver initialization finished.\n",
HCLGE_DRIVER_NAME);
@@ -12233,7 +12235,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
/* Disable MISC vector(vector0) */
hclge_enable_vector(&hdev->misc_vector, false);
- synchronize_irq(hdev->misc_vector.vector_irq);
+ disable_irq(hdev->misc_vector.vector_irq);
/* Disable all hw interrupts */
hclge_config_mac_tnl_int(hdev, false);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
index c1e88e67ebb6..5e94410d8dc2 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
@@ -58,6 +58,9 @@ bool hclge_ptp_set_tx_info(struct hnae3_handle *handle, struct sk_buff *skb)
struct hclge_dev *hdev = vport->back;
struct hclge_ptp *ptp = hdev->ptp;
+ if (!ptp)
+ return false;
+
if (!test_bit(HCLGE_PTP_FLAG_TX_EN, &ptp->flags) ||
test_and_set_bit(HCLGE_STATE_PTP_TX_HANDLING, &hdev->state)) {
ptp->tx_skipped++;
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 7aef40b50b89..3af3fc401331 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -2948,6 +2948,9 @@ int ice_vsi_release(struct ice_vsi *vsi)
return -ENODEV;
pf = vsi->back;
+ if (ice_is_vsi_dflt_vsi(vsi))
+ ice_clear_dflt_vsi(vsi);
+
if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
ice_rss_clean(vsi);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
index d39d86e694cc..006d3258fd4e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
@@ -854,7 +854,7 @@ int rvu_mbox_handler_mcs_ctrl_pkt_rule_write(struct rvu *rvu,
static void rvu_mcs_set_lmac_bmap(struct rvu *rvu)
{
struct mcs *mcs = mcs_get_pdata(0);
- unsigned long lmac_bmap;
+ unsigned long lmac_bmap = 0;
int cgx, lmac, port;
for (port = 0; port < mcs->hw->lmac_cnt; port++) {
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 095c339166b5..d07deeb53a53 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -2252,8 +2252,8 @@ static int nix_smq_flush(struct rvu *rvu, int blkaddr,
int pf = rvu_get_pf(pcifunc);
u8 cgx_id = 0, lmac_id = 0;
u16 tl2_tl3_link_schq;
- u8 link, link_level;
u64 cfg, bmap = 0;
+ u8 link_level;
if (!is_rvu_otx2(rvu)) {
/* Skip SMQ flush if pkt count is zero */
@@ -2285,7 +2285,6 @@ static int nix_smq_flush(struct rvu *rvu, int blkaddr,
link_level = rvu_read64(rvu, blkaddr, NIX_AF_PSE_CHANNEL_LEVEL) & 0x01 ?
NIX_TXSCH_LVL_TL3 : NIX_TXSCH_LVL_TL2;
tl2_tl3_link_schq = smq_flush_ctx->smq_tree_ctx[link_level].schq;
- link = smq_flush_ctx->smq_tree_ctx[NIX_TXSCH_LVL_TL1].schq;
/* SMQ set enqueue xoff */
cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(smq));
@@ -2295,13 +2294,13 @@ static int nix_smq_flush(struct rvu *rvu, int blkaddr,
/* Clear all NIX_AF_TL3_TL2_LINK_CFG[ENA] for the TL3/TL2 queue */
for (i = 0; i < (rvu->hw->cgx_links + rvu->hw->lbk_links); i++) {
cfg = rvu_read64(rvu, blkaddr,
- NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, link));
+ NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, i));
if (!(cfg & BIT_ULL(12)))
continue;
bmap |= BIT_ULL(i);
cfg &= ~BIT_ULL(12);
rvu_write64(rvu, blkaddr,
- NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, link), cfg);
+ NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, i), cfg);
}
/* Do SMQ flush and set enqueue xoff */
@@ -2322,10 +2321,10 @@ static int nix_smq_flush(struct rvu *rvu, int blkaddr,
if (!(bmap & BIT_ULL(i)))
continue;
cfg = rvu_read64(rvu, blkaddr,
- NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, link));
+ NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, i));
cfg |= BIT_ULL(12);
rvu_write64(rvu, blkaddr,
- NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, link), cfg);
+ NIX_AF_TL3_TL2X_LINKX_CFG(tl2_tl3_link_schq, i), cfg);
}
/* clear XOFF on TL2s */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index a7d9b7cb4297..1affcf149f37 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -245,7 +245,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
}
if (unlikely(aff_change && busy_xsk)) {
- mlx5e_trigger_irq(&c->icosq);
+ mlx5e_trigger_napi_icosq(c);
ch_stats->force_irq++;
}
diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 0e9afeb070ba..a179bbf7f42c 100644
--- a/drivers/net/ethernet/nvidia/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -2754,7 +2754,7 @@ static void nv_tx_timeout(struct net_device *dev, unsigned int txqueue)
netdev_info(dev, "Ring at %lx\n", (unsigned long)np->ring_addr);
netdev_info(dev, "Dumping tx registers\n");
- for (i = 0; i <= np->register_size; i += 32) {
+ for (i = 0; i + 32 <= np->register_size; i += 32) {
netdev_info(dev,
"%3x: %08x %08x %08x %08x "
"%08x %08x %08x %08x\n",
@@ -6233,7 +6233,7 @@ static int nv_suspend(struct device *device)
netif_device_detach(dev);
/* save non-pci configuration space */
- for (i = 0; i <= np->register_size/sizeof(u32); i++)
+ for (i = 0; i < np->register_size/sizeof(u32); i++)
np->saved_config_space[i] = readl(base + i*sizeof(u32));
return 0;
@@ -6248,7 +6248,7 @@ static int nv_resume(struct device *device)
int i, rc = 0;
/* restore non-pci configuration space */
- for (i = 0; i <= np->register_size/sizeof(u32); i++)
+ for (i = 0; i < np->register_size/sizeof(u32); i++)
writel(np->saved_config_space[i], base+i*sizeof(u32));
if (np->driver_data & DEV_NEED_MSI_FIX)
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index 042a75f34060..0e7a0c2c1765 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -303,10 +303,10 @@ enum qede_agg_state {
};
struct qede_agg_info {
- /* rx_buf is a data buffer that can be placed / consumed from rx bd
- * chain. It has two purposes: We will preallocate the data buffer
- * for each aggregation when we open the interface and will place this
- * buffer on the rx-bd-ring when we receive TPA_START. We don't want
+ /* buffer is used to retain the Rx consumer descriptor when a TPA
+ * session starts. If the SKB allocation fails during TPA_START,
+ * we use this saved buffer to safely recycle the physical page
+ * back into the rx-bd-ring via qede_reuse_page(). We don't want
* to be in a state where allocation fails, as we can't reuse the
* consumer buffer in the rx-chain since FW may still be writing to it
* (since header needs to be modified for TPA).
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 51affa97aab4..5d83d10de348 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -850,6 +850,7 @@ static void qede_tpa_start(struct qede_dev *edev,
pad, false);
tpa_info->buffer.page_offset = sw_rx_data_cons->page_offset;
tpa_info->buffer.mapping = sw_rx_data_cons->mapping;
+ tpa_info->buffer.data = sw_rx_data_cons->data;
if (unlikely(!tpa_info->skb)) {
DP_NOTICE(edev, "Failed to allocate SKB for gro\n");
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 8471157a431b..615bf82eed51 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -394,6 +394,7 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
if (!skbn)
return NULL;
+ skbn->dev = skb->dev;
skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
skb_put(skbn, packet_len);
memcpy(skbn->data, skb->data, packet_len);
diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
index f5786d78ed23..4091762275d6 100644
--- a/drivers/net/ethernet/realtek/8139cp.c
+++ b/drivers/net/ethernet/realtek/8139cp.c
@@ -2066,7 +2066,7 @@ static int __maybe_unused cp_suspend(struct device *device)
/* Disable Rx and Tx */
cpw16 (IntrMask, 0);
- cpw8 (Cmd, cpr8 (Cmd) & (~RxOn | ~TxOn));
+ cpw8 (Cmd, cpr8 (Cmd) & ~(RxOn | TxOn));
spin_unlock_irqrestore (&cp->lock, flags);
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index e0f8276cffed..d3f29e0cb615 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -1005,6 +1005,7 @@ struct ravb_ptp_perout {
struct ravb_ptp {
struct ptp_clock *clock;
struct ptp_clock_info info;
+ int phc_index;
u32 default_addend;
u32 current_addend;
int extts[N_EXT_TS];
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 085d81576f1a..1f479bcda8be 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1684,7 +1684,7 @@ static int ravb_get_ts_info(struct net_device *ndev,
(1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
(1 << HWTSTAMP_FILTER_ALL);
- info->phc_index = ptp_clock_index(priv->ptp.clock);
+ info->phc_index = READ_ONCE(priv->ptp.phc_index);
}
return 0;
@@ -2713,6 +2713,7 @@ static int ravb_probe(struct platform_device *pdev)
priv->rstc = rstc;
priv->ndev = ndev;
priv->pdev = pdev;
+ priv->ptp.phc_index = -1;
priv->num_tx_ring[RAVB_BE] = BE_TX_RING_SIZE;
priv->num_rx_ring[RAVB_BE] = BE_RX_RING_SIZE;
if (info->nc_queues) {
diff --git a/drivers/net/ethernet/renesas/ravb_ptp.c b/drivers/net/ethernet/renesas/ravb_ptp.c
index 6e4ef7af27bf..fd674f121086 100644
--- a/drivers/net/ethernet/renesas/ravb_ptp.c
+++ b/drivers/net/ethernet/renesas/ravb_ptp.c
@@ -325,6 +325,7 @@ void ravb_ptp_interrupt(struct net_device *ndev)
void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev)
{
struct ravb_private *priv = netdev_priv(ndev);
+ struct ptp_clock *clock;
unsigned long flags;
priv->ptp.info = ravb_ptp_info;
@@ -337,7 +338,15 @@ void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev)
ravb_modify(ndev, GCCR, GCCR_TCSS, GCCR_TCSS_ADJGPTP);
spin_unlock_irqrestore(&priv->lock, flags);
- priv->ptp.clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
+ clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
+ if (IS_ERR(clock)) {
+ netdev_err(ndev, "failed to register PTP clock: %pe\n", clock);
+ clock = NULL;
+ }
+
+ priv->ptp.clock = clock;
+ if (clock)
+ WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock));
}
void ravb_ptp_stop(struct net_device *ndev)
@@ -347,5 +356,7 @@ void ravb_ptp_stop(struct net_device *ndev)
ravb_write(ndev, 0, GIC);
ravb_write(ndev, 0, GIS);
- ptp_clock_unregister(priv->ptp.clock);
+ WRITE_ONCE(priv->ptp.phc_index, -1);
+ if (priv->ptp.clock)
+ ptp_clock_unregister(priv->ptp.clock);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 8555299443f4..009d16868a19 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -185,7 +185,7 @@ static void dwmac1000_set_filter(struct mac_device_info *hw,
dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2);
/* Handle multiple unicast addresses (perfect filtering) */
- if (netdev_uc_count(dev) > perfect_addr_number)
+ if (netdev_uc_count(dev) + 1 > perfect_addr_number)
/* Switch to promiscuous mode if more than unicast
* addresses are requested than supported by hardware.
*/
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index bdb4f527289d..065e24054e3b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -684,7 +684,7 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
value |= GMAC_PACKET_FILTER_HPF;
/* Handle multiple unicast addresses */
- if (netdev_uc_count(dev) > hw->unicast_filter_entries) {
+ if (netdev_uc_count(dev) + 1 > hw->unicast_filter_entries) {
/* Switch to promiscuous mode if more than 128 addrs
* are required
*/
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index aab02328a613..29863ec5526c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -540,7 +540,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
dwxgmac2_set_mchash(ioaddr, mc_filter, mcbitslog2);
/* Handle multiple unicast addresses */
- if (netdev_uc_count(dev) > hw->unicast_filter_entries) {
+ if (netdev_uc_count(dev) + 1 > hw->unicast_filter_entries) {
value |= XGMAC_FILTER_PR;
} else {
struct netdev_hw_addr *ha;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index f9e43fc32ee8..83e78613d0d9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -415,11 +415,17 @@ static int stmmac_test_mmc(struct stmmac_priv *priv)
stmmac_mmc_read(priv, priv->mmcaddr, &final);
/*
- * The number of MMC counters available depends on HW configuration
- * so we just use this one to validate the feature. I hope there is
- * not a version without this counter.
+ * The number of MMC counters available depends on HW configuration,
+ * and there doesn't seem to be a way to enumerate the implemented
+ * counters.
+ *
+ * Let's check a hand-picked set of counters, knowing that :
+ * - Starfive JH7110 doesn't implement mmc_tx_framecount_g
+ * - Amlogic SM1 doesn't implement any mmc_tx_*
+ *
*/
- if (final.mmc_tx_framecount_g <= initial.mmc_tx_framecount_g)
+ if (final.mmc_tx_framecount_g <= initial.mmc_tx_framecount_g &&
+ final.mmc_rx_framecount_gb <= initial.mmc_rx_framecount_gb)
return -EINVAL;
return 0;
@@ -493,6 +499,21 @@ static int stmmac_filter_check(struct stmmac_priv *priv)
return -EOPNOTSUPP;
}
+static int stmmac_uc_filter_check(struct stmmac_priv *priv)
+{
+ /* For tests involving the UC filter, we need at least one empty
+ * slot in the UC filter. The UC filters contains netdev_uc_count() + 1
+ * entries: The dev->uc list + one entry for the HW address.
+ *
+ * Having an empty slot therefore means netdev_uc_count() + 2 entries
+ * can fit in the filter
+ */
+ if (netdev_uc_count(priv->dev) + 2 > priv->hw->unicast_filter_entries)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
static bool stmmac_hash_check(struct stmmac_priv *priv, unsigned char *addr)
{
int mc_offset = 32 - priv->hw->mcast_bits_log2;
@@ -584,7 +605,7 @@ static int stmmac_test_pfilt(struct stmmac_priv *priv)
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
- if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
+ if (stmmac_uc_filter_check(priv))
return -EOPNOTSUPP;
while (--tries) {
@@ -628,7 +649,7 @@ static int stmmac_test_mcfilt(struct stmmac_priv *priv)
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
- if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
+ if (stmmac_uc_filter_check(priv))
return -EOPNOTSUPP;
if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
return -EOPNOTSUPP;
@@ -674,7 +695,7 @@ static int stmmac_test_ucfilt(struct stmmac_priv *priv)
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
- if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
+ if (stmmac_uc_filter_check(priv))
return -EOPNOTSUPP;
if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
return -EOPNOTSUPP;
@@ -1452,11 +1473,11 @@ static int __stmmac_test_l4filt(struct stmmac_priv *priv, u32 dst, u32 src,
struct {
struct flow_dissector_key_basic bkey;
struct flow_dissector_key_ports key;
- } __aligned(BITS_PER_LONG / 8) keys;
+ } __aligned(BITS_PER_LONG / 8) keys = { };
struct {
struct flow_dissector_key_basic bmask;
struct flow_dissector_key_ports mask;
- } __aligned(BITS_PER_LONG / 8) masks;
+ } __aligned(BITS_PER_LONG / 8) masks = { };
unsigned long dummy_cookie = 0xdeadbeef;
struct stmmac_packet_attrs attr = { };
struct flow_dissector *dissector;
@@ -1509,6 +1530,8 @@ static int __stmmac_test_l4filt(struct stmmac_priv *priv, u32 dst, u32 src,
keys.bkey.ip_proto = udp ? IPPROTO_UDP : IPPROTO_TCP;
keys.key.src = htons(src);
keys.key.dst = htons(dst);
+ /* Match the full IP proto field */
+ masks.bmask.ip_proto = 0xff;
masks.mask.src = src_mask;
masks.mask.dst = dst_mask;
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index fc6217917fc2..0645ef20f664 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -9075,6 +9075,8 @@ static void niu_try_msix(struct niu *np, u8 *ldg_num_map)
msi_vec[i].entry = i;
}
+ pdev->dev_flags |= PCI_DEV_FLAGS_MSIX_TOUCH_ENTRY_DATA_FIRST;
+
num_irqs = pci_enable_msix_range(pdev, msi_vec, 1, num_irqs);
if (num_irqs < 0) {
np->flags &= ~NIU_FLAGS_MSIX;
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 23dbe4e4b36c..c27e68e320e6 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -1327,6 +1327,7 @@ static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
u8 tun_prot = 0;
if (skb->ip_summed != CHECKSUM_PARTIAL) {
+csum_failed:
if (!(first->tx_flags & WX_TX_FLAGS_HW_VLAN) &&
!(first->tx_flags & WX_TX_FLAGS_CC))
return;
@@ -1420,7 +1421,8 @@ static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
WX_TXD_L4LEN_SHIFT;
break;
default:
- break;
+ skb_checksum_help(skb);
+ goto csum_failed;
}
/* update TX checksum flag */
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 7bb3da8f2536..466210f3e34c 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1107,6 +1107,8 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
out_encap:
gtp_encap_disable(gtp);
out_hashtable:
+ /* Wait for RCU readers that may still reference this gtp_dev. */
+ synchronize_net();
kfree(gtp->addr_hash);
kfree(gtp->tid_hash);
return err;
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index f5924142021d..fe569c8c14af 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -936,7 +936,7 @@ static int ipa_probe(struct platform_device *pdev)
return ret;
}
-static int ipa_remove(struct platform_device *pdev)
+static void ipa_remove(struct platform_device *pdev)
{
struct ipa *ipa = dev_get_drvdata(&pdev->dev);
struct ipa_power *power = ipa->power;
@@ -959,8 +959,16 @@ static int ipa_remove(struct platform_device *pdev)
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
ret = ipa_modem_stop(ipa);
}
- if (ret)
- return ret;
+ if (ret) {
+ /*
+ * Continuing teardown after failing to stop the modem
+ * could crash, so leave the remaining resources allocated.
+ */
+ dev_err(dev, "Failed to stop modem (%pe), leaking resources\n",
+ ERR_PTR(ret));
+ pm_runtime_put_noidle(dev);
+ return;
+ }
ipa_teardown(ipa);
}
@@ -978,17 +986,6 @@ static int ipa_remove(struct platform_device *pdev)
ipa_power_exit(power);
dev_info(dev, "IPA driver removed");
-
- return 0;
-}
-
-static void ipa_shutdown(struct platform_device *pdev)
-{
- int ret;
-
- ret = ipa_remove(pdev);
- if (ret)
- dev_err(&pdev->dev, "shutdown: remove returned %d\n", ret);
}
static const struct attribute_group *ipa_attribute_groups[] = {
@@ -1001,8 +998,8 @@ static const struct attribute_group *ipa_attribute_groups[] = {
static struct platform_driver ipa_driver = {
.probe = ipa_probe,
- .remove = ipa_remove,
- .shutdown = ipa_shutdown,
+ .remove_new = ipa_remove,
+ .shutdown = ipa_remove,
.driver = {
.name = "ipa",
.pm = &ipa_pm_ops,
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index 554ec6e54440..525040622f67 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -115,6 +115,9 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
goto enqueue_again;
}
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += len;
+
new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
if (!new_skb) {
ndev->stats.rx_dropped++;
@@ -125,13 +128,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
skb->protocol = eth_type_trans(skb, ndev);
skb->ip_summed = CHECKSUM_NONE;
- if (netif_rx(skb) == NET_RX_DROP) {
- ndev->stats.rx_errors++;
- ndev->stats.rx_dropped++;
- } else {
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += len;
- }
+ netif_rx(skb);
skb = new_skb;
@@ -167,8 +164,10 @@ static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev,
static int ntb_netdev_maybe_stop_tx(struct net_device *ndev,
struct ntb_transport_qp *qp, int size)
{
- if (netif_queue_stopped(ndev) ||
- (ntb_transport_tx_free_entry(qp) >= size))
+ if (netif_queue_stopped(ndev))
+ return -EBUSY;
+
+ if (ntb_transport_tx_free_entry(qp) >= size)
return 0;
return __ntb_netdev_maybe_stop_tx(ndev, qp, size);
@@ -211,21 +210,30 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
struct ntb_netdev *dev = netdev_priv(ndev);
int rc;
- ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop);
+ if (unlikely(ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop)))
+ return NETDEV_TX_BUSY;
rc = ntb_transport_tx_enqueue(dev->qp, skb, skb->data, skb->len);
- if (rc)
- goto err;
+ if (rc) {
+ if (rc == -EAGAIN || rc == -EBUSY) {
+ netif_stop_queue(ndev);
+ mod_timer(&dev->tx_timer,
+ jiffies + usecs_to_jiffies(tx_time));
+ return NETDEV_TX_BUSY;
+ }
+
+ goto drop;
+ }
/* check for next submit */
ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop);
return NETDEV_TX_OK;
-err:
+drop:
+ dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
- ndev->stats.tx_errors++;
- return NETDEV_TX_BUSY;
+ return NETDEV_TX_OK;
}
static void ntb_netdev_tx_timer(struct timer_list *t)
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
index 08364f10a43f..61b3453469dc 100644
--- a/drivers/net/ppp/pppox.c
+++ b/drivers/net/ppp/pppox.c
@@ -74,7 +74,9 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
switch (cmd) {
case PPPIOCGCHAN: {
+ struct sk_buff *skb;
int index;
+
rc = -ENOTCONN;
if (!(sk->sk_state & PPPOX_CONNECTED))
break;
@@ -85,7 +87,22 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
rc = 0;
+ /* PPPIOCGCHAN historically marks the userspace handoff to
+ * generic PPP; pppd then attaches the returned channel to
+ * /dev/ppp.
+ */
sk->sk_state |= PPPOX_BOUND;
+ /* Let lockless receive paths finish queueing against the old
+ * state.
+ */
+ synchronize_net();
+ /* Drain packets queued before the handoff because a bound
+ * socket is no longer readable.
+ */
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ ppp_input(&po->chan, skb);
+ }
break;
}
default:
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index b9e5a56f136e..3e400349b7c8 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -628,9 +628,15 @@ static void sl_uninit(struct net_device *dev)
struct slip *sl = netdev_priv(dev);
sl_free_bufs(sl);
+ /* Drop the slip_devs[] entry here rather than from the destructor:
+ * ndo_uninit runs under RTNL, so it cannot race sl_sync().
+ */
+ slip_devs[dev->base_addr] = NULL;
}
-/* Hook the destructor so we can free slip devices at the right point in time */
+/* Only for the slip_open() error path: register_netdevice() can fail before
+ * ndo_init, and then ndo_uninit is not called either.
+ */
static void sl_free_netdev(struct net_device *dev)
{
int i = dev->base_addr;
@@ -657,7 +663,6 @@ static void sl_setup(struct net_device *dev)
{
dev->netdev_ops = &sl_netdev_ops;
dev->needs_free_netdev = true;
- dev->priv_destructor = sl_free_netdev;
dev->hard_header_len = 0;
dev->addr_len = 0;
@@ -881,8 +886,6 @@ static int slip_open(struct tty_struct *tty)
* Close down a SLIP channel.
* This means flushing out any pending queues, and then returning. This
* call is serialized against other ldisc functions.
- *
- * We also use this method fo a hangup event
*/
static void slip_close(struct tty_struct *tty)
@@ -908,12 +911,7 @@ static void slip_close(struct tty_struct *tty)
#endif
/* Flush network side */
unregister_netdev(sl->dev);
- /* This will complete via sl_free_netdev */
-}
-
-static void slip_hangup(struct tty_struct *tty)
-{
- slip_close(tty);
+ /* sl_uninit() has dropped the slip_devs[] entry by now */
}
/************************************************************************
* STANDARD SLIP ENCAPSULATION *
@@ -1275,7 +1273,6 @@ static struct tty_ldisc_ops sl_ldisc = {
.name = "slip",
.open = slip_open,
.close = slip_close,
- .hangup = slip_hangup,
.ioctl = slip_ioctl,
.receive_buf = slip_receive_buf,
.write_wakeup = slip_write_wakeup,
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index ceaba08cd378..096f3ff36a43 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -629,6 +629,14 @@ static int tbnet_alloc_tx_buffers(struct tbnet *net)
return 0;
}
+static void tbnet_connect_failed(struct tbnet *net)
+{
+ /* Leave login_received set: only the peer can make it true again. */
+ mutex_lock(&net->connection_lock);
+ net->login_sent = false;
+ mutex_unlock(&net->connection_lock);
+}
+
static void tbnet_connected_work(struct work_struct *work)
{
struct tbnet *net = container_of(work, typeof(*net), connected_work);
@@ -650,6 +658,9 @@ static void tbnet_connected_work(struct work_struct *work)
ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path);
if (ret != net->remote_transmit_path) {
netdev_err(net->dev, "failed to allocate Rx HopID\n");
+ if (ret >= 0)
+ tb_xdomain_release_in_hopid(net->xd, ret);
+ tbnet_connect_failed(net);
return;
}
@@ -694,6 +705,7 @@ static void tbnet_connected_work(struct work_struct *work)
tb_ring_stop(net->rx_ring.ring);
tb_ring_stop(net->tx_ring.ring);
tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
+ tbnet_connect_failed(net);
}
static void tbnet_login_work(struct work_struct *work)
@@ -895,9 +907,11 @@ static int tbnet_poll(struct napi_struct *napi, int budget)
le32_to_cpu(net->rx_hdr.frame_count) - 1;
rx_packets++;
- net->stats.rx_bytes += frame_size;
if (last) {
+ /* Before eth_type_trans() pulls the Ethernet header. */
+ net->stats.rx_packets++;
+ net->stats.rx_bytes += skb->len;
skb->protocol = eth_type_trans(skb, net->dev);
trace_tbnet_rx_skb(skb);
napi_gro_receive(&net->napi, skb);
@@ -905,8 +919,6 @@ static int tbnet_poll(struct napi_struct *napi, int budget)
}
}
- net->stats.rx_packets += rx_packets;
-
if (cleaned_count)
tbnet_alloc_rx_buffers(net, cleaned_count);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 9a4192d6a4ca..98711c4e12d1 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1175,11 +1175,16 @@ static netdev_features_t tun_net_fix_features(struct net_device *dev,
static void tun_set_headroom(struct net_device *dev, int new_hr)
{
struct tun_struct *tun = netdev_priv(dev);
+ size_t max_headroom;
- if (new_hr < NET_SKB_PAD)
- new_hr = NET_SKB_PAD;
+ max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1);
- tun->align = new_hr;
+ if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
+ max_headroom -= ETH_HLEN + NET_IP_ALIGN;
+ else
+ max_headroom -= 1;
+
+ tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom);
}
static void
@@ -1879,7 +1884,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
switch (tun->flags & TUN_TYPE_MASK) {
case IFF_TUN:
if (tun->flags & IFF_NO_PI) {
- u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+ u8 ip_version;
+
+ if (!pskb_may_pull(skb, 1)) {
+ err = -EINVAL;
+ goto drop;
+ }
+ ip_version = skb->data[0] >> 4;
switch (ip_version) {
case 4:
@@ -1899,7 +1910,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb->dev = tun->dev;
break;
case IFF_TAP:
- if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
+ if (!pskb_may_pull(skb, ETH_HLEN)) {
err = -ENOMEM;
drop_reason = SKB_DROP_REASON_HDR_TRUNC;
goto drop;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 73df808978b2..01202cf700a6 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1368,6 +1368,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
{QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */
+ {QMI_QUIRK_SET_DTR(0x1bc7, 0x0991, 0)}, /* Telit FE990D50 */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1034, 2)}, /* Telit LE910C4-WWX */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1037, 4)}, /* Telit LE910C4-WWX */
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 4415c1f6585e..dd7c4c50ce58 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -3181,7 +3181,9 @@ static const struct net_device_ops vxlan_netdev_ether_ops = {
.ndo_fdb_get = vxlan_fdb_get,
.ndo_mdb_add = vxlan_mdb_add,
.ndo_mdb_del = vxlan_mdb_del,
+ .ndo_mdb_del_bulk = vxlan_mdb_del_bulk,
.ndo_mdb_dump = vxlan_mdb_dump,
+ .ndo_mdb_get = vxlan_mdb_get,
.ndo_fill_metadata_dst = vxlan_fill_metadata_dst,
};
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index eb919fc05cdf..2c40e489ff8b 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -75,6 +75,14 @@ struct vxlan_mdb_config {
u8 rt_protocol;
};
+struct vxlan_mdb_flush_desc {
+ union vxlan_addr remote_ip;
+ __be32 src_vni;
+ __be32 remote_vni;
+ __be16 remote_port;
+ u8 rt_protocol;
+};
+
static const struct rhashtable_params vxlan_mdb_rht_params = {
.head_offset = offsetof(struct vxlan_mdb_entry, rhnode),
.key_offset = offsetof(struct vxlan_mdb_entry, key),
@@ -371,12 +379,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
return true;
}
-static void vxlan_mdb_config_group_set(struct vxlan_mdb_config *cfg,
- const struct br_mdb_entry *entry,
- const struct nlattr *source_attr)
+static void vxlan_mdb_group_set(struct vxlan_mdb_entry_key *group,
+ const struct br_mdb_entry *entry,
+ const struct nlattr *source_attr)
{
- struct vxlan_mdb_entry_key *group = &cfg->group;
-
switch (entry->addr.proto) {
case htons(ETH_P_IP):
group->dst.sa.sa_family = AF_INET;
@@ -504,7 +510,7 @@ static int vxlan_mdb_config_attrs_init(struct vxlan_mdb_config *cfg,
entry->addr.proto, extack))
return -EINVAL;
- vxlan_mdb_config_group_set(cfg, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
+ vxlan_mdb_group_set(&cfg->group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
/* rtnetlink code only validates that IPv4 group address is
* multicast.
@@ -938,23 +944,20 @@ vxlan_mdb_nlmsg_src_list_size(const struct vxlan_mdb_entry_key *group,
return nlmsg_size;
}
-static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
- const struct vxlan_mdb_entry *mdb_entry,
- const struct vxlan_mdb_remote *remote)
+static size_t
+vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
+ const struct vxlan_mdb_entry *mdb_entry,
+ const struct vxlan_mdb_remote *remote)
{
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
- nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
- /* MDBA_MDB */
- nla_total_size(0) +
- /* MDBA_MDB_ENTRY */
- nla_total_size(0) +
/* MDBA_MDB_ENTRY_INFO */
- nla_total_size(sizeof(struct br_mdb_entry)) +
+ nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +
/* MDBA_MDB_EATTR_TIMER */
nla_total_size(sizeof(u32));
+
/* MDBA_MDB_EATTR_SOURCE */
if (vxlan_mdb_is_sg(group))
nlmsg_size += nla_total_size(vxlan_addr_size(&group->dst));
@@ -982,6 +985,19 @@ static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
return nlmsg_size;
}
+static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
+ const struct vxlan_mdb_entry *mdb_entry,
+ const struct vxlan_mdb_remote *remote)
+{
+ return NLMSG_ALIGN(sizeof(struct br_port_msg)) +
+ /* MDBA_MDB */
+ nla_total_size(0) +
+ /* MDBA_MDB_ENTRY */
+ nla_total_size(0) +
+ /* Remote entry */
+ vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry, remote);
+}
+
static int vxlan_mdb_nlmsg_fill(const struct vxlan_dev *vxlan,
struct sk_buff *skb,
const struct vxlan_mdb_entry *mdb_entry,
@@ -1304,6 +1320,298 @@ int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
return err;
}
+static const struct nla_policy
+vxlan_mdbe_attrs_del_bulk_pol[MDBE_ATTR_MAX + 1] = {
+ [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC),
+ [MDBE_ATTR_DST] = NLA_POLICY_RANGE(NLA_BINARY,
+ sizeof(struct in_addr),
+ sizeof(struct in6_addr)),
+ [MDBE_ATTR_DST_PORT] = { .type = NLA_U16 },
+ [MDBE_ATTR_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
+ [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
+ [MDBE_ATTR_STATE_MASK] = NLA_POLICY_MASK(NLA_U8, MDB_PERMANENT),
+};
+
+static int vxlan_mdb_flush_desc_init(struct vxlan_dev *vxlan,
+ struct vxlan_mdb_flush_desc *desc,
+ struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
+{
+ struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
+ struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
+ int err;
+
+ if (entry->ifindex && entry->ifindex != vxlan->dev->ifindex) {
+ NL_SET_ERR_MSG_MOD(extack, "Invalid port net device");
+ return -EINVAL;
+ }
+
+ if (entry->vid) {
+ NL_SET_ERR_MSG_MOD(extack, "VID must not be specified");
+ return -EINVAL;
+ }
+
+ if (!tb[MDBA_SET_ENTRY_ATTRS])
+ return 0;
+
+ err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX,
+ tb[MDBA_SET_ENTRY_ATTRS],
+ vxlan_mdbe_attrs_del_bulk_pol, extack);
+ if (err)
+ return err;
+
+ if (mdbe_attrs[MDBE_ATTR_STATE_MASK]) {
+ u8 state_mask = nla_get_u8(mdbe_attrs[MDBE_ATTR_STATE_MASK]);
+
+ if ((state_mask & MDB_PERMANENT) && !(entry->state & MDB_PERMANENT)) {
+ NL_SET_ERR_MSG_MOD(extack, "Only permanent MDB entries are supported");
+ return -EINVAL;
+ }
+ }
+
+ if (mdbe_attrs[MDBE_ATTR_RTPROT])
+ desc->rt_protocol = nla_get_u8(mdbe_attrs[MDBE_ATTR_RTPROT]);
+
+ if (mdbe_attrs[MDBE_ATTR_DST])
+ vxlan_nla_get_addr(&desc->remote_ip, mdbe_attrs[MDBE_ATTR_DST]);
+
+ if (mdbe_attrs[MDBE_ATTR_DST_PORT])
+ desc->remote_port =
+ cpu_to_be16(nla_get_u16(mdbe_attrs[MDBE_ATTR_DST_PORT]));
+
+ if (mdbe_attrs[MDBE_ATTR_VNI])
+ desc->remote_vni =
+ cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_VNI]));
+
+ if (mdbe_attrs[MDBE_ATTR_SRC_VNI])
+ desc->src_vni =
+ cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI]));
+
+ return 0;
+}
+
+static void vxlan_mdb_remotes_flush(struct vxlan_dev *vxlan,
+ struct vxlan_mdb_entry *mdb_entry,
+ const struct vxlan_mdb_flush_desc *desc)
+{
+ struct vxlan_mdb_remote *remote, *tmp;
+
+ list_for_each_entry_safe(remote, tmp, &mdb_entry->remotes, list) {
+ struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
+ __be32 remote_vni;
+
+ if (desc->remote_ip.sa.sa_family &&
+ !vxlan_addr_equal(&desc->remote_ip, &rd->remote_ip))
+ continue;
+
+ /* Encapsulation is performed with source VNI if remote VNI
+ * is not set.
+ */
+ remote_vni = rd->remote_vni ? : mdb_entry->key.vni;
+ if (desc->remote_vni && desc->remote_vni != remote_vni)
+ continue;
+
+ if (desc->remote_port && desc->remote_port != rd->remote_port)
+ continue;
+
+ if (desc->rt_protocol &&
+ desc->rt_protocol != remote->rt_protocol)
+ continue;
+
+ vxlan_mdb_remote_del(vxlan, mdb_entry, remote);
+ }
+}
+
+static void vxlan_mdb_flush(struct vxlan_dev *vxlan,
+ const struct vxlan_mdb_flush_desc *desc)
+{
+ struct vxlan_mdb_entry *mdb_entry;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry_safe(mdb_entry, tmp, &vxlan->mdb_list, mdb_node) {
+ if (desc->src_vni && desc->src_vni != mdb_entry->key.vni)
+ continue;
+
+ vxlan_mdb_remotes_flush(vxlan, mdb_entry, desc);
+ /* The flush can remove the (S, G) entries created for the
+ * source list of this entry, including the one saved by
+ * hlist_for_each_entry_safe(), so re-read it while this entry
+ * is still linked.
+ */
+ tmp = mdb_entry->mdb_node.next;
+ /* Entry will only be removed if its remotes list is empty. */
+ vxlan_mdb_entry_put(vxlan, mdb_entry);
+ }
+}
+
+int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
+{
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_mdb_flush_desc desc = {};
+ int err;
+
+ ASSERT_RTNL();
+
+ err = vxlan_mdb_flush_desc_init(vxlan, &desc, tb, extack);
+ if (err)
+ return err;
+
+ vxlan_mdb_flush(vxlan, &desc);
+
+ return 0;
+}
+
+static const struct nla_policy vxlan_mdbe_attrs_get_pol[MDBE_ATTR_MAX + 1] = {
+ [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
+ sizeof(struct in_addr),
+ sizeof(struct in6_addr)),
+ [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
+};
+
+static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[],
+ struct vxlan_mdb_entry_key *group,
+ struct netlink_ext_ack *extack)
+{
+ struct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]);
+ struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+ int err;
+
+ memset(group, 0, sizeof(*group));
+ group->vni = vxlan->default_dst.remote_vni;
+
+ if (!tb[MDBA_GET_ENTRY_ATTRS]) {
+ vxlan_mdb_group_set(group, entry, NULL);
+ return 0;
+ }
+
+ err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX,
+ tb[MDBA_GET_ENTRY_ATTRS],
+ vxlan_mdbe_attrs_get_pol, extack);
+ if (err)
+ return err;
+
+ if (mdbe_attrs[MDBE_ATTR_SOURCE] &&
+ !vxlan_mdb_is_valid_source(mdbe_attrs[MDBE_ATTR_SOURCE],
+ entry->addr.proto, extack))
+ return -EINVAL;
+
+ vxlan_mdb_group_set(group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
+
+ if (mdbe_attrs[MDBE_ATTR_SRC_VNI])
+ group->vni =
+ cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI]));
+
+ return 0;
+}
+
+static struct sk_buff *
+vxlan_mdb_get_reply_alloc(const struct vxlan_dev *vxlan,
+ const struct vxlan_mdb_entry *mdb_entry)
+{
+ struct vxlan_mdb_remote *remote;
+ size_t nlmsg_size;
+
+ nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
+ /* MDBA_MDB */
+ nla_total_size(0) +
+ /* MDBA_MDB_ENTRY */
+ nla_total_size(0);
+
+ list_for_each_entry(remote, &mdb_entry->remotes, list)
+ nlmsg_size += vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry,
+ remote);
+
+ return nlmsg_new(nlmsg_size, GFP_KERNEL);
+}
+
+static int
+vxlan_mdb_get_reply_fill(const struct vxlan_dev *vxlan,
+ struct sk_buff *skb,
+ const struct vxlan_mdb_entry *mdb_entry,
+ u32 portid, u32 seq)
+{
+ struct nlattr *mdb_nest, *mdb_entry_nest;
+ struct vxlan_mdb_remote *remote;
+ struct br_port_msg *bpm;
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlh = nlmsg_put(skb, portid, seq, RTM_NEWMDB, sizeof(*bpm), 0);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ bpm = nlmsg_data(nlh);
+ memset(bpm, 0, sizeof(*bpm));
+ bpm->family = AF_BRIDGE;
+ bpm->ifindex = vxlan->dev->ifindex;
+ mdb_nest = nla_nest_start_noflag(skb, MDBA_MDB);
+ if (!mdb_nest) {
+ err = -EMSGSIZE;
+ goto cancel;
+ }
+ mdb_entry_nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
+ if (!mdb_entry_nest) {
+ err = -EMSGSIZE;
+ goto cancel;
+ }
+
+ list_for_each_entry(remote, &mdb_entry->remotes, list) {
+ err = vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote);
+ if (err)
+ goto cancel;
+ }
+
+ nla_nest_end(skb, mdb_entry_nest);
+ nla_nest_end(skb, mdb_nest);
+ nlmsg_end(skb, nlh);
+
+ return 0;
+
+cancel:
+ nlmsg_cancel(skb, nlh);
+ return err;
+}
+
+int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
+ u32 seq, struct netlink_ext_ack *extack)
+{
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_mdb_entry *mdb_entry;
+ struct vxlan_mdb_entry_key group;
+ struct sk_buff *skb;
+ int err;
+
+ ASSERT_RTNL();
+
+ err = vxlan_mdb_get_parse(dev, tb, &group, extack);
+ if (err)
+ return err;
+
+ mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group);
+ if (!mdb_entry) {
+ NL_SET_ERR_MSG_MOD(extack, "MDB entry not found");
+ return -ENOENT;
+ }
+
+ skb = vxlan_mdb_get_reply_alloc(vxlan, mdb_entry);
+ if (!skb)
+ return -ENOMEM;
+
+ err = vxlan_mdb_get_reply_fill(vxlan, skb, mdb_entry, portid, seq);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to fill MDB get reply");
+ goto free;
+ }
+
+ return rtnl_unicast(skb, dev_net(dev), portid);
+
+free:
+ kfree_skb(skb);
+ return err;
+}
+
struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni)
@@ -1423,29 +1731,6 @@ static void vxlan_mdb_check_empty(void *ptr, void *arg)
WARN_ON_ONCE(1);
}
-static void vxlan_mdb_remotes_flush(struct vxlan_dev *vxlan,
- struct vxlan_mdb_entry *mdb_entry)
-{
- struct vxlan_mdb_remote *remote, *tmp;
-
- list_for_each_entry_safe(remote, tmp, &mdb_entry->remotes, list)
- vxlan_mdb_remote_del(vxlan, mdb_entry, remote);
-}
-
-static void vxlan_mdb_entries_flush(struct vxlan_dev *vxlan)
-{
- struct vxlan_mdb_entry *mdb_entry;
- struct hlist_node *tmp;
-
- /* The removal of an entry cannot trigger the removal of another entry
- * since entries are always added to the head of the list.
- */
- hlist_for_each_entry_safe(mdb_entry, tmp, &vxlan->mdb_list, mdb_node) {
- vxlan_mdb_remotes_flush(vxlan, mdb_entry);
- vxlan_mdb_entry_put(vxlan, mdb_entry);
- }
-}
-
int vxlan_mdb_init(struct vxlan_dev *vxlan)
{
int err;
@@ -1461,7 +1746,9 @@ int vxlan_mdb_init(struct vxlan_dev *vxlan)
void vxlan_mdb_fini(struct vxlan_dev *vxlan)
{
- vxlan_mdb_entries_flush(vxlan);
+ struct vxlan_mdb_flush_desc desc = {};
+
+ vxlan_mdb_flush(vxlan, &desc);
WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB);
rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty,
NULL);
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index 85b6d0c347e3..76a351a997d5 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -235,6 +235,10 @@ int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
struct netlink_ext_ack *extack);
int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack);
+int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
+ struct netlink_ext_ack *extack);
+int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
+ u32 seq, struct netlink_ext_ack *extack);
struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni);
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 272fa31ef074..b5854aceddf9 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -467,10 +467,8 @@ static int vxlan_vnifilter_dump(struct sk_buff *skb, struct netlink_callback *cb
static const struct nla_policy vni_filter_entry_policy[VXLAN_VNIFILTER_ENTRY_MAX + 1] = {
[VXLAN_VNIFILTER_ENTRY_START] = { .type = NLA_U32 },
[VXLAN_VNIFILTER_ENTRY_END] = { .type = NLA_U32 },
- [VXLAN_VNIFILTER_ENTRY_GROUP] = { .type = NLA_BINARY,
- .len = sizeof_field(struct iphdr, daddr) },
- [VXLAN_VNIFILTER_ENTRY_GROUP6] = { .type = NLA_BINARY,
- .len = sizeof(struct in6_addr) },
+ [VXLAN_VNIFILTER_ENTRY_GROUP] = NLA_POLICY_EXACT_LEN(sizeof_field(struct iphdr, daddr)),
+ [VXLAN_VNIFILTER_ENTRY_GROUP6] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
};
static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = {
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 1d06d4125992..17cf939a019f 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -5,6 +5,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -1452,11 +1453,15 @@ static void ath10k_msa_dump_memory(struct ath10k *ar,
hdr->length = cpu_to_le32(ar->msa.mem_size);
if (current_region->len < ar->msa.mem_size) {
- memcpy(buf, ar->msa.vaddr, current_region->len);
+ memcpy_fromio(buf,
+ (const void __iomem __force *)ar->msa.vaddr,
+ current_region->len);
ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n",
current_region->len, ar->msa.mem_size);
} else {
- memcpy(buf, ar->msa.vaddr, ar->msa.mem_size);
+ memcpy_fromio(buf,
+ (const void __iomem __force *)ar->msa.vaddr,
+ ar->msa.mem_size);
}
}
diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile
index cc47e0114595..43d2d8ddcdc0 100644
--- a/drivers/net/wireless/ath/ath11k/Makefile
+++ b/drivers/net/wireless/ath/ath11k/Makefile
@@ -17,7 +17,9 @@ ath11k-y += core.o \
peer.o \
dbring.o \
hw.o \
- pcic.o
+ pcic.o \
+ fw.o \
+ p2p.o
ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 9eb8887f84e7..e3f730e0dd58 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -16,6 +16,7 @@
#include "debug.h"
#include "hif.h"
#include "wow.h"
+#include "fw.h"
unsigned int ath11k_debug_mask;
EXPORT_SYMBOL(ath11k_debug_mask);
@@ -2124,6 +2125,12 @@ int ath11k_core_pre_init(struct ath11k_base *ab)
return ret;
}
+ ret = ath11k_fw_pre_init(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to pre init firmware: %d", ret);
+ return ret;
+ }
+
return 0;
}
EXPORT_SYMBOL(ath11k_core_pre_init);
@@ -2163,6 +2170,7 @@ void ath11k_core_deinit(struct ath11k_base *ab)
ath11k_hif_power_down(ab);
ath11k_mac_destroy(ab);
ath11k_core_soc_destroy(ab);
+ ath11k_fw_destroy(ab);
}
EXPORT_SYMBOL(ath11k_core_deinit);
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 4bb36dc6ae08..4cf6716a182d 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -15,6 +15,8 @@
#include <linux/ctype.h>
#include <linux/rhashtable.h>
#include <linux/average.h>
+#include <linux/firmware.h>
+
#include "qmi.h"
#include "htc.h"
#include "wmi.h"
@@ -29,6 +31,7 @@
#include "dbring.h"
#include "spectral.h"
#include "wow.h"
+#include "fw.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -993,6 +996,18 @@ struct ath11k_base {
const struct ath11k_pci_ops *ops;
} pci;
+ struct {
+ u32 api_version;
+
+ const struct firmware *fw;
+ const u8 *amss_data;
+ size_t amss_len;
+ const u8 *m3_data;
+ size_t m3_len;
+
+ DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT);
+ } fw;
+
#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 data_pos;
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 23771abb3ace..7e9a5de2776c 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2335,10 +2335,10 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
case RX_MSDU_START_PKT_TYPE_11N:
rx_status->encoding = RX_ENC_HT;
if (rate_mcs > ATH11K_HT_MCS_MAX) {
- ath11k_warn(ar->ab,
- "Received with invalid mcs in HT mode %d\n",
- rate_mcs);
- break;
+ ath11k_dbg(ar->ab, ATH11K_DBG_DP_RX,
+ "Received HT frame with out-of-range mcs %d, capping to %d\n",
+ rate_mcs, ATH11K_HT_MCS_MAX);
+ rate_mcs = ATH11K_HT_MCS_MAX;
}
rx_status->rate_idx = rate_mcs + (8 * (nss - 1));
if (sgi)
@@ -2347,13 +2347,13 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
break;
case RX_MSDU_START_PKT_TYPE_11AC:
rx_status->encoding = RX_ENC_VHT;
- rx_status->rate_idx = rate_mcs;
if (rate_mcs > ATH11K_VHT_MCS_MAX) {
- ath11k_warn(ar->ab,
- "Received with invalid mcs in VHT mode %d\n",
- rate_mcs);
- break;
+ ath11k_dbg(ar->ab, ATH11K_DBG_DP_RX,
+ "Received VHT frame with out-of-range mcs %d, capping to %d\n",
+ rate_mcs, ATH11K_VHT_MCS_MAX);
+ rate_mcs = ATH11K_VHT_MCS_MAX;
}
+ rx_status->rate_idx = rate_mcs;
rx_status->nss = nss;
if (sgi)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
@@ -2363,14 +2363,14 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
break;
case RX_MSDU_START_PKT_TYPE_11AX:
- rx_status->rate_idx = rate_mcs;
+ rx_status->encoding = RX_ENC_HE;
if (rate_mcs > ATH11K_HE_MCS_MAX) {
- ath11k_warn(ar->ab,
- "Received with invalid mcs in HE mode %d\n",
- rate_mcs);
- break;
+ ath11k_dbg(ar->ab, ATH11K_DBG_DP_RX,
+ "Received HE frame with out-of-range mcs %d, capping to %d\n",
+ rate_mcs, ATH11K_HE_MCS_MAX);
+ rate_mcs = ATH11K_HE_MCS_MAX;
}
- rx_status->encoding = RX_ENC_HE;
+ rx_status->rate_idx = rate_mcs;
rx_status->nss = nss;
rx_status->he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi);
rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw);
diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c
new file mode 100644
index 000000000000..8f84fba29886
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/fw.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "core.h"
+
+#include "debug.h"
+
+static int ath11k_fw_request_firmware_api_n(struct ath11k_base *ab,
+ const char *name)
+{
+ size_t magic_len, len, ie_len;
+ int ie_id, i, index, bit, ret;
+ struct ath11k_fw_ie *hdr;
+ const u8 *data;
+ __le32 *timestamp;
+
+ ab->fw.fw = ath11k_core_firmware_request(ab, name);
+ if (IS_ERR(ab->fw.fw)) {
+ ret = PTR_ERR(ab->fw.fw);
+ ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
+ ab->fw.fw = NULL;
+ return ret;
+ }
+
+ data = ab->fw.fw->data;
+ len = ab->fw.fw->size;
+
+ /* magic also includes the null byte, check that as well */
+ magic_len = strlen(ATH11K_FIRMWARE_MAGIC) + 1;
+
+ if (len < magic_len) {
+ ath11k_err(ab, "firmware image too small to contain magic: %zu\n",
+ len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (memcmp(data, ATH11K_FIRMWARE_MAGIC, magic_len) != 0) {
+ ath11k_err(ab, "Invalid firmware magic\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* jump over the padding */
+ magic_len = ALIGN(magic_len, 4);
+
+ /* make sure there's space for padding */
+ if (magic_len > len) {
+ ath11k_err(ab, "No space for padding after magic\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ len -= magic_len;
+ data += magic_len;
+
+ /* loop elements */
+ while (len > sizeof(struct ath11k_fw_ie)) {
+ hdr = (struct ath11k_fw_ie *)data;
+
+ ie_id = le32_to_cpu(hdr->id);
+ ie_len = le32_to_cpu(hdr->len);
+
+ len -= sizeof(*hdr);
+ data += sizeof(*hdr);
+
+ if (len < ie_len) {
+ ath11k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
+ ie_id, len, ie_len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (ie_id) {
+ case ATH11K_FW_IE_TIMESTAMP:
+ if (ie_len != sizeof(u32))
+ break;
+
+ timestamp = (__le32 *)data;
+
+ ath11k_dbg(ab, ATH11K_DBG_BOOT, "found fw timestamp %d\n",
+ le32_to_cpup(timestamp));
+ break;
+ case ATH11K_FW_IE_FEATURES:
+ ath11k_dbg(ab, ATH11K_DBG_BOOT,
+ "found firmware features ie (%zd B)\n",
+ ie_len);
+
+ for (i = 0; i < ATH11K_FW_FEATURE_COUNT; i++) {
+ index = i / 8;
+ bit = i % 8;
+
+ if (index == ie_len)
+ break;
+
+ if (data[index] & (1 << bit))
+ __set_bit(i, ab->fw.fw_features);
+ }
+
+ ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "features", "",
+ ab->fw.fw_features,
+ sizeof(ab->fw.fw_features));
+ break;
+ case ATH11K_FW_IE_AMSS_IMAGE:
+ ath11k_dbg(ab, ATH11K_DBG_BOOT,
+ "found fw image ie (%zd B)\n",
+ ie_len);
+
+ ab->fw.amss_data = data;
+ ab->fw.amss_len = ie_len;
+ break;
+ case ATH11K_FW_IE_M3_IMAGE:
+ ath11k_dbg(ab, ATH11K_DBG_BOOT,
+ "found m3 image ie (%zd B)\n",
+ ie_len);
+
+ ab->fw.m3_data = data;
+ ab->fw.m3_len = ie_len;
+ break;
+ default:
+ ath11k_warn(ab, "Unknown FW IE: %u\n", ie_id);
+ break;
+ }
+
+ /* jump over the padding */
+ ie_len = ALIGN(ie_len, 4);
+
+ /* make sure there's space for padding */
+ if (ie_len > len)
+ break;
+
+ len -= ie_len;
+ data += ie_len;
+ };
+
+ return 0;
+
+err:
+ release_firmware(ab->fw.fw);
+ ab->fw.fw = NULL;
+ return ret;
+}
+
+int ath11k_fw_pre_init(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = ath11k_fw_request_firmware_api_n(ab, ATH11K_FW_API2_FILE);
+ if (ret == 0) {
+ ab->fw.api_version = 2;
+ goto out;
+ }
+
+ ab->fw.api_version = 1;
+
+out:
+ ath11k_dbg(ab, ATH11K_DBG_BOOT, "using fw api %d\n",
+ ab->fw.api_version);
+
+ return 0;
+}
+
+void ath11k_fw_destroy(struct ath11k_base *ab)
+{
+ release_firmware(ab->fw.fw);
+}
diff --git a/drivers/net/wireless/ath/ath11k/fw.h b/drivers/net/wireless/ath/ath11k/fw.h
new file mode 100644
index 000000000000..d9893ceb2c3d
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/fw.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef ATH11K_FW_H
+#define ATH11K_FW_H
+
+#define ATH11K_FW_API2_FILE "firmware-2.bin"
+#define ATH11K_FIRMWARE_MAGIC "QCOM-ATH11K-FW"
+
+enum ath11k_fw_ie_type {
+ ATH11K_FW_IE_TIMESTAMP = 0,
+ ATH11K_FW_IE_FEATURES = 1,
+ ATH11K_FW_IE_AMSS_IMAGE = 2,
+ ATH11K_FW_IE_M3_IMAGE = 3,
+};
+
+enum ath11k_fw_features {
+ /* keep last */
+ ATH11K_FW_FEATURE_COUNT,
+};
+
+int ath11k_fw_pre_init(struct ath11k_base *ab);
+void ath11k_fw_destroy(struct ath11k_base *ab);
+
+#endif /* ATH11K_FW_H */
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index 48ae81efc269..d3a4b561832c 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -6,6 +6,7 @@
#include <linux/msi.h>
#include <linux/pci.h>
+#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/ioport.h>
@@ -389,16 +390,23 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
if (!mhi_ctrl)
return -ENOMEM;
- ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE,
- ab_pci->amss_path,
- sizeof(ab_pci->amss_path));
-
ab_pci->mhi_ctrl = mhi_ctrl;
mhi_ctrl->cntrl_dev = ab->dev;
- mhi_ctrl->fw_image = ab_pci->amss_path;
mhi_ctrl->regs = ab->mem;
mhi_ctrl->reg_len = ab->mem_len;
+ if (ab->fw.amss_data && ab->fw.amss_len > 0) {
+ /* use MHI firmware file from firmware-N.bin */
+ mhi_ctrl->fw_data = ab->fw.amss_data;
+ mhi_ctrl->fw_sz = ab->fw.amss_len;
+ } else {
+ /* use the old separate mhi.bin MHI firmware file */
+ ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE,
+ ab_pci->amss_path,
+ sizeof(ab_pci->amss_path));
+ mhi_ctrl->fw_image = ab_pci->amss_path;
+ }
+
ret = ath11k_mhi_get_msi(ab_pci);
if (ret) {
ath11k_err(ab, "failed to get msi for mhi\n");
diff --git a/drivers/net/wireless/ath/ath11k/p2p.c b/drivers/net/wireless/ath/ath11k/p2p.c
new file mode 100644
index 000000000000..01e14523f1fe
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/p2p.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "core.h"
+#include "wmi.h"
+#include "mac.h"
+#include "p2p.h"
+
+static void ath11k_p2p_noa_ie_fill(u8 *data, size_t len,
+ const struct ath11k_wmi_p2p_noa_info *noa)
+{
+ struct ieee80211_p2p_noa_attr *noa_attr;
+ u8 noa_descriptors, ctwindow;
+ bool oppps;
+ __le16 *noa_attr_len;
+ u16 attr_len;
+ int i;
+
+ ctwindow = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_CTWIN_TU);
+ oppps = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS);
+ noa_descriptors = u32_get_bits(noa->noa_attr,
+ WMI_P2P_NOA_INFO_DESC_NUM);
+
+ /* P2P IE */
+ data[0] = WLAN_EID_VENDOR_SPECIFIC;
+ data[1] = len - 2;
+ data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
+ data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
+ data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
+ data[5] = WLAN_OUI_TYPE_WFA_P2P;
+
+ /* NOA ATTR */
+ data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
+ noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
+ noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
+
+ noa_attr->index = u32_get_bits(noa->noa_attr,
+ WMI_P2P_NOA_INFO_INDEX);
+ noa_attr->oppps_ctwindow = ctwindow;
+ if (oppps)
+ noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
+
+ for (i = 0; i < noa_descriptors; i++) {
+ noa_attr->desc[i].count = noa->descriptors[i].type_count;
+ noa_attr->desc[i].duration =
+ cpu_to_le32(noa->descriptors[i].duration);
+ noa_attr->desc[i].interval =
+ cpu_to_le32(noa->descriptors[i].interval);
+ noa_attr->desc[i].start_time =
+ cpu_to_le32(noa->descriptors[i].start_time);
+ }
+
+ attr_len = 2; /* index + oppps_ctwindow */
+ attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+ *noa_attr_len = __cpu_to_le16(attr_len);
+}
+
+static size_t
+ath11k_p2p_noa_ie_len_compute(const struct ath11k_wmi_p2p_noa_info *noa)
+{
+ size_t len = 0;
+ u8 noa_descriptors = u32_get_bits(noa->noa_attr,
+ WMI_P2P_NOA_INFO_DESC_NUM);
+
+ if (!(noa_descriptors) &&
+ !(u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS)))
+ return 0;
+
+ len += 1 + 1 + 4; /* EID + len + OUI */
+ len += 1 + 2; /* noa attr + attr len */
+ len += 1 + 1; /* index + oppps_ctwindow */
+ len += noa_descriptors *
+ sizeof(struct ieee80211_p2p_noa_desc);
+
+ return len;
+}
+
+static void ath11k_p2p_noa_ie_assign(struct ath11k_vif *arvif, void *ie,
+ size_t len)
+{
+ struct ath11k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ kfree(arvif->u.ap.noa_data);
+
+ arvif->u.ap.noa_data = ie;
+ arvif->u.ap.noa_len = len;
+}
+
+static void __ath11k_p2p_noa_update(struct ath11k_vif *arvif,
+ const struct ath11k_wmi_p2p_noa_info *noa)
+{
+ struct ath11k *ar = arvif->ar;
+ void *ie;
+ size_t len;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ ath11k_p2p_noa_ie_assign(arvif, NULL, 0);
+
+ len = ath11k_p2p_noa_ie_len_compute(noa);
+ if (!len)
+ return;
+
+ ie = kmalloc(len, GFP_ATOMIC);
+ if (!ie)
+ return;
+
+ ath11k_p2p_noa_ie_fill(ie, len, noa);
+ ath11k_p2p_noa_ie_assign(arvif, ie, len); }
+
+void ath11k_p2p_noa_update(struct ath11k_vif *arvif,
+ const struct ath11k_wmi_p2p_noa_info *noa)
+{
+ struct ath11k *ar = arvif->ar;
+
+ spin_lock_bh(&ar->data_lock);
+ __ath11k_p2p_noa_update(arvif, noa);
+ spin_unlock_bh(&ar->data_lock);
+}
+
+static void ath11k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+ struct ath11k_p2p_noa_arg *arg = data;
+
+ if (arvif->vdev_id != arg->vdev_id)
+ return;
+
+ ath11k_p2p_noa_update(arvif, arg->noa);
+}
+
+void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id,
+ const struct ath11k_wmi_p2p_noa_info *noa)
+{
+ struct ath11k_p2p_noa_arg arg = {
+ .vdev_id = vdev_id,
+ .noa = noa,
+ };
+
+ ieee80211_iterate_active_interfaces_atomic(ar->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ ath11k_p2p_noa_update_vdev_iter,
+ &arg);
+}
diff --git a/drivers/net/wireless/ath/ath11k/p2p.h b/drivers/net/wireless/ath/ath11k/p2p.h
new file mode 100644
index 000000000000..d907940a9b09
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/p2p.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef ATH11K_P2P_H
+#define ATH11K_P2P_H
+
+#include "wmi.h"
+
+struct ath11k_wmi_p2p_noa_info;
+
+struct ath11k_p2p_noa_arg {
+ u32 vdev_id;
+ const struct ath11k_wmi_p2p_noa_info *noa;
+};
+
+void ath11k_p2p_noa_update(struct ath11k_vif *arvif,
+ const struct ath11k_wmi_p2p_noa_info *noa);
+void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id,
+ const struct ath11k_wmi_p2p_noa_info *noa);
+#endif
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 966ba712ff90..02c261a30a4d 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -908,6 +908,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
return 0;
err_free_irq:
+ /* __free_irq() expects the caller to have cleared the affinity hint */
+ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
ath11k_pcic_free_irq(ab);
err_ce_free:
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 84d214c38f0f..c3723e49092a 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -2514,38 +2514,56 @@ static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab,
static int ath11k_qmi_m3_load(struct ath11k_base *ab)
{
struct m3_mem_region *m3_mem = &ab->qmi.m3_mem;
- const struct firmware *fw;
+ const struct firmware *fw = NULL;
+ const void *m3_data;
char path[100];
+ size_t m3_len;
int ret;
- fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE);
- if (IS_ERR(fw)) {
- ret = PTR_ERR(fw);
- ath11k_core_create_firmware_path(ab, ATH11K_M3_FILE,
- path, sizeof(path));
- ath11k_err(ab, "failed to load %s: %d\n", path, ret);
- return ret;
- }
+ if (m3_mem->vaddr)
+ /* m3 firmware buffer is already available in the DMA buffer */
+ return 0;
- if (m3_mem->vaddr || m3_mem->size)
- goto skip_m3_alloc;
+ if (ab->fw.m3_data && ab->fw.m3_len > 0) {
+ /* firmware-N.bin had a m3 firmware file so use that */
+ m3_data = ab->fw.m3_data;
+ m3_len = ab->fw.m3_len;
+ } else {
+ /* No m3 file in firmware-N.bin so try to request old
+ * separate m3.bin.
+ */
+ fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE);
+ if (IS_ERR(fw)) {
+ ret = PTR_ERR(fw);
+ ath11k_core_create_firmware_path(ab, ATH11K_M3_FILE,
+ path, sizeof(path));
+ ath11k_err(ab, "failed to load %s: %d\n", path, ret);
+ return ret;
+ }
+
+ m3_data = fw->data;
+ m3_len = fw->size;
+ }
m3_mem->vaddr = dma_alloc_coherent(ab->dev,
- fw->size, &m3_mem->paddr,
+ m3_len, &m3_mem->paddr,
GFP_KERNEL);
if (!m3_mem->vaddr) {
ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n",
- fw->size);
- release_firmware(fw);
- return -ENOMEM;
+ m3_len);
+ ret = -ENOMEM;
+ goto out;
}
-skip_m3_alloc:
- memcpy(m3_mem->vaddr, fw->data, fw->size);
- m3_mem->size = fw->size;
+ memcpy(m3_mem->vaddr, m3_data, m3_len);
+ m3_mem->size = m3_len;
+
+ ret = 0;
+
+out:
release_firmware(fw);
- return 0;
+ return ret;
}
static void ath11k_qmi_m3_free(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c
index b9f0ef458988..7aa62a7d9a27 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -198,7 +198,7 @@ static void ath11k_tm_wmi_event_segmented(struct ath11k_base *ab, u32 cmd_id,
u16 length;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index a23f20fb1ebd..4db48d02a8f3 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -20,6 +20,7 @@
#include "hw.h"
#include "peer.h"
#include "testmode.h"
+#include "p2p.h"
struct wmi_tlv_policy {
size_t min_len;
@@ -154,6 +155,12 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_per_chain_rssi_stats) },
[WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = {
.min_len = sizeof(struct wmi_twt_add_dialog_event) },
+ [WMI_TAG_P2P_NOA_INFO] = {
+ .min_len = sizeof(struct ath11k_wmi_p2p_noa_info) },
+ [WMI_TAG_P2P_NOA_EVENT] = {
+ .min_len = sizeof(struct wmi_p2p_noa_event) },
+ [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = {
+ .min_len = sizeof(struct wmi_pdev_csa_switch_ev) },
};
#define PRIMAP(_hw_mode_) \
@@ -238,8 +245,8 @@ static int ath11k_wmi_tlv_parse(struct ath11k_base *ar, const void **tb,
(void *)tb);
}
-const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
- size_t len, gfp_t gfp)
+const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab,
+ struct sk_buff *skb, gfp_t gfp)
{
const void **tb;
int ret;
@@ -248,7 +255,7 @@ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
if (!tb)
return ERR_PTR(-ENOMEM);
- ret = ath11k_wmi_tlv_parse(ab, tb, ptr, len);
+ ret = ath11k_wmi_tlv_parse(ab, tb, skb->data, skb->len);
if (ret) {
kfree(tb);
return ERR_PTR(ret);
@@ -257,6 +264,13 @@ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
return tb;
}
+static u32 ath11k_wmi_tlv_data_len(const void *data)
+{
+ const struct wmi_tlv *tlv = (const struct wmi_tlv *)data - 1;
+
+ return FIELD_GET(WMI_TLV_LEN, tlv->header);
+}
+
static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
u32 cmd_id)
{
@@ -981,7 +995,7 @@ int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg,
FIELD_PREP(WMI_TLV_LEN, 0);
/* Note: This is a nested TLV containing:
- * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv]..
+ * [wmi_tlv][ath11k_wmi_p2p_noa_descriptor][wmi_tlv]..
*/
ptr += sizeof(*tlv);
@@ -2364,8 +2378,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
for (i = 0; i < params->num_hint_bssid; ++i) {
hint_bssid->freq_flags =
params->hint_bssid[i].freq_flags;
- ether_addr_copy(¶ms->hint_bssid[i].bssid.addr[0],
- &hint_bssid->bssid.addr[0]);
+ ether_addr_copy(&hint_bssid->bssid.addr[0],
+ ¶ms->hint_bssid[i].bssid.addr[0]);
hint_bssid++;
}
}
@@ -3933,7 +3947,7 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk
struct ath11k_vif *arvif;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -4945,6 +4959,7 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab,
return 0;
err:
+ kfree(svc_rdy_ext.mac_phy_caps);
ath11k_wmi_free_dbring_caps(ab);
return ret;
}
@@ -5006,7 +5021,7 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf
const struct wmi_vdev_start_resp_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5105,7 +5120,7 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n");
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5281,7 +5296,7 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n");
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5636,7 +5651,7 @@ static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *
const struct wmi_peer_delete_resp_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5668,7 +5683,7 @@ static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab,
const struct wmi_vdev_delete_resp_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5688,15 +5703,15 @@ static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab,
return 0;
}
-static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf,
- u32 len, u32 *vdev_id,
- u32 *tx_status)
+static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab,
+ struct sk_buff *skb,
+ u32 *vdev_id, u32 *tx_status)
{
const void **tb;
const struct wmi_bcn_tx_status_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5724,7 +5739,7 @@ static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_
const struct wmi_vdev_stopped_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5881,7 +5896,7 @@ static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab,
const struct wmi_mgmt_tx_compl_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6057,7 +6072,7 @@ static int ath11k_pull_scan_ev(struct ath11k_base *ab, struct sk_buff *skb,
const struct wmi_scan_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6090,7 +6105,7 @@ static int ath11k_pull_peer_sta_kickout_ev(struct ath11k_base *ab, struct sk_buf
const struct wmi_peer_sta_kickout_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6117,7 +6132,7 @@ static int ath11k_pull_roam_ev(struct ath11k_base *ab, struct sk_buff *skb,
const struct wmi_roam_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6158,14 +6173,14 @@ static int freq_to_idx(struct ath11k *ar, int freq)
return idx;
}
-static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, u8 *evt_buf,
- u32 len, struct wmi_chan_info_event *ch_info_ev)
+static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb,
+ struct wmi_chan_info_event *ch_info_ev)
{
const void **tb;
const struct wmi_chan_info_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6204,7 +6219,7 @@ ath11k_pull_pdev_bss_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb,
const struct wmi_pdev_bss_chan_info_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6244,7 +6259,7 @@ ath11k_pull_vdev_install_key_compl_ev(struct ath11k_base *ab, struct sk_buff *sk
const struct wmi_vdev_install_key_compl_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6275,7 +6290,7 @@ static int ath11k_pull_peer_assoc_conf_ev(struct ath11k_base *ab, struct sk_buff
const struct wmi_peer_assoc_conf_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -7000,7 +7015,7 @@ static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *s
const void **tb;
int ret, i;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -7391,8 +7406,7 @@ static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *s
struct ath11k_vif *arvif;
u32 vdev_id, tx_status;
- if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len,
- &vdev_id, &tx_status) != 0) {
+ if (ath11k_pull_bcn_tx_status_ev(ab, skb, &vdev_id, &tx_status) != 0) {
ath11k_warn(ab, "failed to extract bcn tx status");
return;
}
@@ -7423,7 +7437,7 @@ static void ath11k_wmi_event_peer_sta_ps_state_chg(struct ath11k_base *ab,
enum ath11k_wmi_peer_ps_state peer_previous_ps_state;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -7891,7 +7905,7 @@ static void ath11k_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb)
/* HW channel counters frequency value in hertz */
u32 cc_freq_hz = ab->cc_freq_hz;
- if (ath11k_pull_chan_info_ev(ab, skb->data, skb->len, &ch_info_ev) != 0) {
+ if (ath11k_pull_chan_info_ev(ab, skb, &ch_info_ev) != 0) {
ath11k_warn(ab, "failed to extract chan info event");
return;
}
@@ -8223,7 +8237,7 @@ static void ath11k_pdev_ctl_failsafe_check_event(struct ath11k_base *ab,
const struct wmi_pdev_ctl_failsafe_chk_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8256,15 +8270,23 @@ ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab,
const struct wmi_pdev_csa_switch_ev *ev,
const u32 *vdev_ids)
{
- int i;
+ u32 vdev_ids_len = ath11k_wmi_tlv_data_len(vdev_ids);
+ u32 num_vdevs = ev->num_vdevs;
struct ath11k_vif *arvif;
+ int i;
/* Finish CSA once the switch count becomes NULL */
if (ev->current_switch_count)
return;
+ if (num_vdevs > vdev_ids_len / sizeof(*vdev_ids)) {
+ ath11k_warn(ab, "csa switch count num_vdevs %u exceeds tlv array length %u\n",
+ num_vdevs, vdev_ids_len);
+ return;
+ }
+
rcu_read_lock();
- for (i = 0; i < ev->num_vdevs; i++) {
+ for (i = 0; i < num_vdevs; i++) {
arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]);
if (!arvif) {
@@ -8288,7 +8310,7 @@ ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab,
const u32 *vdev_ids;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8322,7 +8344,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff
struct ath11k *ar;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8376,7 +8398,7 @@ ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,
const struct wmi_pdev_temperature_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8416,7 +8438,7 @@ static void ath11k_fils_discovery_event(struct ath11k_base *ab,
const struct wmi_fils_discovery_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab,
@@ -8448,7 +8470,7 @@ static void ath11k_probe_resp_tx_status_event(struct ath11k_base *ab,
const struct wmi_probe_resp_tx_status_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab,
@@ -8574,7 +8596,7 @@ static void ath11k_wmi_twt_add_dialog_event(struct ath11k_base *ab,
const struct wmi_twt_add_dialog_event *ev;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab,
@@ -8611,7 +8633,7 @@ static void ath11k_wmi_gtk_offload_status_event(struct ath11k_base *ab,
u64 replay_ctr;
int ret;
- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8654,18 +8676,72 @@ static void ath11k_wmi_gtk_offload_status_event(struct ath11k_base *ab,
kfree(tb);
}
+static void ath11k_wmi_p2p_noa_event(struct ath11k_base *ab,
+ struct sk_buff *skb)
+{
+ const void **tb;
+ const struct wmi_p2p_noa_event *ev;
+ const struct ath11k_wmi_p2p_noa_info *noa;
+ struct ath11k *ar;
+ int vdev_id;
+ u8 noa_descriptors;
+
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath11k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
+ return;
+ }
+
+ ev = tb[WMI_TAG_P2P_NOA_EVENT];
+ noa = tb[WMI_TAG_P2P_NOA_INFO];
+
+ if (!ev || !noa)
+ goto out;
+
+ vdev_id = ev->vdev_id;
+ noa_descriptors = u32_get_bits(noa->noa_attr,
+ WMI_P2P_NOA_INFO_DESC_NUM);
+
+ if (noa_descriptors > WMI_P2P_MAX_NOA_DESCRIPTORS) {
+ ath11k_warn(ab, "invalid descriptor num %d in P2P NoA event\n",
+ noa_descriptors);
+ goto out;
+ }
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi tlv p2p noa vdev_id %i descriptors %u\n",
+ vdev_id, noa_descriptors);
+
+ rcu_read_lock();
+ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id);
+ if (!ar) {
+ ath11k_warn(ab, "invalid vdev id %d in P2P NoA event\n",
+ vdev_id);
+ goto unlock;
+ }
+
+ ath11k_p2p_noa_update_by_vdev_id(ar, vdev_id, noa);
+
+unlock:
+ rcu_read_unlock();
+out:
+ kfree(tb);
+}
+
static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
enum wmi_tlv_event_id id;
+ if (skb->len < sizeof(*cmd_hdr))
+ goto out;
+
cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id));
trace_ath11k_wmi_event(ab, id, skb->data, skb->len);
- if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
- goto out;
+ skb_pull(skb, sizeof(*cmd_hdr));
switch (id) {
/* Process all the WMI events here */
@@ -8781,6 +8857,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
case WMI_GTK_OFFLOAD_STATUS_EVENTID:
ath11k_wmi_gtk_offload_status_event(ab, skb);
break;
+ case WMI_P2P_NOA_EVENTID:
+ ath11k_wmi_p2p_noa_event(ab, skb);
+ break;
default:
ath11k_dbg(ab, ATH11K_DBG_WMI, "unsupported event id 0x%x\n", id);
break;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index fa3b480b9d24..6276d420db1f 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -3584,6 +3584,37 @@ struct wmi_ftm_event_msg {
u8 data[];
} __packed;
+#define WMI_P2P_MAX_NOA_DESCRIPTORS 4
+
+struct wmi_p2p_noa_event {
+ u32 vdev_id;
+} __packed;
+
+struct ath11k_wmi_p2p_noa_descriptor {
+ u32 type_count; /* 255: continuous schedule, 0: reserved */
+ u32 duration; /* Absent period duration in micro seconds */
+ u32 interval; /* Absent period interval in micro seconds */
+ u32 start_time; /* 32 bit tsf time when in starts */
+} __packed;
+
+#define WMI_P2P_NOA_INFO_CHANGED_FLAG BIT(0)
+#define WMI_P2P_NOA_INFO_INDEX GENMASK(15, 8)
+#define WMI_P2P_NOA_INFO_OPP_PS BIT(16)
+#define WMI_P2P_NOA_INFO_CTWIN_TU GENMASK(23, 17)
+#define WMI_P2P_NOA_INFO_DESC_NUM GENMASK(31, 24)
+
+struct ath11k_wmi_p2p_noa_info {
+ /* Bit 0 - Flag to indicate an update in NOA schedule
+ * Bits 7-1 - Reserved
+ * Bits 15-8 - Index (identifies the instance of NOA sub element)
+ * Bit 16 - Opp PS state of the AP
+ * Bits 23-17 - Ctwindow in TUs
+ * Bits 31-24 - Number of NOA descriptors
+ */
+ u32 noa_attr;
+ struct ath11k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
+} __packed;
+
#define WMI_BEACON_TX_BUFFER_SIZE 512
#define WMI_EMA_TMPL_IDX_SHIFT 8
@@ -6320,8 +6351,8 @@ enum wmi_sta_keepalive_method {
#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
-const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
- size_t len, gfp_t gfp);
+const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab,
+ struct sk_buff *skb, gfp_t gfp);
int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
u32 cmd_id);
struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index fe920ecd25ba..5b5001314d5e 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -2388,8 +2388,8 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar,
for (i = 0; i < arg->num_hint_bssid; ++i) {
hint_bssid->freq_flags =
arg->hint_bssid[i].freq_flags;
- ether_addr_copy(&arg->hint_bssid[i].bssid.addr[0],
- &hint_bssid->bssid.addr[0]);
+ ether_addr_copy(&hint_bssid->bssid.addr[0],
+ &arg->hint_bssid[i].bssid.addr[0]);
hint_bssid++;
}
}
@@ -6652,12 +6652,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
struct wmi_cmd_hdr *cmd_hdr;
enum wmi_tlv_event_id id;
- cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
- id = le32_get_bits(cmd_hdr->cmd_id, WMI_CMD_HDR_CMD_ID);
-
- if (!skb_pull(skb, sizeof(struct wmi_cmd_hdr)))
+ cmd_hdr = skb_pull_data(skb, sizeof(*cmd_hdr));
+ if (!cmd_hdr)
goto out;
+ id = le32_get_bits(cmd_hdr->cmd_id, WMI_CMD_HDR_CMD_ID);
+
switch (id) {
/* Process all the WMI events here */
case WMI_SERVICE_READY_EVENTID:
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 0c2b8b1a10d5..77404c7e7041 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -753,6 +753,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
assoc_resp_ie_offset;
+ if (assoc_req_len < assoc_req_ie_offset)
+ assoc_req_len = assoc_req_ie_offset;
+ if (assoc_resp_len < assoc_resp_ie_offset)
+ assoc_resp_len = assoc_resp_ie_offset;
+
assoc_req_len -= assoc_req_ie_offset;
assoc_resp_len -= assoc_resp_ie_offset;
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 447896b871c3..eacaae752d8a 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -1296,6 +1296,9 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len,
{
struct wmi_scan_complete_event *ev;
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
ev = (struct wmi_scan_complete_event *) datap;
ath6kl_scan_complete_evt(vif, a_sle32_to_cpu(ev->status));
@@ -3372,7 +3375,12 @@ static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len,
struct ath6kl_vif *vif)
{
- struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap;
+ struct wmi_addba_req_event *cmd;
+
+ if (len < sizeof(*cmd))
+ return -EINVAL;
+
+ cmd = (struct wmi_addba_req_event *)datap;
aggr_recv_addba_req_evt(vif, cmd->tid,
le16_to_cpu(cmd->st_seq_no), cmd->win_sz);
@@ -3383,7 +3391,12 @@ static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len,
static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len,
struct ath6kl_vif *vif)
{
- struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap;
+ struct wmi_delba_event *cmd;
+
+ if (len < sizeof(*cmd))
+ return -EINVAL;
+
+ cmd = (struct wmi_delba_event *)datap;
aggr_recv_delba_req_evt(vif, cmd->tid);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 0877507abd3c..ffaf0fb11db4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -1826,17 +1826,18 @@ brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
if (bus->rxctl) {
brcmf_err("last control frame is being processed.\n");
spin_unlock_bh(&bus->rxctl_lock);
- vfree(buf);
goto done;
}
bus->rxctl = buf + doff;
bus->rxctl_orig = buf;
bus->rxlen = len - doff;
spin_unlock_bh(&bus->rxctl_lock);
+ buf = NULL;
done:
/* Awake any waiters */
brcmf_sdio_dcmd_resp_wake(bus);
+ vfree(buf);
}
/* Pad read to blocksize for efficiency */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index 906591bae538..21cfc97542c5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -1586,7 +1586,7 @@ static int brcmf_usb_reset_device(struct device *dev, void *notused)
void brcmf_usb_exit(void)
{
- struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
+ struct device_driver *drv = &brcmf_usbdrvr.driver;
int ret;
brcmf_dbg(USB, "Enter\n");
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
index 8f107ceec407..0aa53212a993 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
@@ -488,7 +488,7 @@ static void iwl_fwrt_dump_fseq_regs(struct iwl_fw_runtime *fwrt)
void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt)
{
struct iwl_pc_data *pc_data;
- u8 count;
+ u32 count;
if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) {
IWL_ERR(fwrt,
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
index d588e4cd808d..ecbd709d7f21 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/firmware.h>
#include "iwl-drv.h"
@@ -603,6 +603,9 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))
return 0;
+ if (!fw_mon_cfg->req_size)
+ return -EIO;
+
num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);
if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
@@ -613,6 +616,9 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
return -EIO;
}
+ if (!num_frags)
+ return -EIO;
+
remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),
PAGE_SIZE);
num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);
diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h
index 655d95d3a068..19a0bb93aee7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h
+++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h
@@ -283,6 +283,16 @@ struct iwl_mei_colloc_info {
u8 bssid[ETH_ALEN];
};
+/**
+ * enum iwl_mei_sap_version - SAP version
+ * @IWL_MEI_SAP_VERSION_3: SAP version 3
+ * @IWL_MEI_SAP_VERSION_4: SAP version 4
+ */
+enum iwl_mei_sap_version {
+ IWL_MEI_SAP_VERSION_3 = 3,
+ IWL_MEI_SAP_VERSION_4 = 4,
+};
+
/*
* struct iwl_mei_ops - driver's operations called by iwlmei
* Operations will not be called more than once concurrently.
diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c
index 1dd9106c6513..1486702d37e7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mei/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2021-2023 Intel Corporation
+ * Copyright (C) 2021-2024 Intel Corporation
+ * Copyright (C) 2026 Intel Corporation
*/
#include <linux/etherdevice.h>
@@ -58,7 +59,6 @@ bool iwl_mei_is_connected(void)
}
EXPORT_SYMBOL_GPL(iwl_mei_is_connected);
-#define SAP_VERSION 3
#define SAP_CONTROL_BLOCK_ID 0x21504153 /* SAP! in ASCII */
struct iwl_sap_q_ctrl_blk {
@@ -110,16 +110,19 @@ struct iwl_sap_shared_mem_ctrl_blk {
#define SAP_H2M_DATA_Q_SZ 48256
#define SAP_M2H_DATA_Q_SZ 24128
-#define SAP_H2M_NOTIF_Q_SZ 2240
+#define SAP_H2M_NOTIF_Q_SZ_VER3 2240
+#define SAP_H2M_NOTIF_Q_SZ_VER4 32768
#define SAP_M2H_NOTIF_Q_SZ 62720
-#define _IWL_MEI_SAP_SHARED_MEM_SZ \
+#define _IWL_MEI_SAP_SHARED_MEM_SZ_VER3 \
(sizeof(struct iwl_sap_shared_mem_ctrl_blk) + \
- SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ + \
+ SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ_VER3 + \
SAP_M2H_DATA_Q_SZ + SAP_M2H_NOTIF_Q_SZ + 4)
-#define IWL_MEI_SAP_SHARED_MEM_SZ \
- (roundup(_IWL_MEI_SAP_SHARED_MEM_SZ, PAGE_SIZE))
+#define _IWL_MEI_SAP_SHARED_MEM_SZ_VER4 \
+ (sizeof(struct iwl_sap_shared_mem_ctrl_blk) + \
+ SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ_VER4 + \
+ SAP_M2H_DATA_Q_SZ + SAP_M2H_NOTIF_Q_SZ + 4)
struct iwl_mei_shared_mem_ptrs {
struct iwl_sap_shared_mem_ctrl_blk *ctrl;
@@ -206,6 +209,7 @@ struct iwl_mei {
* @mac_address: interface MAC address.
* @nvm_address: NVM MAC address.
* @priv: A pointer to iwlwifi.
+ * @sap_version: The SAP version to use. enum iwl_mei_sap_version.
*
* This used to cache the configurations coming from iwlwifi's way. The data
* is cached here so that we can buffer the configuration even if we don't have
@@ -220,6 +224,7 @@ struct iwl_mei_cache {
u16 mcc;
u8 mac_address[6];
u8 nvm_address[6];
+ enum iwl_mei_sap_version sap_version;
void *priv;
};
@@ -238,14 +243,17 @@ static void iwl_mei_free_shared_mem(struct mei_cl_device *cldev)
#define HBM_DMA_BUF_ID_WLAN 1
-static int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev)
+static int iwl_mei_alloc_mem_for_version(struct mei_cl_device *cldev,
+ enum iwl_mei_sap_version version)
{
struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
+ u32 mem_size = roundup(version == IWL_MEI_SAP_VERSION_4 ?
+ _IWL_MEI_SAP_SHARED_MEM_SZ_VER4 :
+ _IWL_MEI_SAP_SHARED_MEM_SZ_VER3, PAGE_SIZE);
- mem->ctrl = mei_cldev_dma_map(cldev, HBM_DMA_BUF_ID_WLAN,
- IWL_MEI_SAP_SHARED_MEM_SZ);
-
+ iwl_mei_cache.sap_version = version;
+ mem->ctrl = mei_cldev_dma_map(cldev, HBM_DMA_BUF_ID_WLAN, mem_size);
if (IS_ERR(mem->ctrl)) {
int ret = PTR_ERR(mem->ctrl);
@@ -254,11 +262,30 @@ static int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev)
return ret;
}
- memset(mem->ctrl, 0, IWL_MEI_SAP_SHARED_MEM_SZ);
+ memset(mem->ctrl, 0, mem_size);
return 0;
}
+static int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev)
+{
+ int ret;
+
+ /*
+ * SAP version 4 uses a larger Host to MEI notif queue.
+ * Since it is unknown at this stage which SAP version is used by the
+ * CSME firmware on this platform, try to allocate the version 4 first.
+ * If the CSME firmware uses version 3, this allocation is expected to
+ * fail because the CSME firmware allocated less memory for our driver.
+ */
+ ret = iwl_mei_alloc_mem_for_version(cldev, IWL_MEI_SAP_VERSION_4);
+ if (ret)
+ ret = iwl_mei_alloc_mem_for_version(cldev,
+ IWL_MEI_SAP_VERSION_3);
+
+ return ret;
+}
+
static void iwl_mei_init_shared_mem(struct iwl_mei *mei)
{
struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
@@ -277,7 +304,9 @@ static void iwl_mei_init_shared_mem(struct iwl_mei *mei)
h2m->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
cpu_to_le32(SAP_H2M_DATA_Q_SZ);
h2m->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
- cpu_to_le32(SAP_H2M_NOTIF_Q_SZ);
+ iwl_mei_cache.sap_version == IWL_MEI_SAP_VERSION_3 ?
+ cpu_to_le32(SAP_H2M_NOTIF_Q_SZ_VER3) :
+ cpu_to_le32(SAP_H2M_NOTIF_Q_SZ_VER4);
m2h->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
cpu_to_le32(SAP_M2H_DATA_Q_SZ);
m2h->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
@@ -429,7 +458,7 @@ static int iwl_mei_send_sap_msg_payload(struct mei_cl_device *cldev,
notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
- ret = iwl_mei_write_cyclic_buf(q_head, notif_q, q_head, hdr, q_sz);
+ ret = iwl_mei_write_cyclic_buf(cldev, notif_q, q_head, hdr, q_sz);
if (ret < 0)
return ret;
@@ -647,7 +676,7 @@ iwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev,
return;
}
- if (rsp->supported_version != SAP_VERSION) {
+ if (rsp->supported_version != iwl_mei_cache.sap_version) {
dev_err(&cldev->dev,
"didn't get the expected version: got %d\n",
rsp->supported_version);
@@ -1119,6 +1148,11 @@ static void iwl_mei_handle_sap_rx_cmd(struct mei_cl_device *cldev,
iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr, sizeof(*hdr));
valid_rx_sz -= sizeof(*hdr);
len = le16_to_cpu(hdr->len);
+ if (len + sizeof(*hdr) > PAGE_SIZE) {
+ dev_err(&cldev->dev,
+ "SAP message is too big: %u\n", len);
+ break;
+ }
if (valid_rx_sz < len)
break;
@@ -1281,7 +1315,7 @@ static int iwl_mei_send_start(struct mei_cl_device *cldev)
.hdr.type = cpu_to_le32(SAP_ME_MSG_START),
.hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)),
.hdr.len = cpu_to_le32(sizeof(msg)),
- .supported_versions[0] = SAP_VERSION,
+ .supported_versions[0] = iwl_mei_cache.sap_version,
.init_data_seq_num = cpu_to_le16(0x100),
.init_notif_seq_num = cpu_to_le16(0x800),
};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index d2dbbc9fe384..7ed684d638bd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -858,7 +858,7 @@ static void iwl_mvm_frob_txf_key_iter(struct ieee80211_hw *hw,
}
match++;
if (match == keylen) {
- memset(txf->buf + i - keylen, 0xAA, keylen);
+ memset(txf->buf + i + 1 - keylen, 0xAA, keylen);
match = 0;
}
}
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index a2ad2b53f016..54a77304a64e 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
status);
mwifiex_cancel_all_pending_cmd(adapter);
+
+ /* The command response path writes through cmd_node->data_buf.
+ * On an interrupted wait, the caller can return and release a
+ * stack-allocated data_buf before a late firmware response is
+ * processed. Detach the caller-owned buffer from the current
+ * command so a late response cannot corrupt freed stack memory.
+ */
+ spin_lock_bh(&adapter->mwifiex_cmd_lock);
+ if (adapter->curr_cmd == cmd_queued)
+ adapter->curr_cmd->data_buf = NULL;
+ spin_unlock_bh(&adapter->mwifiex_cmd_lock);
+
return status;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index d3ab9572e711..515e6db410f2 100644
--- a/drivers/net/wireless/marvell/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
@@ -687,7 +687,7 @@ static struct usb_driver mwifiex_usb_driver = {
.suspend = mwifiex_usb_suspend,
.resume = mwifiex_usb_resume,
.soft_unbind = 1,
- .drvwrap.driver = {
+ .driver = {
.coredump = mwifiex_usb_coredump,
},
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index b908ccfa4877..c3aa38ea6493 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -1250,11 +1250,12 @@ static int mt7615_suspend(struct ieee80211_hw *hw,
cancel_delayed_work_sync(&dev->pm.ps_work);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+ cancel_delayed_work_sync(&phy->scan_work);
+ cancel_delayed_work_sync(&phy->mt76->mac_work);
+
mt7615_mutex_acquire(dev);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
- cancel_delayed_work_sync(&phy->scan_work);
- cancel_delayed_work_sync(&phy->mt76->mac_work);
set_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
ieee80211_iterate_active_interfaces(hw,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index d5db6ffd6d36..338f5b46c651 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -850,7 +850,7 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
}
}
- if (WARN_ON_ONCE(len > skb->len))
+ if (len > skb->len)
return -EINVAL;
if (pskb_trim(skb, len))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index 1c47a4d95978..0425cf8b5650 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -246,7 +246,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev,
nss = min_t(u8, min_t(u8, nss_max, nss), path);
mphy->chainmask = BIT(path) - 1;
- if (band)
+ if (band && dev->dbdc_support)
mphy->chainmask <<= dev->chainshift;
mphy->antenna_mask = BIT(nss) - 1;
dev->chainmask |= mphy->chainmask;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 529a3640944b..ba8da7e2e7e6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -1232,14 +1232,19 @@ int mt7915_register_device(struct mt7915_dev *dev)
ret = mt7915_init_debugfs(&dev->phy);
if (ret)
- goto unreg_thermal;
+ goto unreg_ext_phy;
ret = mt7915_coredump_register(dev);
if (ret)
- goto unreg_thermal;
+ goto unreg_ext_phy;
return 0;
+unreg_ext_phy:
+ if (phy2) {
+ mt7915_unregister_ext_phy(dev);
+ phy2 = NULL;
+ }
unreg_thermal:
mt7915_unregister_thermal(&dev->phy);
unreg_dev:
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index ae91a9815fd2..a594a88f2054 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -437,7 +437,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb,
if (v0 & MT_PRXV_HT_AD_CODE)
status->enc_flags |= RX_ENC_FLAG_LDPC;
- status->chains = mphy->antenna_mask;
+ status->chains = mt7915_band_chainmask(phy);
status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1);
status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1);
@@ -2363,8 +2363,10 @@ void mt7915_mac_add_twt_setup(struct ieee80211_hw *hw,
}
flow->tsf = le64_to_cpu(twt_agrt->twt);
- if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
+ if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) {
+ list_del(&flow->list);
goto unlock;
+ }
setup_cmd = TWT_SETUP_CMD_ACCEPT;
dev->twt.table_mask |= BIT(table_id);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index bad92fa4c8fc..19355a11e0c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -1038,7 +1038,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
int max_nss = hweight8(hw->wiphy->available_antennas_tx);
- u8 chainshift = dev->chainshift;
+ u8 shift = mt7915_band_chainshift(phy);
u8 band = phy->mt76->band_idx;
if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
@@ -1051,9 +1051,9 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
/* handle a variant of mt7916/mt7981 which has 3T3R but nss2 on 5 GHz band */
if ((is_mt7916(&dev->mt76) || is_mt7981(&dev->mt76)) &&
band && hweight8(tx_ant) == max_nss)
- phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift;
+ phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
else
- phy->mt76->chainmask = tx_ant << (chainshift * band);
+ phy->mt76->chainmask = tx_ant << shift;
mt76_set_stream_caps(phy->mt76, true);
mt7915_set_stream_vht_txbf_caps(phy);
@@ -1643,7 +1643,7 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
path->mtk_wdma.wdma_idx = wed->wdma_idx;
path->mtk_wdma.bss = mvif->mt76.idx;
path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff;
- path->mtk_wdma.queue = phy != &dev->phy;
+ path->mtk_wdma.queue = phy->mt76->band_idx;
ctx->dev = NULL;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 9ac95f14fdaf..6c0ae5715ca6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -51,7 +51,7 @@ mt7915_mcu_get_sta_nss(u16 mcs_map)
break;
}
- return nss - 1;
+ return nss ? nss - 1 : 0;
}
static void
@@ -526,9 +526,9 @@ mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
ra->rx_streams = max_nss;
ra->algo = 4;
ra->train_up_rule = 2;
- ra->train_up_high_thres = 110;
- ra->train_up_rule_rssi = -70;
- ra->low_traffic_thres = 2;
+ ra->train_up_high_thres = cpu_to_le16(110);
+ ra->train_up_rule_rssi = cpu_to_le16(-70);
+ ra->low_traffic_thres = cpu_to_le16(2);
ra->phy_cap = cpu_to_le32(0xfdf);
ra->interval = cpu_to_le32(500);
ra->fast_interval = cpu_to_le32(100);
@@ -2743,7 +2743,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
.center_ch = ieee80211_frequency_to_channel(freq1),
.bw = mt76_connac_chan_bw(chandef),
.tx_path_num = hweight16(phy->mt76->chainmask),
- .rx_path = phy->mt76->chainmask >> (dev->chainshift * band),
+ .rx_path = mt7915_band_chainmask(phy),
.band_idx = band,
.channel_band = ch_band[chandef->chan->band],
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index acc1371a94b1..d10312f75403 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -318,9 +318,9 @@ struct bss_info_ra {
u8 antenna_idx;
u8 train_up_rule;
u8 rsv[3];
- unsigned short train_up_high_thres;
- short train_up_rule_rssi;
- unsigned short low_traffic_thres;
+ __le16 train_up_high_thres;
+ __le16 train_up_rule_rssi;
+ __le16 low_traffic_thres;
__le16 max_phyrate;
__le32 phy_cap;
__le32 interval;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index b16c798ec282..19e3a773d9c2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -377,6 +377,24 @@ mt7915_ext_phy(struct mt7915_dev *dev)
return phy->priv;
}
+/* without dbdc, the chainmask is stored unshifted, even if the phy is
+ * bound to band 1
+ */
+static inline u8 mt7915_band_chainshift(struct mt7915_phy *phy)
+{
+ struct mt7915_dev *dev = phy->dev;
+
+ if (!dev->dbdc_support)
+ return 0;
+
+ return phy->mt76->band_idx * dev->chainshift;
+}
+
+static inline u16 mt7915_band_chainmask(struct mt7915_phy *phy)
+{
+ return phy->mt76->chainmask >> mt7915_band_chainshift(phy);
+}
+
static inline u32 mt7915_check_adie(struct mt7915_dev *dev, bool sku)
{
u32 mask = sku ? MT_CONNINFRA_SKU_MASK : MT_ADIE_TYPE_MASK;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
index 07b0a5766eab..c24f1b12f064 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
@@ -135,7 +135,6 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
mdev = &dev->mt76;
mt7915_wfsys_reset(dev);
- hif2 = mt7915_pci_init_hif2(pdev);
ret = mt7915_mmio_wed_init(dev, pdev, true, &irq);
if (ret < 0)
@@ -145,16 +144,20 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
hif2 = mt7915_pci_init_hif2(pdev);
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
- if (ret < 0)
+ if (ret < 0) {
+ mt7915_put_hif2(hif2);
goto free_device;
+ }
irq = pdev->irq;
}
ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
- if (ret)
+ if (ret) {
+ mt7915_put_hif2(hif2);
goto free_wed_or_irq_vector;
+ }
/* master switch of PCIe tnterrupt enable */
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
index 37348b208736..f182ea7c5f1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
@@ -913,7 +913,7 @@ static void mt7986_wmac_clock_enable(struct mt7915_dev *dev, u32 adie_type)
read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK),
USEC_PER_MSEC, 50 * USEC_PER_MSEC, false,
- dev, MT_ADIE_SLP_CTRL_CK0(0));
+ dev, MT_ADIE_SLP_CTRL_CK0(1));
}
mt76_wmac_spi_unlock(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
index 0d76ae31b376..845ac6154308 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -692,9 +692,7 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
{
struct mt76_testmode_data *td = &mphy->test;
struct mt7915_phy *phy = mphy->priv;
- struct mt7915_dev *dev = phy->dev;
- u32 chainmask = mphy->chainmask, changed = 0;
- bool ext_phy = phy != &dev->phy;
+ u32 chainmask = mt7915_band_chainmask(phy), changed = 0;
int i;
BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
@@ -703,7 +701,6 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
td->state == MT76_TM_STATE_OFF)
return 0;
- chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask;
if (td->tx_antenna_mask > chainmask)
return -EINVAL;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index 544b6c6f1ea3..8cc1d7ae4d98 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -43,6 +43,12 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
goto out;
}
+ if (fw->size < MT7996_EEPROM_SIZE) {
+ dev_err(dev->mt76.dev, "Invalid default bin size\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
dev->flash_mode = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 9270a68e6a38..74ba07577bfd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -1737,6 +1737,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
phy3 = mt7996_phy3(dev);
dev->recovery.hw_full_reset = true;
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
wake_up(&dev->mt76.mcu.wait);
ieee80211_stop_queues(mt76_hw(dev));
if (phy2)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index c559212ea86a..dd00a0fb5df0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -998,8 +998,6 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
sinfo->txrate.flags = txrate->flags;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
}
- sinfo->txrate.flags = txrate->flags;
- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
sinfo->ack_signal = (s8)msta->ack_signal;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 2abc8d15ea85..3699a43df5a9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -3172,21 +3172,31 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
return ret;
/* fixed field */
+ if (skb->len < 4) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
skb_pull(skb, 4);
buf = skb->data;
- while (buf - skb->data < skb->len) {
+ while (buf - skb->data + sizeof(struct tlv) <= skb->len) {
struct tlv *tlv = (struct tlv *)buf;
+ u16 tlv_len = le16_to_cpu(tlv->len);
+
+ if (tlv_len < sizeof(*tlv) ||
+ tlv_len > skb->len - (buf - skb->data))
+ break;
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION:
- *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv)));
+ if (tlv_len >= sizeof(*tlv) + sizeof(__le32))
+ *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv)));
break;
default:
break;
}
- buf += le16_to_cpu(tlv->len);
+ buf += tlv_len;
}
dev_kfree_skb(skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index fee79666fe16..70f65350111d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -591,7 +591,7 @@ enum {
#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \
sizeof(struct bss_bcn_content_tlv) + \
4 + MT_TXD_SIZE + \
- sizeof(struct bss_bcn_cntdwn_tlv) + \
+ sizeof(struct bss_bcn_cntdwn_tlv) * 2 + \
sizeof(struct bss_bcn_mbss_tlv))
#define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \
MT7996_BEACON_UPDATE_SIZE)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
index f0fa0f513be9..5f3b3ca15624 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
@@ -11,6 +11,8 @@
#include "mac.h"
#include "../trace.h"
+#define INVALID_REG_ADDR 0xffffffff
+
static const struct __base mt7996_reg_base[] = {
[WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } },
[WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } },
@@ -125,7 +127,7 @@ static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
return dev->reg.map[i].mapped + ofs;
}
- return 0;
+ return INVALID_REG_ADDR;
}
static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr)
@@ -154,7 +156,7 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
{
u32 addr = __mt7996_reg_addr(dev, offset);
- if (addr) {
+ if (addr != INVALID_REG_ADDR) {
memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
return;
}
@@ -170,7 +172,7 @@ static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset)
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
u32 addr = __mt7996_reg_addr(dev, offset), val;
- if (addr)
+ if (addr != INVALID_REG_ADDR)
return dev->bus_ops->rr(mdev, addr);
spin_lock_bh(&dev->reg_lock);
@@ -185,7 +187,7 @@ static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val)
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
u32 addr = __mt7996_reg_addr(dev, offset);
- if (addr) {
+ if (addr != INVALID_REG_ADDR) {
dev->bus_ops->wr(mdev, addr, val);
return;
}
@@ -200,7 +202,7 @@ static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
u32 addr = __mt7996_reg_addr(dev, offset);
- if (addr)
+ if (addr != INVALID_REG_ADDR)
return dev->bus_ops->rmw(mdev, addr, mask, val);
spin_lock_bh(&dev->reg_lock);
diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c
index 2c761d469c06..76464bbf1532 100644
--- a/drivers/net/wireless/mediatek/mt76/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/sdio.c
@@ -519,6 +519,10 @@ mt76s_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
enum mt76_txq_id qid, struct sk_buff *skb,
struct mt76_wcid *wcid, struct ieee80211_sta *sta)
{
+ struct ieee80211_tx_status status = {
+ .sta = sta,
+ };
+
struct mt76_tx_info tx_info = {
.skb = skb,
};
@@ -530,8 +534,13 @@ mt76s_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
skb->prev = skb->next = NULL;
err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
- if (err < 0)
+ if (err < 0) {
+ status.skb = tx_info.skb;
+ spin_lock_bh(&dev->rx_lock);
+ ieee80211_tx_status_ext(dev->hw, &status);
+ spin_unlock_bh(&dev->rx_lock);
return err;
+ }
q->entry[q->head].skb = tx_info.skb;
q->entry[q->head].buf_sz = len;
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
index fa8d8e216bd0..add2e856e007 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
@@ -1648,7 +1648,7 @@ static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom)
static void rtl8180_eeprom_read(struct rtl8180_priv *priv)
{
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
int eeprom_cck_table_adr;
u16 eeprom_val;
int i;
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
index 70df4c0b939c..5dfc0e9cb1c0 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -1441,7 +1441,7 @@ static int rtl8187_probe(struct usb_interface *intf,
struct usb_device *udev = interface_to_usbdev(intf);
struct ieee80211_hw *dev;
struct rtl8187_priv *priv;
- struct eeprom_93cx6 eeprom;
+ struct eeprom_93cx6 eeprom = {};
struct ieee80211_channel *channel;
const char *chip_name;
u16 txpwr, reg;
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index b978d31e47ca..7bd72cc1f662 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -2228,13 +2228,17 @@ int rtl_pci_probe(struct pci_dev *pdev,
rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG,
"%s: failed to register IRQ handler\n",
wiphy_name(hw->wiphy));
- goto fail3;
+ goto fail6;
}
rtlpci->irq_alloc = 1;
set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
return 0;
+fail6:
+ rtl_deinit_rfkill(hw);
+ rtl_debug_remove_one(hw);
+ ieee80211_unregister_hw(hw);
fail5:
rtl_pci_deinit(hw);
fail4:
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
index 2122ff5ab50a..a9f61ebf7585 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.c
+++ b/drivers/net/wireless/realtek/rtw88/tx.c
@@ -609,6 +609,7 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev,
ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb);
if (ret) {
rtw_err(rtwdev, "failed to write TX skb to HCI\n");
+ ieee80211_free_txskb(rtwdev->hw, skb);
return ret;
}
return 0;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 3d63f8b2770e..fede78213ad8 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -4223,7 +4223,7 @@ static void rtw89_mac_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data,
ies->len);
- if (!elem || elem->datalen < 10 ||
+ if (!elem || elem->datalen < 11 ||
!(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT))
*tolerated = false;
rcu_read_unlock();
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index c9bfea77d0ec..bacbb4988984 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2084,6 +2084,7 @@ static int mac80211_hwsim_start(struct ieee80211_hw *hw)
static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
{
struct mac80211_hwsim_data *data = hw->priv;
+ struct sk_buff *skb;
int i;
data->started = false;
@@ -2091,8 +2092,8 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
for (i = 0; i < ARRAY_SIZE(data->link_data); i++)
hrtimer_cancel(&data->link_data[i].beacon_timer);
- while (!skb_queue_empty(&data->pending))
- ieee80211_free_txskb(hw, skb_dequeue(&data->pending));
+ while ((skb = skb_dequeue(&data->pending)))
+ ieee80211_free_txskb(hw, skb);
wiphy_dbg(hw->wiphy, "%s\n", __func__);
}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index 8505d84eeed6..c7da2c498e7f 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
@@ -1351,6 +1351,14 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id)
struct zd_usb *usb;
struct ieee80211_hw *hw = NULL;
+ /*
+ * ZD1211 devices are single-function. Reject secondary interfaces
+ * to prevent multiple instances from conflicting on hardcoded endpoints
+ * and triggering recursive locking warnings.
+ */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
print_id(udev);
if (id->driver_info & DEVICE_INSTALLER)
diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c
index 6abe7b38acfb..c804c16f4b3b 100644
--- a/drivers/nfc/pn533/pn533.c
+++ b/drivers/nfc/pn533/pn533.c
@@ -434,6 +434,18 @@ static int pn533_send_async_complete(struct pn533 *dev)
return rc;
}
+static int pn533_send_cmd_frame(struct pn533 *dev, struct pn533_cmd *cmd)
+{
+ struct sk_buff *req = cmd->req;
+ int rc;
+
+ skb_get(req);
+ dev->cmd = cmd;
+ rc = dev->phy_ops->send_frame(dev, req);
+ dev_kfree_skb(req);
+ return rc;
+}
+
static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
struct sk_buff *req,
pn533_send_async_complete_t complete_cb,
@@ -458,8 +470,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
mutex_lock(&dev->cmd_lock);
if (!dev->cmd_pending) {
- dev->cmd = cmd;
- rc = dev->phy_ops->send_frame(dev, req);
+ rc = pn533_send_cmd_frame(dev, cmd);
if (rc) {
dev->cmd = NULL;
goto error;
@@ -529,8 +540,7 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
pn533_build_cmd_frame(dev, cmd_code, req);
- dev->cmd = cmd;
- rc = dev->phy_ops->send_frame(dev, req);
+ rc = pn533_send_cmd_frame(dev, cmd);
if (rc < 0) {
dev->cmd = NULL;
kfree(cmd);
@@ -569,8 +579,7 @@ static void pn533_wq_cmd(struct work_struct *work)
mutex_unlock(&dev->cmd_lock);
- dev->cmd = cmd;
- rc = dev->phy_ops->send_frame(dev, cmd->req);
+ rc = pn533_send_cmd_frame(dev, cmd);
if (rc < 0) {
dev->cmd = NULL;
dev_kfree_skb(cmd->req);
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 0087c23655c7..f62110713d77 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1745,9 +1745,16 @@ static void ntb_transport_rxc_db(unsigned long data)
static void ntb_tx_copy_callback(void *data,
const struct dmaengine_result *res)
{
+ struct ntb_payload_header __iomem *hdr;
struct ntb_queue_entry *entry = data;
- struct ntb_transport_qp *qp = entry->qp;
- struct ntb_payload_header __iomem *hdr = entry->tx_hdr;
+ struct ntb_transport_qp *qp;
+ unsigned int len;
+ void *cb_data;
+
+ qp = entry->qp;
+ hdr = entry->tx_hdr;
+ cb_data = entry->cb_data;
+ len = entry->len;
/* we need to check DMA results if we are using DMA */
if (res) {
@@ -1787,15 +1794,13 @@ static void ntb_tx_copy_callback(void *data,
* "link down" or similar. Since no payload is being sent in these
* cases, there is nothing to add to the completion queue.
*/
- if (entry->len > 0) {
- qp->tx_bytes += entry->len;
-
- if (qp->tx_handler)
- qp->tx_handler(qp, qp->cb_data, entry->cb_data,
- entry->len);
- }
+ if (len > 0)
+ qp->tx_bytes += len;
ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q);
+
+ if (len > 0 && qp->tx_handler)
+ qp->tx_handler(qp, qp->cb_data, cb_data, len);
}
static void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset)
@@ -1919,15 +1924,6 @@ static int ntb_process_tx(struct ntb_transport_qp *qp,
return -EAGAIN;
}
- if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
- if (qp->tx_handler)
- qp->tx_handler(qp, qp->cb_data, NULL, -EIO);
-
- ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
- &qp->tx_free_q);
- return 0;
- }
-
ntb_async_tx(qp, entry);
qp->tx_index++;
@@ -2299,9 +2295,11 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
if (!qp || !len)
return -EINVAL;
- /* If the qp link is down already, just ignore. */
if (!qp->link_is_up)
- return 0;
+ return -ENOLINK;
+
+ if (len > qp->tx_max_frame - sizeof(struct ntb_payload_header))
+ return -EMSGSIZE;
entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
if (!entry) {
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 9e54083505f3..4cfed0a3d142 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -886,6 +886,14 @@ static int discover_arenas(struct btt *btt)
arena->external_lba_start = cur_nlba;
parse_arena_meta(arena, super, cur_off);
+ if (arena->nfree < btt->nd_region->num_lanes) {
+ dev_err(to_dev(arena),
+ "nfree %u smaller than lane count %d\n",
+ arena->nfree, btt->nd_region->num_lanes);
+ ret = -ENODEV;
+ goto out;
+ }
+
ret = log_set_indices(arena);
if (ret) {
dev_err(to_dev(arena),
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 04f4a049599a..93ff219d91fc 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -145,10 +145,21 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
/* label sizes larger than 128 arrived with v1.2 */
version = __le16_to_cpu(nsindex[i]->major) * 100
+ __le16_to_cpu(nsindex[i]->minor);
- if (version >= 102)
+ if (version >= 102) {
+ /*
+ * labelsize feeds the shift below; only 0 (128-byte)
+ * and 1 (256-byte) are valid -- a larger value would
+ * overflow or exceed the width of int.
+ */
+ if (nsindex[i]->labelsize > 1) {
+ dev_dbg(dev, "nsindex%d labelsize: %d invalid\n",
+ i, nsindex[i]->labelsize);
+ continue;
+ }
labelsize = 1 << (7 + nsindex[i]->labelsize);
- else
+ } else {
labelsize = 128;
+ }
if (labelsize != sizeof_namespace_label(ndd)) {
dev_dbg(dev, "nsindex%d labelsize %d invalid\n",
@@ -202,7 +213,7 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
}
nslot = __le32_to_cpu(nsindex[i]->nslot);
- if (nslot * sizeof_namespace_label(ndd)
+ if ((u64)nslot * sizeof_namespace_label(ndd)
+ 2 * sizeof_namespace_index(ndd)
> ndd->nsarea.config_size) {
dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n",
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index 04bb23757a52..771c03fd850b 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -35,7 +35,6 @@
#include "nvme.h"
#define APPLE_ANS_BOOT_TIMEOUT USEC_PER_SEC
-#define APPLE_ANS_MAX_QUEUE_DEPTH 64
#define APPLE_ANS_COPROC_CPU_CONTROL 0x44
#define APPLE_ANS_COPROC_CPU_CONTROL_RUN BIT(4)
@@ -48,9 +47,6 @@
#define APPLE_ANS_BOOT_STATUS 0x1300
#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55
-#define APPLE_ANS_UNKNOWN_CTRL 0x24008
-#define APPLE_ANS_PRP_NULL_CHECK BIT(11)
-
#define APPLE_ANS_LINEAR_SQ_CTRL 0x24908
#define APPLE_ANS_LINEAR_SQ_EN BIT(0)
@@ -75,6 +71,8 @@
#define APPLE_NVME_AQ_DEPTH 2
#define APPLE_NVME_AQ_MQ_TAG_DEPTH (APPLE_NVME_AQ_DEPTH - 1)
+#define APPLE_NVME_IOSQES 7
+
/*
* These can be higher, but we need to ensure that any command doesn't
* require an sg allocation that needs more than a page of data.
@@ -142,6 +140,7 @@ struct apple_nvme_queue {
u32 __iomem *sq_db;
u32 __iomem *cq_db;
+ u16 sq_tail;
u16 cq_head;
u8 cq_phase;
@@ -149,6 +148,23 @@ struct apple_nvme_queue {
bool enabled;
};
+static inline bool apple_nvme_queue_enabled(struct apple_nvme_queue *q)
+{
+ /* Pair with apple_nvme_enable_queue(). */
+ return smp_load_acquire(&q->enabled);
+}
+
+static inline void apple_nvme_enable_queue(struct apple_nvme_queue *q)
+{
+ /* Publish queue initialization before setting q->enabled. */
+ smp_store_release(&q->enabled, true);
+}
+
+static inline void apple_nvme_disable_queue(struct apple_nvme_queue *q)
+{
+ WRITE_ONCE(q->enabled, false);
+}
+
/*
* The apple_nvme_iod describes the data in an I/O.
*
@@ -166,11 +182,17 @@ struct apple_nvme_iod {
struct scatterlist *sg;
};
+struct apple_nvme_hw {
+ bool has_lsq_nvmmu;
+ u32 max_queue_depth;
+};
+
struct apple_nvme {
struct device *dev;
void __iomem *mmio_coproc;
void __iomem *mmio_nvme;
+ const struct apple_nvme_hw *hw;
struct device **pd_dev;
struct device_link **pd_link;
@@ -215,10 +237,12 @@ static inline struct apple_nvme *queue_to_apple_nvme(struct apple_nvme_queue *q)
static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q)
{
+ struct apple_nvme *anv = queue_to_apple_nvme(q);
+
if (q->is_adminq)
return APPLE_NVME_AQ_DEPTH;
- return APPLE_ANS_MAX_QUEUE_DEPTH;
+ return anv->hw->max_queue_depth;
}
static void apple_nvme_rtkit_crashed(void *cookie)
@@ -280,20 +304,43 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag)
"NVMMU TCB invalidation failed\n");
}
-static void apple_nvme_submit_cmd(struct apple_nvme_queue *q,
+static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q,
+ struct nvme_command *cmd)
+{
+ struct apple_nvme *anv = queue_to_apple_nvme(q);
+
+ spin_lock_irq(&anv->lock);
+
+ if (q->is_adminq)
+ memcpy(&q->sqes[q->sq_tail], cmd, sizeof(*cmd));
+ else
+ memcpy((void *)q->sqes + (q->sq_tail << APPLE_NVME_IOSQES),
+ cmd, sizeof(*cmd));
+
+ if (++q->sq_tail == apple_nvme_queue_depth(q))
+ q->sq_tail = 0;
+
+ writel(q->sq_tail, q->sq_db);
+ spin_unlock_irq(&anv->lock);
+}
+
+
+static void apple_nvme_submit_cmd_t8103(struct apple_nvme_queue *q,
struct nvme_command *cmd)
{
struct apple_nvme *anv = queue_to_apple_nvme(q);
u32 tag = nvme_tag_from_cid(cmd->common.command_id);
struct apple_nvmmu_tcb *tcb = &q->tcbs[tag];
- tcb->opcode = cmd->common.opcode;
+ tcb->opcode = 0;
tcb->prp1 = cmd->common.dptr.prp1;
tcb->prp2 = cmd->common.dptr.prp2;
tcb->length = cmd->rw.length;
tcb->command_id = tag;
- if (nvme_is_write(cmd))
+ if (!cmd->common.dptr.prp1)
+ tcb->dma_flags = 0;
+ else if (nvme_is_write(cmd))
tcb->dma_flags = APPLE_ANS_TCB_DMA_TO_DEVICE;
else
tcb->dma_flags = APPLE_ANS_TCB_DMA_FROM_DEVICE;
@@ -590,7 +637,8 @@ static inline void apple_nvme_handle_cqe(struct apple_nvme_queue *q,
__u16 command_id = READ_ONCE(cqe->command_id);
struct request *req;
- apple_nvmmu_inval(q, command_id);
+ if (anv->hw->has_lsq_nvmmu)
+ apple_nvmmu_inval(q, command_id);
req = nvme_find_rq(apple_nvme_queue_tagset(anv, q), command_id);
if (unlikely(!req)) {
@@ -644,7 +692,7 @@ static bool apple_nvme_handle_cq(struct apple_nvme_queue *q, bool force)
bool found;
DEFINE_IO_COMP_BATCH(iob);
- if (!READ_ONCE(q->enabled) && !force)
+ if (!apple_nvme_queue_enabled(q) && !force)
return false;
found = apple_nvme_poll_cq(q, &iob);
@@ -684,7 +732,7 @@ static int apple_nvme_create_cq(struct apple_nvme *anv)
c.create_cq.opcode = nvme_admin_create_cq;
c.create_cq.prp1 = cpu_to_le64(anv->ioq.cq_dma_addr);
c.create_cq.cqid = cpu_to_le16(1);
- c.create_cq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1);
+ c.create_cq.qsize = cpu_to_le16(anv->hw->max_queue_depth - 1);
c.create_cq.cq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED);
c.create_cq.irq_vector = cpu_to_le16(0);
@@ -712,7 +760,7 @@ static int apple_nvme_create_sq(struct apple_nvme *anv)
c.create_sq.opcode = nvme_admin_create_sq;
c.create_sq.prp1 = cpu_to_le64(anv->ioq.sq_dma_addr);
c.create_sq.sqid = cpu_to_le16(1);
- c.create_sq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1);
+ c.create_sq.qsize = cpu_to_le16(anv->hw->max_queue_depth - 1);
c.create_sq.sq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG);
c.create_sq.cqid = cpu_to_le16(1);
@@ -747,7 +795,7 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
* We should not need to do this, but we're still using this to
* ensure we can drain requests on a dying queue.
*/
- if (unlikely(!READ_ONCE(q->enabled)))
+ if (unlikely(!apple_nvme_queue_enabled(q)))
return BLK_STS_IOERR;
if (!nvme_check_ready(&anv->ctrl, req, true))
@@ -764,7 +812,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
}
nvme_start_request(req);
- apple_nvme_submit_cmd(q, cmnd);
+
+ if (anv->hw->has_lsq_nvmmu)
+ apple_nvme_submit_cmd_t8103(q, cmnd);
+ else
+ apple_nvme_submit_cmd_t8015(q, cmnd);
+
return BLK_STS_OK;
out_free_cmd:
@@ -824,7 +877,7 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown)
nvme_quiesce_io_queues(&anv->ctrl);
if (!dead) {
- if (READ_ONCE(anv->ioq.enabled)) {
+ if (apple_nvme_queue_enabled(&anv->ioq)) {
apple_nvme_remove_sq(anv);
apple_nvme_remove_cq(anv);
}
@@ -848,8 +901,8 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown)
nvme_disable_ctrl(&anv->ctrl, false);
}
- WRITE_ONCE(anv->ioq.enabled, false);
- WRITE_ONCE(anv->adminq.enabled, false);
+ apple_nvme_disable_queue(&anv->ioq);
+ apple_nvme_disable_queue(&anv->adminq);
mb(); /* ensure that nvme_queue_rq() sees that enabled is cleared */
nvme_quiesce_admin_queue(&anv->ctrl);
@@ -968,14 +1021,16 @@ static const struct blk_mq_ops apple_nvme_mq_ops = {
static void apple_nvme_init_queue(struct apple_nvme_queue *q)
{
unsigned int depth = apple_nvme_queue_depth(q);
+ struct apple_nvme *anv = queue_to_apple_nvme(q);
+ q->sq_tail = 0;
q->cq_head = 0;
q->cq_phase = 1;
- memset(q->tcbs, 0,
- APPLE_ANS_MAX_QUEUE_DEPTH * sizeof(struct apple_nvmmu_tcb));
+ if (anv->hw->has_lsq_nvmmu)
+ memset(q->tcbs, 0, anv->hw->max_queue_depth
+ * sizeof(struct apple_nvmmu_tcb));
memset(q->cqes, 0, depth * sizeof(struct nvme_completion));
- WRITE_ONCE(q->enabled, true);
- wmb(); /* ensure the first interrupt sees the initialization */
+ apple_nvme_enable_queue(q);
}
static void apple_nvme_reset_work(struct work_struct *work)
@@ -1055,33 +1110,23 @@ static void apple_nvme_reset_work(struct work_struct *work)
dma_set_max_seg_size(anv->dev, 0xffffffff);
- /*
- * Enable NVMMU and linear submission queues.
- * While we could keep those disabled and pretend this is slightly
- * more common NVMe controller we'd still need some quirks (e.g.
- * sq entries will be 128 bytes) and Apple might drop support for
- * that mode in the future.
- */
- writel(APPLE_ANS_LINEAR_SQ_EN,
- anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL);
-
- /* Allow as many pending command as possible for both queues */
- writel(APPLE_ANS_MAX_QUEUE_DEPTH | (APPLE_ANS_MAX_QUEUE_DEPTH << 16),
- anv->mmio_nvme + APPLE_ANS_MAX_PEND_CMDS_CTRL);
+ if (anv->hw->has_lsq_nvmmu) {
+ /*
+ * Enable NVMMU and linear submission queues which is required
+ * since T6000.
+ */
+ writel(APPLE_ANS_LINEAR_SQ_EN,
+ anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL);
- /* Setup the NVMMU for the maximum admin and IO queue depth */
- writel(APPLE_ANS_MAX_QUEUE_DEPTH - 1,
- anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS);
+ /* Allow as many pending command as possible for both queues */
+ writel(anv->hw->max_queue_depth
+ | (anv->hw->max_queue_depth << 16), anv->mmio_nvme
+ + APPLE_ANS_MAX_PEND_CMDS_CTRL);
- /*
- * This is probably a chicken bit: without it all commands where any PRP
- * is set to zero (including those that don't use that field) fail and
- * the co-processor complains about "completed with err BAD_CMD-" or
- * a "NULL_PRP_PTR_ERR" in the syslog
- */
- writel(readl(anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL) &
- ~APPLE_ANS_PRP_NULL_CHECK,
- anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL);
+ /* Setup the NVMMU for the maximum admin and IO queue depth */
+ writel(anv->hw->max_queue_depth - 1,
+ anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS);
+ }
/* Setup the admin queue */
aqa = APPLE_NVME_AQ_DEPTH - 1;
@@ -1090,14 +1135,16 @@ static void apple_nvme_reset_work(struct work_struct *work)
writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ);
writeq(anv->adminq.cq_dma_addr, anv->mmio_nvme + NVME_REG_ACQ);
- /* Setup NVMMU for both queues */
- writeq(anv->adminq.tcb_dma_addr,
- anv->mmio_nvme + APPLE_NVMMU_ASQ_TCB_BASE);
- writeq(anv->ioq.tcb_dma_addr,
- anv->mmio_nvme + APPLE_NVMMU_IOSQ_TCB_BASE);
+ if (anv->hw->has_lsq_nvmmu) {
+ /* Setup NVMMU for both queues */
+ writeq(anv->adminq.tcb_dma_addr,
+ anv->mmio_nvme + APPLE_NVMMU_ASQ_TCB_BASE);
+ writeq(anv->ioq.tcb_dma_addr,
+ anv->mmio_nvme + APPLE_NVMMU_IOSQ_TCB_BASE);
+ }
anv->ctrl.sqsize =
- APPLE_ANS_MAX_QUEUE_DEPTH - 1; /* 0's based queue depth */
+ anv->hw->max_queue_depth - 1; /* 0's based queue depth */
anv->ctrl.cap = readq(anv->mmio_nvme + NVME_REG_CAP);
dev_dbg(anv->dev, "Enabling controller now");
@@ -1266,7 +1313,7 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv)
* must be marked as reserved in the IO queue.
*/
anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH;
- anv->tagset.queue_depth = APPLE_ANS_MAX_QUEUE_DEPTH - 1;
+ anv->tagset.queue_depth = anv->hw->max_queue_depth - 1;
anv->tagset.timeout = NVME_IO_TIMEOUT;
anv->tagset.numa_node = NUMA_NO_NODE;
anv->tagset.cmd_size = sizeof(struct apple_nvme_iod);
@@ -1291,6 +1338,7 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv,
struct apple_nvme_queue *q)
{
unsigned int depth = apple_nvme_queue_depth(q);
+ size_t iosq_size;
q->cqes = dmam_alloc_coherent(anv->dev,
depth * sizeof(struct nvme_completion),
@@ -1298,22 +1346,28 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv,
if (!q->cqes)
return -ENOMEM;
- q->sqes = dmam_alloc_coherent(anv->dev,
- depth * sizeof(struct nvme_command),
+ if (anv->hw->has_lsq_nvmmu)
+ iosq_size = depth * sizeof(struct nvme_command);
+ else
+ iosq_size = depth << APPLE_NVME_IOSQES;
+
+ q->sqes = dmam_alloc_coherent(anv->dev, iosq_size,
&q->sq_dma_addr, GFP_KERNEL);
if (!q->sqes)
return -ENOMEM;
- /*
- * We need the maximum queue depth here because the NVMMU only has a
- * single depth configuration shared between both queues.
- */
- q->tcbs = dmam_alloc_coherent(anv->dev,
- APPLE_ANS_MAX_QUEUE_DEPTH *
- sizeof(struct apple_nvmmu_tcb),
- &q->tcb_dma_addr, GFP_KERNEL);
- if (!q->tcbs)
- return -ENOMEM;
+ if (anv->hw->has_lsq_nvmmu) {
+ /*
+ * We need the maximum queue depth here because the NVMMU only
+ * has a single depth configuration shared between both queues.
+ */
+ q->tcbs = dmam_alloc_coherent(anv->dev,
+ anv->hw->max_queue_depth *
+ sizeof(struct apple_nvmmu_tcb),
+ &q->tcb_dma_addr, GFP_KERNEL);
+ if (!q->tcbs)
+ return -ENOMEM;
+ }
/*
* initialize phase to make sure the allocated and empty memory
@@ -1397,6 +1451,12 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev)
anv->adminq.is_adminq = true;
platform_set_drvdata(pdev, anv);
+ anv->hw = of_device_get_match_data(&pdev->dev);
+ if (!anv->hw) {
+ ret = -ENODEV;
+ goto put_dev;
+ }
+
ret = apple_nvme_attach_genpd(anv);
if (ret < 0) {
dev_err_probe(dev, ret, "Failed to attach power domains");
@@ -1428,10 +1488,17 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev)
goto put_dev;
}
- anv->adminq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_ASQ_DB;
- anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB;
- anv->ioq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_IOSQ_DB;
- anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB;
+ if (anv->hw->has_lsq_nvmmu) {
+ anv->adminq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_ASQ_DB;
+ anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB;
+ anv->ioq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_IOSQ_DB;
+ anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB;
+ } else {
+ anv->adminq.sq_db = anv->mmio_nvme + NVME_REG_DBS;
+ anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB;
+ anv->ioq.sq_db = anv->mmio_nvme + NVME_REG_DBS + 8;
+ anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB;
+ }
anv->sart = devm_apple_sart_get(dev);
if (IS_ERR(anv->sart)) {
@@ -1555,6 +1622,15 @@ static int apple_nvme_remove(struct platform_device *pdev)
nvme_stop_ctrl(&anv->ctrl);
nvme_remove_namespaces(&anv->ctrl);
apple_nvme_disable(anv, true);
+ if (anv->ctrl.admin_q && !blk_queue_dying(anv->ctrl.admin_q)) {
+ /*
+ * If the controller was reset during removal, it's possible
+ * user requests may be waiting on a stopped queue. Start the
+ * queue to flush these to completion.
+ */
+ nvme_unquiesce_admin_queue(&anv->ctrl);
+ blk_mq_destroy_queue(anv->ctrl.admin_q);
+ }
nvme_uninit_ctrl(&anv->ctrl);
if (apple_rtkit_is_running(anv->rtk))
@@ -1599,8 +1675,19 @@ static int apple_nvme_suspend(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(apple_nvme_pm_ops, apple_nvme_suspend,
apple_nvme_resume);
+static const struct apple_nvme_hw apple_nvme_t8015_hw = {
+ .has_lsq_nvmmu = false,
+ .max_queue_depth = 16,
+};
+
+static const struct apple_nvme_hw apple_nvme_t8103_hw = {
+ .has_lsq_nvmmu = true,
+ .max_queue_depth = 64,
+};
+
static const struct of_device_id apple_nvme_of_match[] = {
- { .compatible = "apple,nvme-ans2" },
+ { .compatible = "apple,t8015-nvme-ans2", .data = &apple_nvme_t8015_hw },
+ { .compatible = "apple,nvme-ans2", .data = &apple_nvme_t8103_hw },
{},
};
MODULE_DEVICE_TABLE(of, apple_nvme_of_match);
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 331ddcd8e30d..7b8de361046d 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -4534,7 +4534,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) >
PAGE_SIZE);
- ctrl->discard_page = alloc_page(GFP_KERNEL);
+ ctrl->discard_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!ctrl->discard_page) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 20b5048ad15b..3d3cf79d9249 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2092,9 +2092,15 @@ __nvme_fc_init_request(struct nvme_fc_ctrl *ctrl,
dev_err(ctrl->dev,
"FCP Op failed - rspiu dma mapping failed.\n");
ret = -EFAULT;
+ goto out_unmap;
}
atomic_set(&op->state, FCPOP_STATE_IDLE);
+ return 0;
+
+out_unmap:
+ fc_dma_unmap_single(ctrl->lport->dev, op->fcp_req.cmddma,
+ sizeof(op->cmd_iu), DMA_TO_DEVICE);
out_on_error:
return ret;
}
@@ -2310,7 +2316,7 @@ nvme_fc_create_hw_io_queues(struct nvme_fc_ctrl *ctrl, u16 qsize)
return 0;
delete_queues:
- for (; i > 0; i--)
+ for (--i; i > 0; i--)
__nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[i], i);
return ret;
}
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 5cf050e562b7..f19bef1d30dc 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -14,45 +14,54 @@ enum {
NVME_IOCTL_PARTITION = (1 << 1),
};
-static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
- unsigned int flags, bool open_for_write)
+static bool nvme_admin_cmd_allowed(struct nvme_ctrl *ctrl,
+ struct nvme_command *c)
{
- u32 effects;
-
- /*
- * Do not allow unprivileged passthrough on partitions, as that allows an
- * escape from the containment of the partition.
- */
- if (flags & NVME_IOCTL_PARTITION)
- goto admin;
-
- /*
- * Do not allow unprivileged processes to send vendor specific or fabrics
- * commands as we can't be sure about their effects.
- */
- if (c->common.opcode >= nvme_cmd_vendor_start ||
- c->common.opcode == nvme_fabrics_command)
- goto admin;
-
/*
* Do not allow unprivileged passthrough of admin commands except
* for a subset of identify commands that contain information required
* to form proper I/O commands in userspace and do not expose any
* potentially sensitive information.
*/
- if (!ns) {
- if (c->common.opcode == nvme_admin_identify) {
- switch (c->identify.cns) {
- case NVME_ID_CNS_NS:
- case NVME_ID_CNS_CS_NS:
- case NVME_ID_CNS_NS_CS_INDEP:
- case NVME_ID_CNS_CS_CTRL:
- case NVME_ID_CNS_CTRL:
- return true;
- }
+ switch (c->common.opcode) {
+ case nvme_admin_identify:
+ switch (c->identify.cns) {
+ case NVME_ID_CNS_NS:
+ case NVME_ID_CNS_CS_NS:
+ case NVME_ID_CNS_NS_CS_INDEP:
+ case NVME_ID_CNS_CS_CTRL:
+ case NVME_ID_CNS_CTRL:
+ return true;
+ }
+ break;
+ case nvme_admin_set_features:
+ /*
+ * Reject Set Features that change controller state the driver
+ * manages itself; setting them behind the driver's back from
+ * userspace leaves it unable to react correctly. Keep Alive is
+ * only armed for fabrics - on other transports it has no
+ * reserved tag and harms idle power states.
+ */
+ switch (le32_to_cpu(c->features.fid) & 0xff) {
+ case NVME_FEAT_KATO:
+ if (ctrl->ops->flags & NVME_F_FABRICS)
+ break;
+ fallthrough;
+ case NVME_FEAT_HOST_BEHAVIOR:
+ case NVME_FEAT_HOST_MEM_BUF:
+ case NVME_FEAT_NUM_QUEUES:
+ case NVME_FEAT_AUTO_PST:
+ return false;
}
- goto admin;
+ break;
}
+ return capable(CAP_SYS_ADMIN);
+}
+
+static bool nvme_ns_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
+ bool open_for_write)
+{
+ u32 effects;
/*
* Check if the controller provides a Commands Supported and Effects log
@@ -61,7 +70,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
*/
effects = nvme_command_effects(ns->ctrl, ns, c->common.opcode);
if (!(effects & NVME_CMD_EFFECTS_CSUPP))
- goto admin;
+ return capable(CAP_SYS_ADMIN);
/*
* Don't allow passthrough for command that have intrusive (or unknown)
@@ -70,7 +79,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
NVME_CMD_EFFECTS_UUID_SEL |
NVME_CMD_EFFECTS_SCOPE_MASK))
- goto admin;
+ return capable(CAP_SYS_ADMIN);
/*
* Only allow I/O commands that transfer data to the controller or that
@@ -79,11 +88,34 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
*/
if ((nvme_is_write(c) || (effects & NVME_CMD_EFFECTS_LBCC)) &&
!open_for_write)
- goto admin;
+ return capable(CAP_SYS_ADMIN);
return true;
-admin:
- return capable(CAP_SYS_ADMIN);
+}
+
+static bool nvme_cmd_allowed(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
+ struct nvme_command *c, unsigned int flags,
+ bool open_for_write)
+{
+ /*
+ * Do not allow unprivileged passthrough on partitions, as that
+ * allows an escape from the containment of the partition.
+ */
+ if (flags & NVME_IOCTL_PARTITION)
+ return capable(CAP_SYS_ADMIN);
+
+ /*
+ * Do not allow unprivileged processes to send vendor specific or
+ * fabrics commands as we can't be sure about their effects.
+ */
+ if (c->common.opcode >= nvme_cmd_vendor_start ||
+ c->common.opcode == nvme_fabrics_command)
+ return capable(CAP_SYS_ADMIN);
+
+ if (!ns)
+ return nvme_admin_cmd_allowed(ctrl, c);
+
+ return nvme_ns_cmd_allowed(ns, c, open_for_write);
}
/*
@@ -269,7 +301,8 @@ static int nvme_submit_user_cmd(struct request_queue *q,
return ret;
}
-static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
+static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio,
+ unsigned int flags, bool open_for_write)
{
struct nvme_user_io io;
struct nvme_command c;
@@ -327,6 +360,9 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
c.rw.apptag = cpu_to_le16(io.apptag);
c.rw.appmask = cpu_to_le16(io.appmask);
+ if (!nvme_cmd_allowed(ns->ctrl, ns, &c, flags, open_for_write))
+ return -EACCES;
+
return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata,
meta_len, lower_32_bits(io.slba), NULL, 0, 0);
}
@@ -374,7 +410,7 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
- if (!nvme_cmd_allowed(ns, &c, 0, open_for_write))
+ if (!nvme_cmd_allowed(ctrl, ns, &c, 0, open_for_write))
return -EACCES;
if (cmd.timeout_ms)
@@ -421,7 +457,7 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
- if (!nvme_cmd_allowed(ns, &c, flags, open_for_write))
+ if (!nvme_cmd_allowed(ctrl, ns, &c, flags, open_for_write))
return -EACCES;
if (cmd.timeout_ms)
@@ -566,6 +602,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
const struct nvme_uring_cmd *cmd = io_uring_sqe_cmd(ioucmd->sqe);
struct request_queue *q = ns ? ns->queue : ctrl->admin_q;
+ bool open_for_write = ioucmd->file->f_mode & FMODE_WRITE;
struct nvme_uring_data d;
struct nvme_command c;
struct request *req;
@@ -595,7 +632,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(READ_ONCE(cmd->cdw14));
c.common.cdw15 = cpu_to_le32(READ_ONCE(cmd->cdw15));
- if (!nvme_cmd_allowed(ns, &c, 0, ioucmd->file->f_mode & FMODE_WRITE))
+ if (!nvme_cmd_allowed(ctrl, ns, &c, 0, open_for_write))
return -EACCES;
d.metadata = READ_ONCE(cmd->metadata);
@@ -702,7 +739,7 @@ static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
case NVME_IOCTL_SUBMIT_IO32:
#endif
case NVME_IOCTL_SUBMIT_IO:
- return nvme_submit_io(ns, argp);
+ return nvme_submit_io(ns, argp, flags, open_for_write);
case NVME_IOCTL_IO64_CMD_VEC:
flags |= NVME_IOCTL_VEC;
fallthrough;
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 87d30aaa35f1..e4d35e985a0b 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1843,6 +1843,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)
result = queue_request_irq(nvmeq);
if (result) {
dev->online_queues--;
+ nvme_disable_ctrl(&dev->ctrl, false);
return result;
}
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index f2b171c3169b..88489a787e67 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -93,6 +93,7 @@ struct nvme_tcp_request {
struct bio *curr_bio;
struct iov_iter iter;
+ u32 data_recvd;
/* send state */
size_t offset;
@@ -542,6 +543,29 @@ static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl)
queue_work(nvme_reset_wq, &to_tcp_ctrl(ctrl)->err_work);
}
+/*
+ * NVMe has no short read: a read that completes successfully must
+ * have transferred everything it asked for.
+ */
+static bool nvme_tcp_data_in_short(struct nvme_tcp_queue *queue,
+ struct request *rq)
+{
+ struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq);
+
+ if (le16_to_cpu(req->status) >> 1)
+ return false;
+ if (req_op(rq) != REQ_OP_READ || !req->data_len)
+ return false;
+ if (likely(req->data_recvd == req->data_len))
+ return false;
+
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d tag %#x short data-in: got %u of %u\n",
+ nvme_tcp_queue_id(queue), rq->tag,
+ req->data_recvd, req->data_len);
+ return true;
+}
+
static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
struct nvme_completion *cqe)
{
@@ -561,6 +585,9 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
if (req->status == cpu_to_le16(NVME_SC_SUCCESS))
req->status = cqe->status;
+ if (unlikely(nvme_tcp_data_in_short(queue, rq)))
+ return -EPROTO;
+
if (!nvme_try_complete_req(rq, req->status, cqe->result))
nvme_complete_rq(rq);
queue->nr_cqe++;
@@ -571,6 +598,7 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
struct nvme_tcp_data_pdu *pdu)
{
+ struct nvme_tcp_request *req;
struct request *rq;
rq = nvme_find_rq(nvme_tcp_tagset(queue), pdu->command_id);
@@ -581,7 +609,15 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
return -ENOENT;
}
- if (!blk_rq_payload_bytes(rq)) {
+ if (rq_data_dir(rq) != READ) {
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d tag %#x unexpected data for a write\n",
+ nvme_tcp_queue_id(queue), rq->tag);
+ return -EIO;
+ }
+
+ req = blk_mq_rq_to_pdu(rq);
+ if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) {
dev_err(queue->ctrl->ctrl.device,
"queue %d tag %#x unexpected data\n",
nvme_tcp_queue_id(queue), rq->tag);
@@ -675,6 +711,13 @@ static int nvme_tcp_handle_r2t(struct nvme_tcp_queue *queue,
}
req = blk_mq_rq_to_pdu(rq);
+ if (unlikely(rq_data_dir(rq) != WRITE)) {
+ dev_err(queue->ctrl->ctrl.device,
+ "req %d unexpected r2t for a non-write command\n",
+ rq->tag);
+ return -EPROTO;
+ }
+
if (unlikely(!r2t_length)) {
dev_err(queue->ctrl->ctrl.device,
"req %d r2t len is %u, probably a bug...\n",
@@ -860,6 +903,7 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
*len -= recv_len;
*offset += recv_len;
queue->data_remaining -= recv_len;
+ req->data_recvd += recv_len;
}
if (!queue->data_remaining) {
@@ -868,6 +912,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH;
} else {
if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) {
+ if (unlikely(nvme_tcp_data_in_short(queue, rq)))
+ return -EPROTO;
nvme_tcp_end_request(rq,
le16_to_cpu(req->status));
queue->nr_cqe++;
@@ -916,6 +962,9 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
pdu->command_id);
struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq);
+ if (unlikely(nvme_tcp_data_in_short(queue, rq)))
+ return -EPROTO;
+
nvme_tcp_end_request(rq, le16_to_cpu(req->status));
queue->nr_cqe++;
}
@@ -2414,6 +2463,7 @@ static blk_status_t nvme_tcp_setup_cmd_pdu(struct nvme_ns *ns,
req->status = cpu_to_le16(NVME_SC_SUCCESS);
req->offset = 0;
req->data_sent = 0;
+ req->data_recvd = 0;
req->pdu_len = 0;
req->pdu_sent = 0;
req->h2cdata_left = 0;
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index 9fe6ad89bf83..b3a0e1946453 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -227,6 +227,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
sq->dhchap_skey = NULL;
}
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq)
+{
+ cancel_delayed_work_sync(&sq->auth_expired_work);
+ nvmet_auth_sq_free(sq);
+}
+
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
{
ctrl->shash_id = 0;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index e435996c5537..3b1b72022a97 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -804,7 +804,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
wait_for_completion(&sq->confirm_done);
wait_for_completion(&sq->free_done);
percpu_ref_exit(&sq->ref);
- nvmet_auth_sq_free(sq);
+ nvmet_auth_sq_destroy(sq);
/*
* we must reference the ctrl again after waiting for inflight IO
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 65a1a4aae726..193bc75de833 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -31,12 +31,16 @@ void nvmet_auth_sq_init(struct nvmet_sq *sq)
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
}
-static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
+static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d, u32 tl)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_negotiate_data *data = d;
int i, hash_id = 0, fallback_hash_id = 0, dhgid, fallback_dhgid;
+ if (tl < sizeof(*data) +
+ sizeof(struct nvmf_auth_dhchap_protocol_descriptor))
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
__func__, ctrl->cntlid, req->sq->qid,
data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
@@ -53,6 +57,10 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
NVME_AUTH_DHCHAP_AUTH_ID)
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+ if (data->auth_protocol[0].dhchap.dhlen > NVME_AUTH_DHCHAP_MAX_DH_IDS ||
+ data->auth_protocol[0].dhchap.halen > NVME_AUTH_DHCHAP_MAX_HASH_IDS)
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i];
@@ -265,7 +273,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
} else if (data->auth_id != req->sq->dhchap_step)
goto done_failure1;
/* Validate negotiation parameters */
- dhchap_status = nvmet_auth_negotiate(req, d);
+ dhchap_status = nvmet_auth_negotiate(req, d, tl);
if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index c0648216b9e4..c037d1daf94d 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -704,6 +704,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_init(struct nvmet_sq *sq);
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_free(struct nvmet_sq *sq);
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq);
int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id);
bool nvmet_check_auth_status(struct nvmet_req *req);
int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
@@ -728,6 +729,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq)
}
static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {};
static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
+static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {};
static inline bool nvmet_check_auth_status(struct nvmet_req *req)
{
return true;
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 32632e8af832..0dfe264968ff 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -37,6 +37,10 @@
#define NVMET_RDMA_MAX_MDTS 8
#define NVMET_RDMA_MAX_METADATA_MDTS 5
+#define NVMET_RDMA_BACKLOG 128
+
+#define NVMET_RDMA_DISCRETE_RSP_TAG -1
+
struct nvmet_rdma_srq;
struct nvmet_rdma_cmd {
@@ -74,7 +78,7 @@ struct nvmet_rdma_rsp {
u32 invalidate_rkey;
struct list_head wait_list;
- struct list_head free_list;
+ int tag;
};
enum nvmet_rdma_queue_state {
@@ -97,8 +101,7 @@ struct nvmet_rdma_queue {
struct nvmet_sq nvme_sq;
struct nvmet_rdma_rsp *rsps;
- struct list_head free_rsps;
- spinlock_t rsps_lock;
+ struct sbitmap rsp_tags;
struct nvmet_rdma_cmd *cmds;
struct work_struct release_work;
@@ -171,7 +174,8 @@ static void nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue);
static void nvmet_rdma_free_rsp(struct nvmet_rdma_device *ndev,
struct nvmet_rdma_rsp *r);
static int nvmet_rdma_alloc_rsp(struct nvmet_rdma_device *ndev,
- struct nvmet_rdma_rsp *r);
+ struct nvmet_rdma_rsp *r,
+ int tag);
static const struct nvmet_fabrics_ops nvmet_rdma_ops;
@@ -209,15 +213,12 @@ static inline bool nvmet_rdma_need_data_out(struct nvmet_rdma_rsp *rsp)
static inline struct nvmet_rdma_rsp *
nvmet_rdma_get_rsp(struct nvmet_rdma_queue *queue)
{
- struct nvmet_rdma_rsp *rsp;
- unsigned long flags;
+ struct nvmet_rdma_rsp *rsp = NULL;
+ int tag;
- spin_lock_irqsave(&queue->rsps_lock, flags);
- rsp = list_first_entry_or_null(&queue->free_rsps,
- struct nvmet_rdma_rsp, free_list);
- if (likely(rsp))
- list_del(&rsp->free_list);
- spin_unlock_irqrestore(&queue->rsps_lock, flags);
+ tag = sbitmap_get(&queue->rsp_tags);
+ if (tag >= 0)
+ rsp = &queue->rsps[tag];
if (unlikely(!rsp)) {
int ret;
@@ -225,13 +226,12 @@ nvmet_rdma_get_rsp(struct nvmet_rdma_queue *queue)
rsp = kzalloc(sizeof(*rsp), GFP_KERNEL);
if (unlikely(!rsp))
return NULL;
- ret = nvmet_rdma_alloc_rsp(queue->dev, rsp);
+ ret = nvmet_rdma_alloc_rsp(queue->dev, rsp,
+ NVMET_RDMA_DISCRETE_RSP_TAG);
if (unlikely(ret)) {
kfree(rsp);
return NULL;
}
-
- rsp->allocated = true;
}
return rsp;
@@ -240,17 +240,13 @@ nvmet_rdma_get_rsp(struct nvmet_rdma_queue *queue)
static inline void
nvmet_rdma_put_rsp(struct nvmet_rdma_rsp *rsp)
{
- unsigned long flags;
-
- if (unlikely(rsp->allocated)) {
+ if (unlikely(rsp->tag == NVMET_RDMA_DISCRETE_RSP_TAG)) {
nvmet_rdma_free_rsp(rsp->queue->dev, rsp);
kfree(rsp);
return;
}
- spin_lock_irqsave(&rsp->queue->rsps_lock, flags);
- list_add_tail(&rsp->free_list, &rsp->queue->free_rsps);
- spin_unlock_irqrestore(&rsp->queue->rsps_lock, flags);
+ sbitmap_clear_bit(&rsp->queue->rsp_tags, rsp->tag);
}
static void nvmet_rdma_free_inline_pages(struct nvmet_rdma_device *ndev,
@@ -403,7 +399,7 @@ static void nvmet_rdma_free_cmds(struct nvmet_rdma_device *ndev,
}
static int nvmet_rdma_alloc_rsp(struct nvmet_rdma_device *ndev,
- struct nvmet_rdma_rsp *r)
+ struct nvmet_rdma_rsp *r, int tag)
{
/* NVMe CQE / RDMA SEND */
r->req.cqe = kmalloc(sizeof(*r->req.cqe), GFP_KERNEL);
@@ -431,6 +427,7 @@ static int nvmet_rdma_alloc_rsp(struct nvmet_rdma_device *ndev,
r->read_cqe.done = nvmet_rdma_read_data_done;
/* Data Out / RDMA WRITE */
r->write_cqe.done = nvmet_rdma_write_data_done;
+ r->tag = tag;
return 0;
@@ -453,21 +450,23 @@ nvmet_rdma_alloc_rsps(struct nvmet_rdma_queue *queue)
{
struct nvmet_rdma_device *ndev = queue->dev;
int nr_rsps = queue->recv_queue_size * 2;
- int ret = -EINVAL, i;
+ int ret = -ENOMEM, i;
+
+ if (sbitmap_init_node(&queue->rsp_tags, nr_rsps, -1, GFP_KERNEL,
+ NUMA_NO_NODE, false, true))
+ goto out;
queue->rsps = kcalloc(nr_rsps, sizeof(struct nvmet_rdma_rsp),
GFP_KERNEL);
if (!queue->rsps)
- goto out;
+ goto out_free_sbitmap;
for (i = 0; i < nr_rsps; i++) {
struct nvmet_rdma_rsp *rsp = &queue->rsps[i];
- ret = nvmet_rdma_alloc_rsp(ndev, rsp);
+ ret = nvmet_rdma_alloc_rsp(ndev, rsp, i);
if (ret)
goto out_free;
-
- list_add_tail(&rsp->free_list, &queue->free_rsps);
}
return 0;
@@ -476,6 +475,8 @@ nvmet_rdma_alloc_rsps(struct nvmet_rdma_queue *queue)
while (--i >= 0)
nvmet_rdma_free_rsp(ndev, &queue->rsps[i]);
kfree(queue->rsps);
+out_free_sbitmap:
+ sbitmap_free(&queue->rsp_tags);
out:
return ret;
}
@@ -488,6 +489,7 @@ static void nvmet_rdma_free_rsps(struct nvmet_rdma_queue *queue)
for (i = 0; i < nr_rsps; i++)
nvmet_rdma_free_rsp(ndev, &queue->rsps[i]);
kfree(queue->rsps);
+ sbitmap_free(&queue->rsp_tags);
}
static int nvmet_rdma_post_recv(struct nvmet_rdma_device *ndev,
@@ -657,18 +659,25 @@ static void nvmet_rdma_rw_ctx_destroy(struct nvmet_rdma_rsp *rsp)
req->sg, req->sg_cnt, nvmet_data_dir(req));
}
-static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
+static void nvmet_rdma_free_rsp_resources(struct nvmet_rdma_rsp *rsp)
{
struct nvmet_rdma_queue *queue = rsp->queue;
- atomic_add(1 + rsp->n_rdma, &queue->sq_wr_avail);
-
if (rsp->n_rdma)
nvmet_rdma_rw_ctx_destroy(rsp);
if (rsp->req.sg < rsp->cmd->inline_sg ||
rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count)
nvmet_req_free_sgls(&rsp->req);
+}
+
+static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
+{
+ struct nvmet_rdma_queue *queue = rsp->queue;
+
+ atomic_add(1 + rsp->n_rdma, &queue->sq_wr_avail);
+
+ nvmet_rdma_free_rsp_resources(rsp);
if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list)))
nvmet_rdma_process_wr_wait_list(queue);
@@ -1340,9 +1349,27 @@ static int nvmet_rdma_create_queue_ib(struct nvmet_rdma_queue *queue)
goto out;
}
+static bool nvmet_rdma_reclaim_rsp(struct sbitmap *sb, unsigned int bitnr,
+ void *data)
+{
+ struct nvmet_rdma_queue *queue = data;
+
+ nvmet_rdma_free_rsp_resources(&queue->rsps[bitnr]);
+
+ return true;
+}
+
static void nvmet_rdma_destroy_queue_ib(struct nvmet_rdma_queue *queue)
{
ib_drain_qp(queue->qp);
+
+ /*
+ * Reclaim resources of a response that is still in-flight when the
+ * queue is being torn down. This happens when the connection was
+ * forcefully disconnected while an I/O is in flight.
+ */
+ sbitmap_for_each_set(&queue->rsp_tags, nvmet_rdma_reclaim_rsp, queue);
+
if (queue->cm_id)
rdma_destroy_id(queue->cm_id);
ib_destroy_qp(queue->qp);
@@ -1462,8 +1489,6 @@ nvmet_rdma_alloc_queue(struct nvmet_rdma_device *ndev,
INIT_LIST_HEAD(&queue->rsp_wait_list);
INIT_LIST_HEAD(&queue->rsp_wr_wait_list);
spin_lock_init(&queue->rsp_wr_wait_lock);
- INIT_LIST_HEAD(&queue->free_rsps);
- spin_lock_init(&queue->rsps_lock);
INIT_LIST_HEAD(&queue->queue_list);
queue->idx = ida_alloc(&nvmet_rdma_queue_ida, GFP_KERNEL);
@@ -1590,19 +1615,26 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
}
if (queue->host_qid == 0) {
- /* Let inflight controller teardown complete */
- flush_workqueue(nvmet_wq);
+ struct nvmet_rdma_queue *q;
+ int pending = 0;
+
+ /* Check for pending controller teardown */
+ mutex_lock(&nvmet_rdma_queue_mutex);
+ list_for_each_entry(q, &nvmet_rdma_queue_list, queue_list) {
+ if (q->nvme_sq.ctrl == queue->nvme_sq.ctrl &&
+ q->state == NVMET_RDMA_Q_DISCONNECTING)
+ pending++;
+ }
+ mutex_unlock(&nvmet_rdma_queue_mutex);
+ if (pending > NVMET_RDMA_BACKLOG) {
+ ret = NVME_SC_CONNECT_CTRL_BUSY;
+ goto free_queue;
+ }
}
ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);
- if (ret) {
- /*
- * Don't destroy the cm_id in free path, as we implicitly
- * destroy the cm_id here with non-zero ret code.
- */
- queue->cm_id = NULL;
+ if (ret)
goto free_queue;
- }
mutex_lock(&nvmet_rdma_queue_mutex);
list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list);
@@ -1611,6 +1643,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
return 0;
free_queue:
+ /*
+ * Don't destroy the cm_id in free path, as we implicitly
+ * destroy the cm_id here with non-zero ret code.
+ */
+ queue->cm_id = NULL;
nvmet_rdma_free_queue(queue);
put_device:
kref_put(&ndev->ref, nvmet_rdma_free_dev);
@@ -1887,7 +1924,7 @@ static int nvmet_rdma_enable_port(struct nvmet_rdma_port *port)
goto out_destroy_id;
}
- ret = rdma_listen(cm_id, 128);
+ ret = rdma_listen(cm_id, NVMET_RDMA_BACKLOG);
if (ret) {
pr_err("listening to %pISpcs failed (%d)\n", addr, ret);
goto out_destroy_id;
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 9ac557945168..15b2e05d1319 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -88,6 +88,7 @@ enum nvmet_tcp_recv_state {
enum {
NVMET_TCP_F_INIT_FAILED = (1 << 0),
+ NVMET_TCP_F_R2T_SENT = (1 << 1),
};
struct nvmet_tcp_cmd {
@@ -761,6 +762,7 @@ static int nvmet_try_send_r2t(struct nvmet_tcp_cmd *cmd, bool last_in_batch)
return -EAGAIN;
cmd->queue->snd_cmd = NULL;
+ cmd->flags |= NVMET_TCP_F_R2T_SENT;
return 1;
}
@@ -1036,6 +1038,12 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
cmd = &queue->connect;
}
+ if (unlikely(!(cmd->flags & NVMET_TCP_F_R2T_SENT))) {
+ pr_err("queue %d: unsolicited H2CData (ttag %u)\n",
+ queue->idx, data->ttag);
+ goto err_proto;
+ }
+
if (le32_to_cpu(data->data_offset) != cmd->rbytes_done) {
pr_err("ttag %u unexpected data offset %u (expected %u)\n",
data->ttag, le32_to_cpu(data->data_offset),
@@ -1231,6 +1239,8 @@ static int nvmet_tcp_try_recv_pdu(struct nvmet_tcp_queue *queue)
}
queue->left = hdr->hlen - queue->offset + hdgst;
+ if (queue->left > sizeof(queue->pdu) - queue->offset)
+ return -EPROTO;
goto recv;
}
diff --git a/drivers/nvme/target/zns.c b/drivers/nvme/target/zns.c
index b7c88c869b51..b7f48a24cc6b 100644
--- a/drivers/nvme/target/zns.c
+++ b/drivers/nvme/target/zns.c
@@ -124,7 +124,7 @@ void nvmet_execute_identify_ns_zns(struct nvmet_req *req)
mutex_unlock(&req->ns->subsys->lock);
}
- if (!bdev_is_zoned(req->ns->bdev)) {
+ if (!req->ns->bdev || !bdev_is_zoned(req->ns->bdev)) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc = offsetof(struct nvme_identify, nsid);
goto out;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 04c6a3b40429..ca090c1c5425 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1784,7 +1784,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
/* walk the alias backwards to extract the id and work out
* the 'stem' string */
- while (isdigit(*(end-1)) && end > start)
+ while (end > start && isdigit(*(end - 1)))
end--;
len = end - start;
diff --git a/drivers/of/property.c b/drivers/of/property.c
index b3f0285e401c..c0e551e6eae9 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -631,6 +631,70 @@ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
}
EXPORT_SYMBOL(of_graph_get_port_by_id);
+/**
+ * of_graph_get_next_port() - get next port node.
+ * @parent: pointer to the parent device node, or parent ports node
+ * @prev: previous port node, or NULL to get first
+ *
+ * Parent device node can be used as @parent whether device node has ports node
+ * or not. It will work same as ports@0 node.
+ *
+ * Return: A 'port' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *node __free(device_node) =
+ of_get_child_by_name(parent, "ports");
+
+ if (node)
+ parent = node;
+
+ return of_get_child_by_name(parent, "port");
+ }
+
+ do {
+ prev = of_get_next_child(parent, prev);
+ if (!prev)
+ break;
+ } while (!of_node_name_eq(prev, "port"));
+
+ return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port);
+
+/**
+ * of_graph_get_next_port_endpoint() - get next endpoint node in port.
+ * If it reached to end of the port, it will return NULL.
+ * @port: pointer to the target port node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port,
+ struct device_node *prev)
+{
+ while (1) {
+ prev = of_get_next_child(port, prev);
+ if (!prev)
+ break;
+ if (WARN(!of_node_name_eq(prev, "endpoint"),
+ "non endpoint node is used (%pOF)", prev))
+ continue;
+
+ break;
+ }
+
+ return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port_endpoint);
+
/**
* of_graph_get_next_endpoint() - get next endpoint node
* @parent: pointer to the parent device node
@@ -813,10 +877,16 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
}
EXPORT_SYMBOL(of_graph_get_remote_port);
-int of_graph_get_endpoint_count(const struct device_node *np)
+/**
+ * of_graph_get_endpoint_count() - get the number of endpoints in a device node
+ * @np: parent device node containing ports and endpoints
+ *
+ * Return: count of endpoint of this device node
+ */
+unsigned int of_graph_get_endpoint_count(const struct device_node *np)
{
struct device_node *endpoint;
- int num = 0;
+ unsigned int num = 0;
for_each_endpoint_of_node(np, endpoint)
num++;
@@ -825,6 +895,23 @@ int of_graph_get_endpoint_count(const struct device_node *np)
}
EXPORT_SYMBOL(of_graph_get_endpoint_count);
+/**
+ * of_graph_get_port_count() - get the number of port in a device or ports node
+ * @np: pointer to the device or ports node
+ *
+ * Return: count of port of this device or ports node
+ */
+unsigned int of_graph_get_port_count(struct device_node *np)
+{
+ unsigned int num = 0;
+
+ for_each_of_graph_port(np, port)
+ num++;
+
+ return num;
+}
+EXPORT_SYMBOL(of_graph_get_port_count);
+
/**
* of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
* @node: pointer to parent device_node containing graph port/endpoint
diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c
index 9eab974e6baf..b5b5413bfd1e 100644
--- a/drivers/parisc/eisa.c
+++ b/drivers/parisc/eisa.c
@@ -442,6 +442,11 @@ static int __init eisa_irq_setup(char *str)
val = (int) simple_strtoul(cur, &pe, 0);
if (val > 15 || val < 0) {
printk(KERN_ERR "eisa: EISA irq value are 0-15\n");
+ cur = strchr(cur, ',');
+ if (cur)
+ cur++;
+ else
+ break;
continue;
}
if (val == 2) {
diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c
index 753a4c615781..1db430b00890 100644
--- a/drivers/pci/controller/cadence/pci-j721e.c
+++ b/drivers/pci/controller/cadence/pci-j721e.c
@@ -374,14 +374,14 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = {
.quirk_detect_quiet_flag = true,
.linkdown_irq_regfield = J7200_LINK_DOWN,
.byte_access_allowed = true,
- .max_lanes = 2,
+ .max_lanes = 4,
};
static const struct j721e_pcie_data j7200_pcie_ep_data = {
.mode = PCI_MODE_EP,
.quirk_detect_quiet_flag = true,
.quirk_disable_flr = true,
- .max_lanes = 2,
+ .max_lanes = 4,
};
static const struct j721e_pcie_data am64_pcie_rc_data = {
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 282f218e8b68..dbdb04aac4be 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -422,7 +422,7 @@ static int meson_pcie_probe(struct platform_device *pdev)
return PTR_ERR(mp->phy);
}
- mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(mp->reset_gpio)) {
dev_err(dev, "get reset gpio failed\n");
return PTR_ERR(mp->reset_gpio);
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index e379ed9b5d2e..94ee9d19a48b 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -2058,6 +2058,7 @@ static struct irq_chip hv_msi_irq_chip = {
.name = "Hyper-V PCIe MSI",
.irq_compose_msi_msg = hv_compose_msi_msg,
.irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
#ifdef CONFIG_X86
.irq_ack = irq_chip_ack_parent,
#elif defined(CONFIG_ARM64)
diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c
index 887b4941ff32..4c14911170a4 100644
--- a/drivers/pci/controller/pci-xgene.c
+++ b/drivers/pci/controller/pci-xgene.c
@@ -53,13 +53,11 @@
#define XGENE_V1_PCI_EXP_CAP 0x40
/* PCIe IP version */
-#define XGENE_PCIE_IP_VER_UNKN 0
#define XGENE_PCIE_IP_VER_1 1
#define XGENE_PCIE_IP_VER_2 2
#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
struct xgene_pcie {
- struct device_node *node;
struct device *dev;
struct clk *clk;
void __iomem *csr_base;
@@ -528,7 +526,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie *port,
static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie *port)
{
- struct device_node *np = port->node;
+ struct device_node *np = port->dev->of_node;
struct of_pci_range range;
struct of_pci_range_parser parser;
struct device *dev = port->dev;
@@ -596,7 +594,6 @@ static struct pci_ops xgene_pcie_ops = {
static int xgene_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node;
struct xgene_pcie *port;
struct pci_host_bridge *bridge;
int ret;
@@ -607,12 +604,8 @@ static int xgene_pcie_probe(struct platform_device *pdev)
port = pci_host_bridge_priv(bridge);
- port->node = of_node_get(dn);
port->dev = dev;
-
- port->version = XGENE_PCIE_IP_VER_UNKN;
- if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
- port->version = XGENE_PCIE_IP_VER_1;
+ port->version = XGENE_PCIE_IP_VER_1;
ret = xgene_pcie_map_reg(port, pdev);
if (ret)
diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c
index b638731aa5ff..61d7ac733ec0 100644
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -858,6 +858,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
{
struct msi_desc *entry;
bool write_msg;
+ u16 cmd;
if (!dev->msix_enabled)
return;
@@ -867,6 +868,14 @@ void __pci_restore_msix_state(struct pci_dev *dev)
pci_msix_clear_and_set_ctrl(dev, 0,
PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
+ /*
+ * The restored device state may not have Memory Space enabled.
+ * Since the MSI-X Table and PBA are in Memory Space, enable it
+ * while restoring them.
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
write_msg = arch_restore_msi_irqs(dev);
msi_lock_descs(&dev->dev);
@@ -877,6 +886,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
}
msi_unlock_descs(&dev->dev);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 3cd05547853a..f6217872e10f 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -728,7 +728,7 @@ static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
- if (off > size)
+ if (off >= size)
return 0;
if (off + count > size) {
size -= off;
@@ -809,7 +809,7 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
}
- if (off > dev->cfg_size)
+ if (off >= dev->cfg_size)
return 0;
if (off + count > dev->cfg_size) {
size = dev->cfg_size - off;
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index f967709082d6..ac3c105fcf8f 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -14,6 +14,8 @@
#include <linux/capability.h>
#include <linux/uaccess.h>
#include <linux/security.h>
+#include <linux/panic.h>
+#include <linux/sched.h>
#include <asm/byteorder.h>
#include "pci.h"
@@ -38,13 +40,16 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
* undefined locations (think of Intel PIIX4 as a typical example).
*/
- if (capable(CAP_SYS_ADMIN))
+ if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN))
size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
size = 64;
+ if (!nbytes)
+ return 0;
+
if (pos >= size)
return 0;
if (nbytes >= size)
@@ -121,6 +126,15 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
if (ret)
return ret;
+ if (!nbytes)
+ return 0;
+
+ if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
+ pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
+ current->comm, pos);
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+ }
+
if (pos >= size)
return 0;
if (nbytes >= size)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 28fa0a9b18c8..0068ca7cdf36 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -5325,7 +5325,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
if (!(mpc & INTEL_MPC_REG_IRBNCE)) {
pci_info(dev, "Enabling MPC IRBNCE\n");
mpc |= INTEL_MPC_REG_IRBNCE;
- pci_write_config_word(dev, INTEL_MPC_REG, mpc);
+ pci_write_config_dword(dev, INTEL_MPC_REG, mpc);
}
}
@@ -6203,6 +6203,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,
pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2608,
+ pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2608,
+ pci_fixup_pericom_acs_store_forward);
static void nvidia_ion_ahci_fixup(struct pci_dev *pdev)
{
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
index 5164078c06d2..1966de0af58f 100644
--- a/drivers/perf/arm_spe_pmu.c
+++ b/drivers/perf/arm_spe_pmu.c
@@ -489,7 +489,7 @@ static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle)
* the page boundary following it. Keep the tail boundary if
* that's lower.
*/
- if (handle->wakeup < (handle->head + handle->size) && head <= wakeup)
+ if ((handle->wakeup - handle->head) < handle->size && head <= wakeup)
limit = min(limit, round_up(wakeup, PAGE_SIZE));
if (limit > head)
diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
index 3e1a4f900611..2455af65ae27 100644
--- a/drivers/perf/cxl_pmu.c
+++ b/drivers/perf/cxl_pmu.c
@@ -643,7 +643,7 @@ static void cxl_pmu_event_start(struct perf_event *event, int flags)
cfg = cxl_pmu_config2_get_hdm_decoder(event);
else
cfg = GENMASK(31, 0); /* No filtering if 0xFFFF_FFFF */
- writeq(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
+ writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
}
cfg = readq(base + CXL_PMU_COUNTER_CFG_REG(hwc->idx));
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 54fb5fca1c42..0c7d383c79f7 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -1257,7 +1257,7 @@ static const u8 qmp_dp_v4_pre_emphasis_hbr3_hbr2[4][4] = {
static const u8 qmp_dp_v4_pre_emphasis_hbr_rbr[4][4] = {
{ 0x00, 0x0d, 0x14, 0x1a },
{ 0x00, 0x0e, 0x15, 0xff },
- { 0x00, 0x0d, 0xff, 0xff },
+ { 0x00, 0x0e, 0xff, 0xff },
{ 0x03, 0xff, 0xff, 0xff }
};
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c
index 65d2923f83b3..bfea335d554b 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c
@@ -563,6 +563,8 @@ struct qmp_usb {
enum phy_mode mode;
+ bool phy_initialized;
+
struct phy *phy;
struct clk_fixed_rate pipe_clk_fixed;
@@ -928,6 +930,7 @@ static int qmp_usb_legacy_power_off(struct phy *phy)
static int qmp_usb_legacy_enable(struct phy *phy)
{
+ struct qmp_usb *qmp = phy_get_drvdata(phy);
int ret;
ret = qmp_usb_legacy_init(phy);
@@ -937,14 +940,19 @@ static int qmp_usb_legacy_enable(struct phy *phy)
ret = qmp_usb_legacy_power_on(phy);
if (ret)
qmp_usb_legacy_exit(phy);
+ else
+ qmp->phy_initialized = true;
return ret;
}
static int qmp_usb_legacy_disable(struct phy *phy)
{
+ struct qmp_usb *qmp = phy_get_drvdata(phy);
int ret;
+ qmp->phy_initialized = false;
+
ret = qmp_usb_legacy_power_off(phy);
if (ret)
return ret;
@@ -1021,7 +1029,7 @@ static int __maybe_unused qmp_usb_legacy_runtime_suspend(struct device *dev)
dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
- if (!qmp->phy->init_count) {
+ if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
@@ -1042,7 +1050,7 @@ static int __maybe_unused qmp_usb_legacy_runtime_resume(struct device *dev)
dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
- if (!qmp->phy->init_count) {
+ if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
index 5072bca0af7c..0b6d54e9b29d 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
@@ -1219,6 +1219,8 @@ struct qmp_usb {
enum phy_mode mode;
+ bool phy_initialized;
+
struct phy *phy;
struct clk_fixed_rate pipe_clk_fixed;
@@ -1788,6 +1790,7 @@ static int qmp_usb_power_off(struct phy *phy)
static int qmp_usb_enable(struct phy *phy)
{
+ struct qmp_usb *qmp = phy_get_drvdata(phy);
int ret;
ret = qmp_usb_init(phy);
@@ -1797,14 +1800,19 @@ static int qmp_usb_enable(struct phy *phy)
ret = qmp_usb_power_on(phy);
if (ret)
qmp_usb_exit(phy);
+ else
+ qmp->phy_initialized = true;
return ret;
}
static int qmp_usb_disable(struct phy *phy)
{
+ struct qmp_usb *qmp = phy_get_drvdata(phy);
int ret;
+ qmp->phy_initialized = false;
+
ret = qmp_usb_power_off(phy);
if (ret)
return ret;
@@ -1881,7 +1889,7 @@ static int __maybe_unused qmp_usb_runtime_suspend(struct device *dev)
dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
- if (!qmp->phy->init_count) {
+ if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
@@ -1902,7 +1910,7 @@ static int __maybe_unused qmp_usb_runtime_resume(struct device *dev)
dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
- if (!qmp->phy->init_count) {
+ if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
index 03dc753f0de1..9a2a6d0c1542 100644
--- a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
+++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
@@ -10,6 +10,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#define QSERDES_QMP_PLL 0x0
#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 (QSERDES_QMP_PLL + 0x1ac)
@@ -104,7 +105,15 @@
#define QSERDES_PCS_SGMIIPHY_READY BIT(7)
#define QSERDES_COM_C_PLL_LOCKED BIT(1)
+static const struct regulator_bulk_data qcom_dwmac_sgmii_phy_vregs[] = {
+ { .supply = "vdda-0p9", .init_load_uA = 46000 },
+ { .supply = "vdda-1p2", .init_load_uA = 15000 },
+};
+
+#define QCOM_SGMII_NUM_SUPPLIES ARRAY_SIZE(qcom_dwmac_sgmii_phy_vregs)
+
struct qcom_dwmac_sgmii_phy_data {
+ struct regulator_bulk_data *vregs;
struct regmap *regmap;
struct clk *refclk;
int speed;
@@ -346,8 +355,28 @@ static int qcom_dwmac_sgmii_phy_calibrate(struct phy *phy)
static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy)
{
struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regulator_bulk_enable(QCOM_SGMII_NUM_SUPPLIES, data->vregs);
+ if (ret)
+ return ret;
- return clk_prepare_enable(data->refclk);
+ ret = clk_prepare_enable(data->refclk);
+ if (ret)
+ goto err_disable_regulators;
+
+ ret = qcom_dwmac_sgmii_phy_calibrate(phy);
+ if (ret)
+ goto err_disable_clk;
+
+ return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(data->refclk);
+err_disable_regulators:
+ regulator_bulk_disable(QCOM_SGMII_NUM_SUPPLIES, data->vregs);
+
+ return ret;
}
static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy)
@@ -362,6 +391,8 @@ static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy)
clk_disable_unprepare(data->refclk);
+ regulator_bulk_disable(QCOM_SGMII_NUM_SUPPLIES, data->vregs);
+
return 0;
}
@@ -372,6 +403,9 @@ static int qcom_dwmac_sgmii_phy_set_speed(struct phy *phy, int speed)
if (speed != data->speed)
data->speed = speed;
+ if (phy->power_count == 0)
+ return 0;
+
return qcom_dwmac_sgmii_phy_calibrate(phy);
}
@@ -398,6 +432,7 @@ static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev)
struct phy_provider *provider;
void __iomem *base;
struct phy *phy;
+ int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -422,6 +457,12 @@ static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev)
if (IS_ERR(data->refclk))
return PTR_ERR(data->refclk);
+ ret = devm_regulator_bulk_get_const(dev, QCOM_SGMII_NUM_SUPPLIES,
+ qcom_dwmac_sgmii_phy_vregs,
+ &data->vregs);
+ if (ret)
+ return ret;
+
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
index eb0b0f61d98e..980ad1fb1e2e 100644
--- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
+++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
@@ -599,8 +599,18 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret,
"failed to get regulator supplies\n");
+ /*
+ * Enable runtime PM before creating the PHY, phy_create() only enables
+ * it on the PHY device if already enabled on the parent. Hold a usage
+ * reference so callbacks cannot run before the PHY is ready.
+ */
+ pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
/*
* Prevent runtime pm from being ON by default. Users can enable
* it using power/control in sysfs.
@@ -611,6 +621,7 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
if (IS_ERR(generic_phy)) {
ret = PTR_ERR(generic_phy);
dev_err(dev, "failed to create phy, %d\n", ret);
+ pm_runtime_put_noidle(dev);
return ret;
}
hsphy->phy = generic_phy;
@@ -620,12 +631,15 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
qcom_snps_hsphy_read_override_param_seq(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (!IS_ERR(phy_provider))
- dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
- else
- pm_runtime_disable(dev);
+ if (IS_ERR(phy_provider)) {
+ pm_runtime_put_noidle(dev);
+ return PTR_ERR(phy_provider);
+ }
- return PTR_ERR_OR_ZERO(phy_provider);
+ dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
+ pm_runtime_put(dev);
+
+ return 0;
}
static struct platform_driver qcom_snps_hsphy_driver = {
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
index 279e19e7546b..2c1f8e3645df 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
@@ -157,7 +157,7 @@ static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = {
{ 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e},
{ 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e},
{1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37},
- {2199, 0x3c}, {2399, 0x41}, {2499, 0x46}
+ {2199, 0x3c}, {2399, 0x41}, {2500, 0x46}
};
static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = {
diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
index 037a9e0263cd..6dcd6d4b904f 100644
--- a/drivers/phy/starfive/phy-jh7110-dphy-rx.c
+++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
@@ -155,6 +155,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct stf_dphy *dphy;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
@@ -195,7 +196,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
return PTR_ERR(dphy->phy);
}
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c
index 637a5fbae6d9..0ad4c7160d17 100644
--- a/drivers/phy/sunplus/phy-sunplus-usb2.c
+++ b/drivers/phy/sunplus/phy-sunplus-usb2.c
@@ -116,11 +116,11 @@ static int sp_uphy_init(struct phy *phy)
ret = clk_prepare_enable(usbphy->phy_clk);
if (ret)
- goto err_clk;
+ return ret;
ret = reset_control_deassert(usbphy->rstc);
if (ret)
- goto err_reset;
+ goto err_clk;
/* Default value modification */
writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0);
@@ -129,7 +129,7 @@ static int sp_uphy_init(struct phy *phy)
/* disconnect voltage */
ret = update_disc_vol(usbphy);
if (ret < 0)
- return ret;
+ goto err_reset;
/* board uphy 0 internal register modification for tid certification */
val = readl(usbphy->phy_regs + CONFIG9);
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 1489191a213f..8f3470d576e4 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -1280,7 +1280,6 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
pc->pctl_desc = *pdata->pctl_desc;
pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
if (IS_ERR(pc->pctl_dev)) {
- gpiochip_remove(&pc->gpio_chip);
return PTR_ERR(pc->pctl_dev);
}
diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c
index 27f0a54e12bf..4c4ee1d3a3a9 100644
--- a/drivers/pinctrl/mediatek/mtk-eint.c
+++ b/drivers/pinctrl/mediatek/mtk-eint.c
@@ -1,17 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2014-2018 MediaTek Inc.
+// Copyright (c) 2014-2025 MediaTek Inc.
/*
* Library for MediaTek External Interrupt Support
*
* Author: Maoguang Meng <maoguang.meng@mediatek.com>
* Sean Wang <sean.wang@mediatek.com>
+ * Hao Chang <ot_chhao.chang@mediatek.com>
+ * Qingliang Li <qingliang.li@mediatek.com>
*
*/
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
@@ -68,13 +72,11 @@ static void __iomem *mtk_eint_get_offset(struct mtk_eint *eint,
unsigned int eint_num,
unsigned int offset)
{
- unsigned int eint_base = 0;
+ unsigned int idx = eint->pins[eint_num].index;
+ unsigned int inst = eint->pins[eint_num].instance;
void __iomem *reg;
- if (eint_num >= eint->hw->ap_num)
- eint_base = eint->hw->ap_num;
-
- reg = eint->base + offset + ((eint_num - eint_base) / 32) * 4;
+ reg = eint->base[inst] + offset + (idx / 32 * 4);
return reg;
}
@@ -83,7 +85,7 @@ static unsigned int mtk_eint_can_en_debounce(struct mtk_eint *eint,
unsigned int eint_num)
{
unsigned int sens;
- unsigned int bit = BIT(eint_num % 32);
+ unsigned int bit = BIT(eint->pins[eint_num].index % 32);
void __iomem *reg = mtk_eint_get_offset(eint, eint_num,
eint->regs->sens);
@@ -92,7 +94,7 @@ static unsigned int mtk_eint_can_en_debounce(struct mtk_eint *eint,
else
sens = MTK_EINT_EDGE_SENSITIVE;
- if (eint_num < eint->hw->db_cnt && sens != MTK_EINT_EDGE_SENSITIVE)
+ if (eint->pins[eint_num].debounce && sens != MTK_EINT_EDGE_SENSITIVE)
return 1;
else
return 0;
@@ -102,9 +104,9 @@ static int mtk_eint_flip_edge(struct mtk_eint *eint, int hwirq)
{
int start_level, curr_level;
unsigned int reg_offset;
- u32 mask = BIT(hwirq & 0x1f);
- u32 port = (hwirq >> 5) & eint->hw->port_mask;
- void __iomem *reg = eint->base + (port << 2);
+ unsigned int mask = BIT(eint->pins[hwirq].index & 0x1f);
+ unsigned int port = (eint->pins[hwirq].index >> 5) & eint->hw->port_mask;
+ void __iomem *reg = eint->base[eint->pins[hwirq].instance] + (port << 2);
curr_level = eint->gpio_xlate->get_gpio_state(eint->pctl, hwirq);
@@ -126,11 +128,13 @@ static int mtk_eint_flip_edge(struct mtk_eint *eint, int hwirq)
static void mtk_eint_mask(struct irq_data *d)
{
struct mtk_eint *eint = irq_data_get_irq_chip_data(d);
- u32 mask = BIT(d->hwirq & 0x1f);
+ unsigned int idx = eint->pins[d->hwirq].index;
+ unsigned int inst = eint->pins[d->hwirq].instance;
+ unsigned int mask = BIT(idx & 0x1f);
void __iomem *reg = mtk_eint_get_offset(eint, d->hwirq,
eint->regs->mask_set);
- eint->cur_mask[d->hwirq >> 5] &= ~mask;
+ eint->cur_mask[inst][idx >> 5] &= ~mask;
writel(mask, reg);
}
@@ -138,22 +142,24 @@ static void mtk_eint_mask(struct irq_data *d)
static void mtk_eint_unmask(struct irq_data *d)
{
struct mtk_eint *eint = irq_data_get_irq_chip_data(d);
- u32 mask = BIT(d->hwirq & 0x1f);
+ unsigned int idx = eint->pins[d->hwirq].index;
+ unsigned int inst = eint->pins[d->hwirq].instance;
+ unsigned int mask = BIT(idx & 0x1f);
void __iomem *reg = mtk_eint_get_offset(eint, d->hwirq,
eint->regs->mask_clr);
- eint->cur_mask[d->hwirq >> 5] |= mask;
+ eint->cur_mask[inst][idx >> 5] |= mask;
writel(mask, reg);
- if (eint->dual_edge[d->hwirq])
+ if (eint->pins[d->hwirq].dual_edge)
mtk_eint_flip_edge(eint, d->hwirq);
}
static unsigned int mtk_eint_get_mask(struct mtk_eint *eint,
unsigned int eint_num)
{
- unsigned int bit = BIT(eint_num % 32);
+ unsigned int bit = BIT(eint->pins[eint_num].index % 32);
void __iomem *reg = mtk_eint_get_offset(eint, eint_num,
eint->regs->mask);
@@ -163,7 +169,7 @@ static unsigned int mtk_eint_get_mask(struct mtk_eint *eint,
static void mtk_eint_ack(struct irq_data *d)
{
struct mtk_eint *eint = irq_data_get_irq_chip_data(d);
- u32 mask = BIT(d->hwirq & 0x1f);
+ unsigned int mask = BIT(eint->pins[d->hwirq].index & 0x1f);
void __iomem *reg = mtk_eint_get_offset(eint, d->hwirq,
eint->regs->ack);
@@ -174,7 +180,7 @@ static int mtk_eint_set_type(struct irq_data *d, unsigned int type)
{
struct mtk_eint *eint = irq_data_get_irq_chip_data(d);
bool masked;
- u32 mask = BIT(d->hwirq & 0x1f);
+ unsigned int mask = BIT(eint->pins[d->hwirq].index & 0x1f);
void __iomem *reg;
if (((type & IRQ_TYPE_EDGE_BOTH) && (type & IRQ_TYPE_LEVEL_MASK)) ||
@@ -186,9 +192,9 @@ static int mtk_eint_set_type(struct irq_data *d, unsigned int type)
}
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- eint->dual_edge[d->hwirq] = 1;
+ eint->pins[d->hwirq].dual_edge = 1;
else
- eint->dual_edge[d->hwirq] = 0;
+ eint->pins[d->hwirq].dual_edge = 0;
if (!mtk_eint_get_mask(eint, d->hwirq)) {
mtk_eint_mask(d);
@@ -223,27 +229,32 @@ static int mtk_eint_set_type(struct irq_data *d, unsigned int type)
static int mtk_eint_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct mtk_eint *eint = irq_data_get_irq_chip_data(d);
- int shift = d->hwirq & 0x1f;
- int reg = d->hwirq >> 5;
+ unsigned int idx = eint->pins[d->hwirq].index;
+ unsigned int inst = eint->pins[d->hwirq].instance;
+ unsigned int shift = idx & 0x1f;
+ unsigned int port = idx >> 5;
if (on)
- eint->wake_mask[reg] |= BIT(shift);
+ eint->wake_mask[inst][port] |= BIT(shift);
else
- eint->wake_mask[reg] &= ~BIT(shift);
+ eint->wake_mask[inst][port] &= ~BIT(shift);
return 0;
}
static void mtk_eint_chip_write_mask(const struct mtk_eint *eint,
- void __iomem *base, u32 *buf)
+ unsigned int **buf)
{
- int port;
+ int inst, port, port_num;
void __iomem *reg;
- for (port = 0; port < eint->hw->ports; port++) {
- reg = base + (port << 2);
- writel_relaxed(~buf[port], reg + eint->regs->mask_set);
- writel_relaxed(buf[port], reg + eint->regs->mask_clr);
+ for (inst = 0; inst < eint->nbase; inst++) {
+ port_num = DIV_ROUND_UP(eint->base_pin_num[inst], 32);
+ for (port = 0; port < port_num; port++) {
+ reg = eint->base[inst] + (port << 2);
+ writel_relaxed(~buf[inst][port], reg + eint->regs->mask_set);
+ writel_relaxed(buf[inst][port], reg + eint->regs->mask_clr);
+ }
}
}
@@ -303,15 +314,18 @@ static struct irq_chip mtk_eint_irq_chip = {
static unsigned int mtk_eint_hw_init(struct mtk_eint *eint)
{
- void __iomem *dom_en = eint->base + eint->regs->dom_en;
- void __iomem *mask_set = eint->base + eint->regs->mask_set;
- unsigned int i;
-
- for (i = 0; i < eint->hw->ap_num; i += 32) {
- writel(0xffffffff, dom_en);
- writel(0xffffffff, mask_set);
- dom_en += 4;
- mask_set += 4;
+ void __iomem *dom_reg, *mask_reg;
+ unsigned int i, j;
+
+ for (i = 0; i < eint->nbase; i++) {
+ dom_reg = eint->base[i] + eint->regs->dom_en;
+ mask_reg = eint->base[i] + eint->regs->mask_set;
+ for (j = 0; j < eint->base_pin_num[i]; j += 32) {
+ writel(0xffffffff, dom_reg);
+ writel(0xffffffff, mask_reg);
+ dom_reg += 4;
+ mask_reg += 4;
+ }
}
return 0;
@@ -322,14 +336,16 @@ mtk_eint_debounce_process(struct mtk_eint *eint, int index)
{
unsigned int rst, ctrl_offset;
unsigned int bit, dbnc;
+ unsigned int inst = eint->pins[index].instance;
+ unsigned int idx = eint->pins[index].index;
- ctrl_offset = (index / 4) * 4 + eint->regs->dbnc_ctrl;
- dbnc = readl(eint->base + ctrl_offset);
- bit = MTK_EINT_DBNC_SET_EN << ((index % 4) * 8);
+ ctrl_offset = (idx / 4) * 4 + eint->regs->dbnc_ctrl;
+ dbnc = readl(eint->base[inst] + ctrl_offset);
+ bit = MTK_EINT_DBNC_SET_EN << ((idx % 4) * 8);
if ((bit & dbnc) > 0) {
- ctrl_offset = (index / 4) * 4 + eint->regs->dbnc_set;
- rst = MTK_EINT_DBNC_RST_BIT << ((index % 4) * 8);
- writel(rst, eint->base + ctrl_offset);
+ ctrl_offset = (idx / 4) * 4 + eint->regs->dbnc_set;
+ rst = MTK_EINT_DBNC_RST_BIT << ((idx % 4) * 8);
+ writel(rst, eint->base[inst] + ctrl_offset);
}
}
@@ -337,65 +353,68 @@ static void mtk_eint_irq_handler(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct mtk_eint *eint = irq_desc_get_handler_data(desc);
- unsigned int status, eint_num;
- int offset, mask_offset, index;
- void __iomem *reg = mtk_eint_get_offset(eint, 0, eint->regs->stat);
+ unsigned int i, j, port, status, shift, mask, eint_num;
+ void __iomem *reg;
int dual_edge, start_level, curr_level;
chained_irq_enter(chip, desc);
- for (eint_num = 0; eint_num < eint->hw->ap_num; eint_num += 32,
- reg += 4) {
- status = readl(reg);
- while (status) {
- offset = __ffs(status);
- mask_offset = eint_num >> 5;
- index = eint_num + offset;
- status &= ~BIT(offset);
-
- /*
- * If we get an interrupt on pin that was only required
- * for wake (but no real interrupt requested), mask the
- * interrupt (as would mtk_eint_resume do anyway later
- * in the resume sequence).
- */
- if (eint->wake_mask[mask_offset] & BIT(offset) &&
- !(eint->cur_mask[mask_offset] & BIT(offset))) {
- writel_relaxed(BIT(offset), reg -
- eint->regs->stat +
- eint->regs->mask_set);
- }
-
- dual_edge = eint->dual_edge[index];
- if (dual_edge) {
- /*
- * Clear soft-irq in case we raised it last
- * time.
- */
- writel(BIT(offset), reg - eint->regs->stat +
- eint->regs->soft_clr);
-
- start_level =
- eint->gpio_xlate->get_gpio_state(eint->pctl,
- index);
- }
-
- generic_handle_domain_irq(eint->domain, index);
-
- if (dual_edge) {
- curr_level = mtk_eint_flip_edge(eint, index);
+ for (i = 0; i < eint->nbase; i++) {
+ for (j = 0; j < eint->base_pin_num[i]; j += 32) {
+ port = j >> 5;
+ status = readl(eint->base[i] + port * 4 + eint->regs->stat);
+ while (status) {
+ shift = __ffs(status);
+ status &= ~BIT(shift);
+ mask = BIT(shift);
+ eint_num = eint->pin_list[i][shift + j];
/*
- * If level changed, we might lost one edge
- * interrupt, raised it through soft-irq.
+ * If we get an interrupt on pin that was only required
+ * for wake (but no real interrupt requested), mask the
+ * interrupt (as would mtk_eint_resume do anyway later
+ * in the resume sequence).
*/
- if (start_level != curr_level)
- writel(BIT(offset), reg -
- eint->regs->stat +
- eint->regs->soft_set);
+ if (eint->wake_mask[i][port] & mask &&
+ !(eint->cur_mask[i][port] & mask)) {
+ reg = mtk_eint_get_offset(eint, eint_num,
+ eint->regs->mask_set);
+ writel_relaxed(mask, reg);
+ }
+
+ dual_edge = eint->pins[eint_num].dual_edge;
+ if (dual_edge) {
+ /*
+ * Clear soft-irq in case we raised it last
+ * time.
+ */
+ reg = mtk_eint_get_offset(eint, eint_num,
+ eint->regs->soft_clr);
+ writel(mask, reg);
+
+ start_level =
+ eint->gpio_xlate->get_gpio_state(eint->pctl,
+ eint_num);
+ }
+
+ generic_handle_domain_irq(eint->domain, eint_num);
+
+ if (dual_edge) {
+ curr_level = mtk_eint_flip_edge(eint, eint_num);
+
+ /*
+ * If level changed, we might lost one edge
+ * interrupt, raised it through soft-irq.
+ */
+ if (start_level != curr_level) {
+ reg = mtk_eint_get_offset(eint, eint_num,
+ eint->regs->soft_set);
+ writel(mask, reg);
+ }
+ }
+
+ if (eint->pins[eint_num].debounce)
+ mtk_eint_debounce_process(eint, eint_num);
}
-
- if (index < eint->hw->db_cnt)
- mtk_eint_debounce_process(eint, index);
}
}
chained_irq_exit(chip, desc);
@@ -403,7 +422,7 @@ static void mtk_eint_irq_handler(struct irq_desc *desc)
int mtk_eint_do_suspend(struct mtk_eint *eint)
{
- mtk_eint_chip_write_mask(eint, eint->base, eint->wake_mask);
+ mtk_eint_chip_write_mask(eint, eint->wake_mask);
return 0;
}
@@ -411,7 +430,7 @@ EXPORT_SYMBOL_GPL(mtk_eint_do_suspend);
int mtk_eint_do_resume(struct mtk_eint *eint)
{
- mtk_eint_chip_write_mask(eint, eint->base, eint->cur_mask);
+ mtk_eint_chip_write_mask(eint, eint->cur_mask);
return 0;
}
@@ -423,17 +442,19 @@ int mtk_eint_set_debounce(struct mtk_eint *eint, unsigned long eint_num,
int virq, eint_offset;
unsigned int set_offset, bit, clr_bit, clr_offset, rst, i, unmask,
dbnc;
+ unsigned int inst = eint->pins[eint_num].instance;
+ unsigned int idx = eint->pins[eint_num].index;
struct irq_data *d;
if (!eint->hw->db_time)
return -EOPNOTSUPP;
virq = irq_find_mapping(eint->domain, eint_num);
- eint_offset = (eint_num % 4) * 8;
+ eint_offset = (idx % 4) * 8;
d = irq_get_irq_data(virq);
- set_offset = (eint_num / 4) * 4 + eint->regs->dbnc_set;
- clr_offset = (eint_num / 4) * 4 + eint->regs->dbnc_clr;
+ set_offset = (idx / 4) * 4 + eint->regs->dbnc_set;
+ clr_offset = (idx / 4) * 4 + eint->regs->dbnc_clr;
if (!mtk_eint_can_en_debounce(eint, eint_num))
return -EINVAL;
@@ -454,12 +475,12 @@ int mtk_eint_set_debounce(struct mtk_eint *eint, unsigned long eint_num,
}
clr_bit = 0xff << eint_offset;
- writel(clr_bit, eint->base + clr_offset);
+ writel(clr_bit, eint->base[inst] + clr_offset);
bit = ((dbnc << MTK_EINT_DBNC_SET_DBNC_BITS) | MTK_EINT_DBNC_SET_EN) <<
eint_offset;
rst = MTK_EINT_DBNC_RST_BIT << eint_offset;
- writel(rst | bit, eint->base + set_offset);
+ writel(rst | bit, eint->base[inst] + set_offset);
/*
* Delay a while (more than 2T) to wait for hw debounce counter reset
@@ -485,34 +506,89 @@ int mtk_eint_find_irq(struct mtk_eint *eint, unsigned long eint_n)
}
EXPORT_SYMBOL_GPL(mtk_eint_find_irq);
-int mtk_eint_do_init(struct mtk_eint *eint)
+static void mtk_eint_teardown(void *data)
+{
+ struct mtk_eint *eint = data;
+ unsigned int i, virq;
+
+ /* Detach the demux handler so it can no longer reference freed data. */
+ irq_set_chained_handler_and_data(eint->irq, NULL, NULL);
+
+ /* Wait for any in-flight handler to finish before tearing down. */
+ synchronize_irq(eint->irq);
+
+ /* Dispose of all child mappings before the domain is removed. */
+ for (i = 0; i < eint->hw->ap_num; i++) {
+ virq = irq_find_mapping(eint->domain, i);
+ if (virq)
+ irq_dispose_mapping(virq);
+ }
+
+ irq_domain_remove(eint->domain);
+}
+
+int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
{
- int i;
+ unsigned int size, i, port, virq, inst = 0;
/* If clients don't assign a specific regs, let's use generic one */
if (!eint->regs)
eint->regs = &mtk_generic_eint_regs;
- eint->wake_mask = devm_kcalloc(eint->dev, eint->hw->ports,
- sizeof(*eint->wake_mask), GFP_KERNEL);
- if (!eint->wake_mask)
+ eint->base_pin_num = devm_kmalloc_array(eint->dev, eint->nbase, sizeof(u16),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!eint->base_pin_num)
return -ENOMEM;
- eint->cur_mask = devm_kcalloc(eint->dev, eint->hw->ports,
- sizeof(*eint->cur_mask), GFP_KERNEL);
- if (!eint->cur_mask)
- return -ENOMEM;
+ if (eint_pin) {
+ eint->pins = eint_pin;
+ for (i = 0; i < eint->hw->ap_num; i++) {
+ inst = eint->pins[i].instance;
+ if (inst >= eint->nbase)
+ continue;
+ eint->base_pin_num[inst]++;
+ }
+ } else {
+ size = eint->hw->ap_num * sizeof(struct mtk_eint_pin);
+ eint->pins = devm_kmalloc(eint->dev, size, GFP_KERNEL);
+ if (!eint->pins)
+ goto err_pins;
+
+ eint->base_pin_num[inst] = eint->hw->ap_num;
+ for (i = 0; i < eint->hw->ap_num; i++) {
+ eint->pins[i].instance = inst;
+ eint->pins[i].index = i;
+ eint->pins[i].debounce = (i < eint->hw->db_cnt) ? 1 : 0;
+ }
+ }
- eint->dual_edge = devm_kcalloc(eint->dev, eint->hw->ap_num,
- sizeof(int), GFP_KERNEL);
- if (!eint->dual_edge)
- return -ENOMEM;
+ eint->pin_list = devm_kmalloc(eint->dev, eint->nbase * sizeof(u16 *), GFP_KERNEL);
+ if (!eint->pin_list)
+ goto err_pin_list;
+
+ eint->wake_mask = devm_kmalloc(eint->dev, eint->nbase * sizeof(u32 *), GFP_KERNEL);
+ if (!eint->wake_mask)
+ goto err_wake_mask;
+
+ eint->cur_mask = devm_kmalloc(eint->dev, eint->nbase * sizeof(u32 *), GFP_KERNEL);
+ if (!eint->cur_mask)
+ goto err_cur_mask;
+
+ for (i = 0; i < eint->nbase; i++) {
+ eint->pin_list[i] = devm_kzalloc(eint->dev, eint->base_pin_num[i] * sizeof(u16),
+ GFP_KERNEL);
+ port = DIV_ROUND_UP(eint->base_pin_num[i], 32);
+ eint->wake_mask[i] = devm_kzalloc(eint->dev, port * sizeof(u32), GFP_KERNEL);
+ eint->cur_mask[i] = devm_kzalloc(eint->dev, port * sizeof(u32), GFP_KERNEL);
+ if (!eint->pin_list[i] || !eint->wake_mask[i] || !eint->cur_mask[i])
+ goto err_eint;
+ }
eint->domain = irq_domain_add_linear(eint->dev->of_node,
eint->hw->ap_num,
&irq_domain_simple_ops, NULL);
if (!eint->domain)
- return -ENOMEM;
+ goto err_eint;
if (eint->hw->db_time) {
for (i = 0; i < MTK_EINT_DBNC_MAX; i++)
@@ -523,8 +599,11 @@ int mtk_eint_do_init(struct mtk_eint *eint)
mtk_eint_hw_init(eint);
for (i = 0; i < eint->hw->ap_num; i++) {
- int virq = irq_create_mapping(eint->domain, i);
-
+ inst = eint->pins[i].instance;
+ if (inst >= eint->nbase)
+ continue;
+ eint->pin_list[inst][eint->pins[i].index] = i;
+ virq = irq_create_mapping(eint->domain, i);
irq_set_chip_and_handler(virq, &mtk_eint_irq_chip,
handle_level_irq);
irq_set_chip_data(virq, eint);
@@ -533,7 +612,28 @@ int mtk_eint_do_init(struct mtk_eint *eint)
irq_set_chained_handler_and_data(eint->irq, mtk_eint_irq_handler,
eint);
- return 0;
+ return devm_add_action_or_reset(eint->dev, mtk_eint_teardown, eint);
+
+err_eint:
+ for (i = 0; i < eint->nbase; i++) {
+ if (eint->cur_mask[i])
+ devm_kfree(eint->dev, eint->cur_mask[i]);
+ if (eint->wake_mask[i])
+ devm_kfree(eint->dev, eint->wake_mask[i]);
+ if (eint->pin_list[i])
+ devm_kfree(eint->dev, eint->pin_list[i]);
+ }
+ devm_kfree(eint->dev, eint->cur_mask);
+err_cur_mask:
+ devm_kfree(eint->dev, eint->wake_mask);
+err_wake_mask:
+ devm_kfree(eint->dev, eint->pin_list);
+err_pin_list:
+ if (!eint_pin)
+ devm_kfree(eint->dev, eint->pins);
+err_pins:
+ devm_kfree(eint->dev, eint->base_pin_num);
+ return -ENOMEM;
}
EXPORT_SYMBOL_GPL(mtk_eint_do_init);
diff --git a/drivers/pinctrl/mediatek/mtk-eint.h b/drivers/pinctrl/mediatek/mtk-eint.h
index 6139b16cd225..fc31a4c0c77b 100644
--- a/drivers/pinctrl/mediatek/mtk-eint.h
+++ b/drivers/pinctrl/mediatek/mtk-eint.h
@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) 2014-2018 MediaTek Inc.
+ * Copyright (C) 2014-2025 MediaTek Inc.
*
* Author: Maoguang Meng <maoguang.meng@mediatek.com>
* Sean Wang <sean.wang@mediatek.com>
- *
+ * Hao Chang <ot_chhao.chang@mediatek.com>
+ * Qingliang Li <qingliang.li@mediatek.com>
*/
#ifndef __MTK_EINT_H
#define __MTK_EINT_H
@@ -40,6 +41,14 @@ struct mtk_eint_hw {
const unsigned int *db_time;
};
+struct mtk_eint_pin {
+ u16 number;
+ u8 instance;
+ u8 index;
+ bool debounce;
+ bool dual_edge;
+};
+
extern const unsigned int debounce_time_mt2701[];
extern const unsigned int debounce_time_mt6765[];
extern const unsigned int debounce_time_mt6795[];
@@ -56,17 +65,21 @@ struct mtk_eint_xt {
struct mtk_eint {
struct device *dev;
- void __iomem *base;
+ void __iomem **base;
+ int nbase;
+ u16 *base_pin_num;
struct irq_domain *domain;
int irq;
int *dual_edge;
- u32 *wake_mask;
- u32 *cur_mask;
+ u16 **pin_list;
+ u32 **wake_mask;
+ u32 **cur_mask;
/* Used to fit into various EINT device */
const struct mtk_eint_hw *hw;
const struct mtk_eint_regs *regs;
+ struct mtk_eint_pin *pins;
u16 num_db_time;
/* Used to fit into various pinctrl device */
@@ -75,7 +88,7 @@ struct mtk_eint {
};
#if IS_ENABLED(CONFIG_EINT_MTK)
-int mtk_eint_do_init(struct mtk_eint *eint);
+int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin);
int mtk_eint_do_suspend(struct mtk_eint *eint);
int mtk_eint_do_resume(struct mtk_eint *eint);
int mtk_eint_set_debounce(struct mtk_eint *eint, unsigned long eint_n,
@@ -83,7 +96,8 @@ int mtk_eint_set_debounce(struct mtk_eint *eint, unsigned long eint_n,
int mtk_eint_find_irq(struct mtk_eint *eint, unsigned long eint_n);
#else
-static inline int mtk_eint_do_init(struct mtk_eint *eint)
+static inline int mtk_eint_do_init(struct mtk_eint *eint,
+ struct mtk_eint_pin *eint_pin)
{
return -EOPNOTSUPP;
}
diff --git a/drivers/pinctrl/mediatek/pinctrl-moore.h b/drivers/pinctrl/mediatek/pinctrl-moore.h
index e1b4b82b9d3d..36ee0021cf7e 100644
--- a/drivers/pinctrl/mediatek/pinctrl-moore.h
+++ b/drivers/pinctrl/mediatek/pinctrl-moore.h
@@ -37,12 +37,12 @@
.funcs = NULL, \
}
-#define PINCTRL_PIN_GROUP(name, id) \
- { \
- name, \
- id##_pins, \
- ARRAY_SIZE(id##_pins), \
- id##_funcs, \
+#define PINCTRL_PIN_GROUP(_name_, id) \
+ { \
+ .name = _name_, \
+ .pins = id##_pins, \
+ .num_pins = ARRAY_SIZE(id##_pins), \
+ .data = id##_funcs, \
}
int mtk_moore_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
index 54301fbba524..882733516b45 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/of_irq.h>
#include "mtk-eint.h"
@@ -367,7 +368,7 @@ static const struct mtk_eint_xt mtk_eint_xt = {
int mtk_build_eint(struct mtk_pinctrl *hw, struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- int ret;
+ int ret, i, j, count_reg_names;
if (!IS_ENABLED(CONFIG_EINT_MTK))
return 0;
@@ -379,10 +380,27 @@ int mtk_build_eint(struct mtk_pinctrl *hw, struct platform_device *pdev)
if (!hw->eint)
return -ENOMEM;
- hw->eint->base = devm_platform_ioremap_resource_byname(pdev, "eint");
- if (IS_ERR(hw->eint->base)) {
- ret = PTR_ERR(hw->eint->base);
- goto err_free_eint;
+ count_reg_names = of_property_count_strings(np, "reg-names");
+ if (count_reg_names < 0)
+ return -EINVAL;
+
+ hw->eint->nbase = count_reg_names - (int)hw->soc->nbase_names;
+ if (hw->eint->nbase <= 0)
+ return -EINVAL;
+
+ hw->eint->base = devm_kmalloc_array(&pdev->dev, hw->eint->nbase,
+ sizeof(*hw->eint->base), GFP_KERNEL | __GFP_ZERO);
+ if (!hw->eint->base) {
+ ret = -ENOMEM;
+ goto err_free_base;
+ }
+
+ for (i = hw->soc->nbase_names, j = 0; i < count_reg_names; i++, j++) {
+ hw->eint->base[j] = of_iomap(np, i);
+ if (IS_ERR(hw->eint->base[j])) {
+ ret = PTR_ERR(hw->eint->base[j]);
+ goto err_free_eint;
+ }
}
hw->eint->irq = irq_of_parse_and_map(np, 0);
@@ -401,9 +419,19 @@ int mtk_build_eint(struct mtk_pinctrl *hw, struct platform_device *pdev)
hw->eint->pctl = hw;
hw->eint->gpio_xlate = &mtk_eint_xt;
- return mtk_eint_do_init(hw->eint);
+ ret = mtk_eint_do_init(hw->eint, hw->soc->eint_pin);
+ if (ret)
+ goto err_free_eint;
+
+ return 0;
err_free_eint:
+ for (j = 0; j < hw->eint->nbase; j++) {
+ if (hw->eint->base[j])
+ iounmap(hw->eint->base[j]);
+ }
+ devm_kfree(hw->dev, hw->eint->base);
+err_free_base:
devm_kfree(hw->dev, hw->eint);
hw->eint = NULL;
return ret;
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
index 23688ca6d04e..1dc2ed8a7df2 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
@@ -241,6 +241,7 @@ struct mtk_pin_soc {
unsigned int nfuncs;
const struct mtk_eint_regs *eint_regs;
const struct mtk_eint_hw *eint_hw;
+ struct mtk_eint_pin *eint_pin;
/* Specific parameters per SoC */
u8 gpio_m;
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index 167df3c7336f..0c3732875e67 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -1024,9 +1024,15 @@ static int mtk_eint_init(struct mtk_pinctrl *pctl, struct platform_device *pdev)
if (!pctl->eint)
return -ENOMEM;
- pctl->eint->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(pctl->eint->base))
- return PTR_ERR(pctl->eint->base);
+ pctl->eint->nbase = 1;
+ /* mtk-eint expects an array */
+ pctl->eint->base = devm_kzalloc(pctl->dev, sizeof(pctl->eint->base), GFP_KERNEL);
+ if (!pctl->eint->base)
+ return -ENOMEM;
+
+ pctl->eint->base[0] = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pctl->eint->base[0]))
+ return PTR_ERR(pctl->eint->base[0]);
pctl->eint->irq = irq_of_parse_and_map(np, 0);
if (!pctl->eint->irq)
@@ -1042,7 +1048,7 @@ static int mtk_eint_init(struct mtk_pinctrl *pctl, struct platform_device *pdev)
pctl->eint->pctl = pctl;
pctl->eint->gpio_xlate = &mtk_eint_xt;
- return mtk_eint_do_init(pctl->eint);
+ return mtk_eint_do_init(pctl->eint, NULL);
}
/* This is used as a common probe function */
diff --git a/drivers/pinctrl/mediatek/pinctrl-paris.h b/drivers/pinctrl/mediatek/pinctrl-paris.h
index 8762ac599329..9c3fbd0fea40 100644
--- a/drivers/pinctrl/mediatek/pinctrl-paris.h
+++ b/drivers/pinctrl/mediatek/pinctrl-paris.h
@@ -49,12 +49,19 @@
__VA_ARGS__, { } }, \
}
-#define PINCTRL_PIN_GROUP(name, id) \
- { \
- name, \
- id##_pins, \
- ARRAY_SIZE(id##_pins), \
- id##_funcs, \
+#define MTK_EINT_PIN(_number, _instance, _index, _debounce) { \
+ .number = _number, \
+ .instance = _instance, \
+ .index = _index, \
+ .debounce = _debounce, \
+ }
+
+#define PINCTRL_PIN_GROUP(_name_, id) \
+ { \
+ .name = _name_, \
+ .pins = id##_pins, \
+ .num_pins = ARRAY_SIZE(id##_pins), \
+ .data = id##_funcs, \
}
int mtk_paris_pinctrl_probe(struct platform_device *pdev);
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 0c2382480c24..f4b7678e7553 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -3240,6 +3240,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
pmu_offs = ctrl->pmu_mux_offset;
drv_pmu_offs = ctrl->pmu_drv_offset;
drv_grf_offs = ctrl->grf_drv_offset;
+
+ /*
+ * This function mutates the static per-SoC data. Most of it is
+ * idempotent: recalculated iomux and drv offsets anchor at the
+ * values calculated by a previous run. The pin count is not, so
+ * reset it here; otherwise it accumulates when the probe runs
+ * again after a probe deferral, shifting every bank's pin_base.
+ */
+ ctrl->nr_pins = 0;
+
bank = ctrl->pin_banks;
for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
int bank_pins = 0;
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index 793c8c4bf35b..f0289839c029 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -521,7 +521,7 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
ret = blocking_notifier_chain_register(&ec->ec_dev->panic_notifier,
&debug_info->notifier_panic);
if (ret)
- goto remove_debugfs;
+ goto cleanup_console_log;
ec->debug_info = debug_info;
@@ -529,6 +529,8 @@ static int cros_ec_debugfs_probe(struct platform_device *pd)
return 0;
+cleanup_console_log:
+ cros_ec_cleanup_console_log(debug_info);
remove_debugfs:
debugfs_remove_recursive(debug_info->dir);
return ret;
@@ -538,6 +540,8 @@ static int cros_ec_debugfs_remove(struct platform_device *pd)
{
struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
+ blocking_notifier_chain_unregister(&ec->ec_dev->panic_notifier,
+ &ec->debug_info->notifier_panic);
debugfs_remove_recursive(ec->debug_info->dir);
cros_ec_cleanup_console_log(ec->debug_info);
diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index 71948dade0e2..5bdeb9860a5b 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -436,6 +436,21 @@ cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub *sensorhub,
fifo_timestamp,
*current_timestamp,
now);
+
+ /*
+ * A standalone timestamp event typically has a sensor_num of
+ * 0xff. Return early here to prevent it from hitting the
+ * bounds check below and spamming the logs.
+ */
+ return false;
+ }
+
+ /* Skip event if sensor_num from EC is out of bounds. */
+ if (in->sensor_num >= sensorhub->sensor_num) {
+ dev_warn_ratelimited(sensorhub->dev,
+ "Invalid sensor number %u from EC\n",
+ in->sensor_num);
+ return false;
}
if (in->flags & MOTIONSENSE_SENSOR_FLAG_ODR) {
@@ -463,10 +478,6 @@ cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub *sensorhub,
return true;
}
- if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
- /* If we just have a timestamp, skip this entry. */
- return false;
-
/* Regular sample */
out->sensor_id = in->sensor_num;
trace_cros_ec_sensorhub_data(in->sensor_num,
@@ -786,8 +797,15 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
sensorhub->msg->outsize = 1;
sensorhub->msg->insize = fifo_info_length;
- if (cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg) < 0)
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
+ if (ret < 0)
goto error;
+ if (ret != fifo_info_length) {
+ dev_warn_ratelimited(sensorhub->dev,
+ "Mismatch read length: size %d - expected %d\n",
+ ret, fifo_info_length);
+ goto error;
+ }
memcpy(fifo_info, &sensorhub->resp->fifo_info,
fifo_info_length);
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 76807ceb313a..65549c3a6748 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -942,6 +942,12 @@ static void cros_typec_register_partner_pdos(struct cros_typec_data *typec,
if (!resp->source_cap_count && !resp->sink_cap_count)
return;
+ if (resp->source_cap_count > PDO_MAX_OBJECTS ||
+ resp->sink_cap_count > PDO_MAX_OBJECTS) {
+ dev_warn(typec->dev, "Invalid PDO count from EC, port: %d\n", port_num);
+ return;
+ }
+
port->partner_pd = typec_partner_usb_power_delivery_register(port->partner, &desc);
if (IS_ERR(port->partner_pd)) {
dev_warn(typec->dev, "Failed to register partner PD device, port: %d\n", port_num);
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
index e278092f889b..2996b5756457 100644
--- a/drivers/platform/mellanox/mlxbf-bootctl.c
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/if_ether.h>
#include <linux/iopoll.h>
diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c
index db7a1d360cd2..25e12b337565 100644
--- a/drivers/platform/mellanox/mlxbf-pmc.c
+++ b/drivers/platform/mellanox/mlxbf-pmc.c
@@ -1385,13 +1385,19 @@ static int mlxbf_pmc_map_counters(struct device *dev)
static int mlxbf_pmc_probe(struct platform_device *pdev)
{
- struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev);
- const char *hid = acpi_device_hid(acpi_dev);
struct device *dev = &pdev->dev;
+ struct acpi_device *acpi_dev;
struct arm_smccc_res res;
+ const char *hid;
guid_t guid;
int ret;
+ acpi_dev = ACPI_COMPANION(&pdev->dev);
+ if (!acpi_dev)
+ return -ENODEV;
+
+ hid = acpi_device_hid(acpi_dev);
+
/* Ensure we have the UUID we expect for this service. */
arm_smccc_smc(MLXBF_PMC_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
guid_parse(mlxbf_pmc_svc_uuid_str, &guid);
diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c
index 897cdd9c3aae..e3bc6f46f248 100644
--- a/drivers/platform/surface/surface_acpi_notify.c
+++ b/drivers/platform/surface/surface_acpi_notify.c
@@ -801,12 +801,16 @@ static int san_consumer_links_setup(struct platform_device *pdev)
static int san_probe(struct platform_device *pdev)
{
- struct acpi_device *san = ACPI_COMPANION(&pdev->dev);
struct ssam_controller *ctrl;
+ struct acpi_device *san;
struct san_data *data;
acpi_status astatus;
int status;
+ san = ACPI_COMPANION(&pdev->dev);
+ if (!san)
+ return -ENODEV;
+
ctrl = ssam_client_bind(&pdev->dev);
if (IS_ERR(ctrl))
return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c
index 01f3ff21c888..713ec2c12a04 100644
--- a/drivers/platform/x86/dell/dell-wmi-base.c
+++ b/drivers/platform/x86/dell/dell-wmi-base.c
@@ -839,9 +839,22 @@ static int __init dell_wmi_init(void)
err = dell_privacy_register_driver();
if (err)
- return err;
+ goto out_smbios;
- return wmi_driver_register(&dell_wmi_driver);
+ err = wmi_driver_register(&dell_wmi_driver);
+ if (err)
+ goto out_privacy;
+
+ return 0;
+
+out_privacy:
+ dell_privacy_unregister_driver();
+
+out_smbios:
+ if (wmi_requires_smbios_request)
+ dell_wmi_events_set_enabled(false);
+
+ return err;
}
late_initcall(dell_wmi_init);
diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c
index c517bd45dd32..3c3865a73226 100644
--- a/drivers/platform/x86/dell/dell-wmi-privacy.c
+++ b/drivers/platform/x86/dell/dell-wmi-privacy.c
@@ -69,11 +69,11 @@ bool dell_privacy_has_mic_mute(void)
{
struct privacy_wmi_data *priv;
- mutex_lock(&list_mutex);
+ guard(mutex)(&list_mutex);
+
priv = list_first_entry_or_null(&wmi_list,
struct privacy_wmi_data,
list);
- mutex_unlock(&list_mutex);
return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO));
}
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c
index c2dd2de6bc20..fea97d6c3bf0 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c
@@ -84,7 +84,6 @@ int set_attribute(const char *a_name, const char *a_value)
if (ret < 0)
goto out;
- print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev,
buffer, buffer_size,
SETATTRIBUTE_METHOD_ID);
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h
index 817ee7ba07ca..b0810a02e6da 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h
@@ -107,7 +107,7 @@ enum {
static int get_##type##_instance_id(struct kobject *kobj) \
{ \
int i; \
- for (i = 0; i <= wmi_priv.type##_instances_count; i++) { \
+ for (i = 0; i < wmi_priv.type##_instances_count; i++) { \
if (!(strcmp(kobj->name, wmi_priv.type##_data[i].attribute_name)))\
return i; \
} \
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index ec7a74bee803..8a29a01eead5 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -85,7 +85,7 @@ int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_
* bytes.
*/
conv_dst_size = size;
- if (size > dst_size)
+ if (size >= dst_size)
conv_dst_size = dst_size - 1;
/*
@@ -666,12 +666,17 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type,
int ret = 0;
/* Take action appropriate to each ACPI TYPE */
- if (obj->package.count < min_elements) {
- pr_err("ACPI-package does not have enough elements: %d < %d\n",
- obj->package.count, min_elements);
+ if (obj->package.count < COMMON_ELEM_CNT) {
+ pr_err("ACPI-package is missing common elements: %d < %d\n",
+ obj->package.count, COMMON_ELEM_CNT);
goto pack_attr_exit;
}
+ if (obj->package.count < min_elements) {
+ pr_warn("ACPI-package has fewer elements than expected: %d < %d, parsing available elements\n",
+ obj->package.count, min_elements);
+ }
+
elements = obj->package.elements;
/* sanity checking */
@@ -736,26 +741,31 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type,
switch (attr_type) {
case HPWMI_STRING_TYPE:
ret = hp_populate_string_package_data(elements,
+ obj->package.count,
instance_id,
attr_name_kobj);
break;
case HPWMI_INTEGER_TYPE:
ret = hp_populate_integer_package_data(elements,
+ obj->package.count,
instance_id,
attr_name_kobj);
break;
case HPWMI_ENUMERATION_TYPE:
ret = hp_populate_enumeration_package_data(elements,
+ obj->package.count,
instance_id,
attr_name_kobj);
break;
case HPWMI_ORDERED_LIST_TYPE:
ret = hp_populate_ordered_list_package_data(elements,
+ obj->package.count,
instance_id,
attr_name_kobj);
break;
case HPWMI_PASSWORD_TYPE:
ret = hp_populate_password_package_data(elements,
+ obj->package.count,
instance_id,
attr_name_kobj);
break;
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
index f1eec0e4ba07..ac57d6eab4c3 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
@@ -279,6 +279,9 @@ enum hp_wmi_data_elements {
PSWD_ENCODINGS = 13,
PSWD_IS_SET = 14,
PSWD_ELEM_CNT = 15,
+
+ /* Minimum elements shared by all attribute types (NAME..SECURITY_LEVEL) */
+ COMMON_ELEM_CNT = SECURITY_LEVEL + 1,
};
#define GET_INSTANCE_ID(type) \
@@ -401,6 +404,7 @@ int hp_populate_string_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
int hp_alloc_string_data(void);
void hp_exit_string_attributes(void);
int hp_populate_string_package_data(union acpi_object *str_obj,
+ int str_obj_count,
int instance_id,
struct kobject *attr_name_kobj);
@@ -411,6 +415,7 @@ int hp_populate_integer_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
int hp_alloc_integer_data(void);
void hp_exit_integer_attributes(void);
int hp_populate_integer_package_data(union acpi_object *integer_obj,
+ int integer_obj_count,
int instance_id,
struct kobject *attr_name_kobj);
@@ -421,6 +426,7 @@ int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
int hp_alloc_enumeration_data(void);
void hp_exit_enumeration_attributes(void);
int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
+ int enum_obj_count,
int instance_id,
struct kobject *attr_name_kobj);
@@ -432,6 +438,7 @@ int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr,
int hp_alloc_ordered_list_data(void);
void hp_exit_ordered_list_attributes(void);
int hp_populate_ordered_list_package_data(union acpi_object *order_obj,
+ int order_obj_count,
int instance_id,
struct kobject *attr_name_kobj);
@@ -440,6 +447,7 @@ int hp_populate_password_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
int instance_id,
struct kobject *attr_name_kobj);
int hp_populate_password_package_data(union acpi_object *password_obj,
+ int password_obj_count,
int instance_id,
struct kobject *attr_name_kobj);
int hp_alloc_password_data(void);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c
index d89d11e8610f..db02f1f49a59 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c
@@ -165,10 +165,11 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
/* Check that both expected and read object type match */
if (expected_enum_types[eloc] != enum_obj[elem].type) {
- pr_err("Error expected type %d for elem %d, but got type %d instead\n",
- expected_enum_types[eloc], elem, enum_obj[elem].type);
+ pr_warn("Unexpected element type at elem %d: expected %d, got %d, skipping\n",
+ elem, expected_enum_types[eloc], enum_obj[elem].type);
kfree(str_value);
- return -EIO;
+ str_value = NULL;
+ continue;
}
/* Assign appropriate element value to corresponding field */
@@ -232,6 +233,8 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
kfree(str_value);
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case SECURITY_LEVEL:
@@ -288,6 +291,8 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
kfree(str_value);
str_value = NULL;
}
+ if (size)
+ elem += (size < MAX_VALUES_SIZE ? size : MAX_VALUES_SIZE) - 1;
break;
default:
pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem);
@@ -308,10 +313,12 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
* Populate all properties of an instance under enumeration attribute
*
* @enum_obj: ACPI object with enumeration data
+ * @enum_obj_count: Number of elements in @enum_obj
* @instance_id: The instance to enumerate
* @attr_name_kobj: The parent kernel object
*/
int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
+ int enum_obj_count,
int instance_id,
struct kobject *attr_name_kobj)
{
@@ -320,7 +327,7 @@ int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
enum_data->attr_name_kobj = attr_name_kobj;
hp_populate_enumeration_elements_from_package(enum_obj,
- enum_obj->package.count,
+ enum_obj_count,
instance_id);
hp_update_attribute_permissions(enum_data->common.is_readonly,
&enumeration_current_val);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
index 875a807ccb89..dec89c391c06 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
@@ -246,6 +246,8 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_
kfree(str_value);
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case SECURITY_LEVEL:
@@ -278,10 +280,12 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_
* Populate all properties of an instance under integer attribute
*
* @integer_obj: ACPI object with integer data
+ * @integer_obj_count: Number of elements in @integer_obj
* @instance_id: The instance to enumerate
* @attr_name_kobj: The parent kernel object
*/
int hp_populate_integer_package_data(union acpi_object *integer_obj,
+ int integer_obj_count,
int instance_id,
struct kobject *attr_name_kobj)
{
@@ -289,7 +293,7 @@ int hp_populate_integer_package_data(union acpi_object *integer_obj,
integer_data->attr_name_kobj = attr_name_kobj;
hp_populate_integer_elements_from_package(integer_obj,
- integer_obj->package.count,
+ integer_obj_count,
instance_id);
hp_update_attribute_permissions(integer_data->common.is_readonly,
&integer_current_val);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
index 94a95ee57810..47ae10d42b02 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
@@ -148,7 +148,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
if (!order_obj)
return -EINVAL;
- for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) {
+ for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count; elem++, eloc++) {
switch (order_obj[elem].type) {
case ACPI_TYPE_STRING:
@@ -239,6 +239,8 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
kfree(str_value);
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case SECURITY_LEVEL:
@@ -268,7 +270,9 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
* Ordered list data is stored in hex and comma separated format
* Convert the data and split it to show each element
*/
- ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);
+ ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer,
+ order_obj[elem].string.length,
+ &tmpstr, &tmp_len);
if (ret)
goto exit_list;
@@ -307,10 +311,12 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
* Populate all properties of an instance under ordered_list attribute
*
* @order_obj: ACPI object with ordered_list data
+ * @order_obj_count: Number of elements in @order_obj
* @instance_id: The instance to enumerate
* @attr_name_kobj: The parent kernel object
*/
-int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id,
+int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int order_obj_count,
+ int instance_id,
struct kobject *attr_name_kobj)
{
struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
@@ -318,7 +324,7 @@ int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int inst
ordered_list_data->attr_name_kobj = attr_name_kobj;
hp_populate_ordered_list_elements_from_package(order_obj,
- order_obj->package.count,
+ order_obj_count,
instance_id);
hp_update_attribute_permissions(ordered_list_data->common.is_readonly,
&ordered_list_current_val);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
index 6775a7ca74f5..b165f9b056e6 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
@@ -67,7 +67,7 @@ static int validate_password_input(int instance_id, const char *buf)
struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
length = strlen(buf);
- if (buf[length - 1] == '\n')
+ if (length > 0 && buf[length - 1] == '\n')
length--;
if (length > MAX_PASSWD_SIZE)
@@ -128,7 +128,7 @@ static ssize_t new_password_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
- return store_password_instance(kobj, buf, count, true);
+ return store_password_instance(kobj, buf, count, false);
}
static struct kobj_attribute password_new_password = __ATTR_WO(new_password);
@@ -329,6 +329,8 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case SECURITY_LEVEL:
password_data->common.security_level = int_value;
@@ -359,6 +361,11 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
case PSWD_ENCODINGS:
size = min_t(u32, password_data->encodings_size, MAX_ENCODINGS_SIZE);
for (pos_values = 0; pos_values < size; pos_values++) {
+ if (elem + pos_values >= password_obj_count) {
+ pr_err("Error elem-objects package is too small\n");
+ return -EINVAL;
+ }
+
ret = hp_convert_hexstr_to_str(password_obj[elem + pos_values].string.pointer,
password_obj[elem + pos_values].string.length,
&str_value, &value_len);
@@ -372,6 +379,8 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case PSWD_IS_SET:
password_data->is_enabled = int_value;
@@ -395,10 +404,12 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
* Populate all properties for an instance under password attribute
*
* @password_obj: ACPI object with password data
+ * @password_obj_count: Number of elements in @password_obj
* @instance_id: The instance to enumerate
* @attr_name_kobj: The parent kernel object
*/
-int hp_populate_password_package_data(union acpi_object *password_obj, int instance_id,
+int hp_populate_password_package_data(union acpi_object *password_obj, int password_obj_count,
+ int instance_id,
struct kobject *attr_name_kobj)
{
struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
@@ -406,7 +417,7 @@ int hp_populate_password_package_data(union acpi_object *password_obj, int insta
password_data->attr_name_kobj = attr_name_kobj;
hp_populate_password_elements_from_package(password_obj,
- password_obj->package.count,
+ password_obj_count,
instance_id);
hp_friendly_user_name_update(password_data->common.path,
diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
index 86f90238750c..cc6b8795a5bf 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
@@ -238,7 +238,7 @@ static ssize_t sk_store(struct kobject *kobj,
ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK,
HPWMI_SECUREPLATFORM,
(void *)bioscfg_drv.spm_data.signing_key,
- count, 0);
+ length, 0);
if (!ret) {
bioscfg_drv.spm_data.mechanism = SIGNING_KEY;
@@ -274,7 +274,7 @@ static ssize_t kek_store(struct kobject *kobj,
ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK,
HPWMI_SECUREPLATFORM,
(void *)bioscfg_drv.spm_data.endorsement_key,
- count, 0);
+ length, 0);
if (!ret) {
bioscfg_drv.spm_data.mechanism = ENDORSEMENT_KEY;
diff --git a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
index 5b3eac7f1685..7a7a13eb1203 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
@@ -237,6 +237,8 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob
kfree(str_value);
str_value = NULL;
}
+ if (size)
+ elem += size - 1;
break;
case SECURITY_LEVEL:
@@ -267,10 +269,12 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob
* Populate all properties of an instance under string attribute
*
* @string_obj: ACPI object with string data
+ * @string_obj_count: Number of elements in @string_obj
* @instance_id: The instance to enumerate
* @attr_name_kobj: The parent kernel object
*/
int hp_populate_string_package_data(union acpi_object *string_obj,
+ int string_obj_count,
int instance_id,
struct kobject *attr_name_kobj)
{
@@ -279,7 +283,7 @@ int hp_populate_string_package_data(union acpi_object *string_obj,
string_data->attr_name_kobj = attr_name_kobj;
hp_populate_string_elements_from_package(string_obj,
- string_obj->package.count,
+ string_obj_count,
instance_id);
hp_update_attribute_permissions(string_data->common.is_readonly,
diff --git a/drivers/platform/x86/intel/ishtp_eclite.c b/drivers/platform/x86/intel/ishtp_eclite.c
index 93ac8b2dbf38..bca7e217878b 100644
--- a/drivers/platform/x86/intel/ishtp_eclite.c
+++ b/drivers/platform/x86/intel/ishtp_eclite.c
@@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
rv = acpi_opregion_init(opr_dev);
if (rv) {
dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n");
- goto err_exit;
+ goto err_put;
}
/* Reprobe devices depending on ECLite - battery, fan, etc. */
acpi_dev_clear_dependencies(opr_dev->adev);
return 0;
+
+err_put:
+ acpi_dev_put(opr_dev->adev);
err_exit:
ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(ecl_ishtp_cl);
diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
index df0b0118b751..7a4f3086e97b 100644
--- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
+++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
@@ -497,6 +497,8 @@ static long isst_if_core_power_state(void __user *argp)
#define SST_CLOS_CONFIG_MAX_START 16
#define SST_CLOS_CONFIG_MAX_WIDTH 8
+#define SST_MAX_CLOS 3
+
static long isst_if_clos_param(void __user *argp)
{
struct tpmi_per_power_domain_info *power_domain_info;
@@ -505,6 +507,9 @@ static long isst_if_clos_param(void __user *argp)
if (copy_from_user(&clos_param, argp, sizeof(clos_param)))
return -EFAULT;
+ if (clos_param.clos > SST_MAX_CLOS)
+ return -EINVAL;
+
power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id);
if (!power_domain_info)
return -EINVAL;
@@ -548,6 +553,8 @@ static long isst_if_clos_param(void __user *argp)
#define SST_CLOS_ASSOC_CPUS_PER_REG 16
#define SST_CLOS_ASSOC_BITS_PER_CPU 4
+#define SST_CLOS_ASSOC_MAX_LOGICAL_CPU 63
+
static long isst_if_clos_assoc(void __user *argp)
{
struct isst_if_clos_assoc_cmds assoc_cmds;
@@ -573,7 +580,13 @@ static long isst_if_clos_assoc(void __user *argp)
if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc)))
return -EFAULT;
- if (clos_assoc.socket_id > topology_max_packages())
+ if (clos_assoc.clos > SST_MAX_CLOS)
+ return -EINVAL;
+
+ if (clos_assoc.socket_id >= topology_max_packages())
+ return -EINVAL;
+
+ if (clos_assoc.logical_cpu > SST_CLOS_ASSOC_MAX_LOGICAL_CPU)
return -EINVAL;
cpu = clos_assoc.logical_cpu;
@@ -591,6 +604,8 @@ static long isst_if_clos_assoc(void __user *argp)
pkg_id = clos_assoc.socket_id;
sst_inst = isst_common.sst_inst[pkg_id];
+ if (!sst_inst)
+ return -EINVAL;
if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains)
return -EINVAL;
@@ -684,6 +699,7 @@ static long isst_if_clos_assoc(void __user *argp)
#define SST_PP_FEATURE_STATE_START 8
#define SST_PP_FEATURE_STATE_WIDTH 8
+#define SST_PP_FEATURE_STATE_VALID_MASK GENMASK(1, 0)
#define SST_BF_FEATURE_SUPPORTED_START 12
#define SST_BF_FEATURE_SUPPORTED_WIDTH 1
@@ -804,6 +820,9 @@ static int isst_if_set_perf_feature(void __user *argp)
if (!power_domain_info)
return -EINVAL;
+ if (perf_feature.feature & ~SST_PP_FEATURE_STATE_VALID_MASK)
+ return -EINVAL;
+
_write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET,
SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH,
SST_MUL_FACTOR_NONE)
@@ -1007,6 +1026,12 @@ static int isst_if_get_perf_level_mask(void __user *argp)
if (!power_domain_info)
return -EINVAL;
+ if (cpumask.level > power_domain_info->max_level)
+ return -EINVAL;
+
+ if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.level)))
+ return -EINVAL;
+
_read_pp_level_info("mask", mask, cpumask.level, SST_PP_INFO_2_OFFSET,
SST_PP_RSLVD_CORE_MASK_START, SST_PP_RSLVD_CORE_MASK_WIDTH,
SST_MUL_FACTOR_NONE)
@@ -1088,6 +1113,9 @@ static int isst_if_get_base_freq_mask(void __user *argp)
if (!power_domain_info)
return -EINVAL;
+ if (cpumask.level > power_domain_info->max_level)
+ return -EINVAL;
+
_read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET,
P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH,
SST_MUL_FACTOR_NONE)
diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c
index 20130549a7a5..85406b4a99b0 100644
--- a/drivers/pmdomain/bcm/bcm2835-power.c
+++ b/drivers/pmdomain/bcm/bcm2835-power.c
@@ -682,7 +682,12 @@ static int bcm2835_power_probe(struct platform_device *pdev)
if (ret)
goto fail;
- of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate);
+ ret = of_genpd_add_provider_onecell(dev->parent->of_node,
+ &power->pd_xlate);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add genpd provider\n");
+ goto fail;
+ }
dev_info(dev, "Broadcom BCM2835 power domains driver");
return 0;
diff --git a/drivers/power/supply/bd99954-charger.h b/drivers/power/supply/bd99954-charger.h
index f58897925383..d053ac901274 100644
--- a/drivers/power/supply/bd99954-charger.h
+++ b/drivers/power/supply/bd99954-charger.h
@@ -279,17 +279,7 @@ enum bd9995x_fields {
F_VBUS_EXTID,
F_VBUS_IDRDET,
F_VBUS_INDO,
- F_VBUS_UCDSWEN,
- F_VBUS_RREF_EN,
- F_VBUS_DPPU_EN,
- F_VBUS_DPREF_EN,
- F_VBUS_DMREF_EN,
- F_VBUS_DPDET_EN,
- F_VBUS_DMDET_EN,
- F_VBUS_DPSINK_EN,
- F_VBUS_DMSINK_EN,
- F_VBUS_DP_BUFF_EN,
- F_VBUS_DM_BUFF_EN,
+
F_VBUS_EXTCLKENBL,
F_VBUS_PLSTESTEN,
F_VBUS_UCDSWEN_TSTENB,
@@ -626,17 +616,6 @@ static const struct reg_field bd9995x_reg_fields[] = {
[F_VBUS_EXTID] = REG_FIELD(VBUS_IDD_STATUS, 5, 5),
[F_VBUS_IDRDET] = REG_FIELD(VBUS_IDD_STATUS, 4, 4),
[F_VBUS_INDO] = REG_FIELD(VBUS_IDD_STATUS, 0, 3),
- [F_VBUS_UCDSWEN] = REG_FIELD(VCC_UCD_FCTRL_SET, 10, 10),
- [F_VBUS_RREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 9, 9),
- [F_VBUS_DPPU_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 8, 8),
- [F_VBUS_DPREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 7, 7),
- [F_VBUS_DMREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 6, 6),
- [F_VBUS_DPDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 5, 5),
- [F_VBUS_DMDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 4, 4),
- [F_VBUS_DPSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 3, 3),
- [F_VBUS_DMSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 2, 2),
- [F_VBUS_DP_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 1, 1),
- [F_VBUS_DM_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 0, 0),
[F_VBUS_EXTCLKENBL] = REG_FIELD(VBUS_UCD_FCTRL_EN, 15, 15),
[F_VBUS_PLSTESTEN] = REG_FIELD(VBUS_UCD_FCTRL_EN, 14, 14),
diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c
index 2852860abf86..6a292e8edda0 100644
--- a/drivers/power/supply/bq24257_charger.c
+++ b/drivers/power/supply/bq24257_charger.c
@@ -18,6 +18,7 @@
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/devm-helpers.h>
#include <linux/acpi.h>
#include <linux/of.h>
@@ -1018,10 +1019,6 @@ static int bq24257_probe(struct i2c_client *client)
if (bq->chip == BQ24250)
bq->iilimit_autoset_enable = false;
- if (bq->iilimit_autoset_enable)
- INIT_DELAYED_WORK(&bq->iilimit_setup_work,
- bq24257_iilimit_setup_work);
-
/*
* The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's
* not probe for it and instead use a SW-based approach to determine
@@ -1062,6 +1059,14 @@ static int bq24257_probe(struct i2c_client *client)
return ret;
}
+ if (bq->iilimit_autoset_enable) {
+ ret = devm_delayed_work_autocancel(dev,
+ &bq->iilimit_setup_work,
+ bq24257_iilimit_setup_work);
+ if (ret)
+ return ret;
+ }
+
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq24257_irq_handler_thread,
IRQF_TRIGGER_FALLING |
@@ -1079,9 +1084,6 @@ static void bq24257_remove(struct i2c_client *client)
{
struct bq24257_device *bq = i2c_get_clientdata(client);
- if (bq->iilimit_autoset_enable)
- cancel_delayed_work_sync(&bq->iilimit_setup_work);
-
bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
}
diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c
index 2b0216d32d5f..a56f31587cc0 100644
--- a/drivers/power/supply/bq256xx_charger.c
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -905,6 +905,8 @@ static void bq256xx_charger_reset(void *data)
if (!IS_ERR_OR_NULL(bq->usb3_phy))
usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb);
+
+ cancel_work_sync(&bq->usb_work);
}
static int bq256xx_set_charger_property(struct power_supply *psy,
@@ -1727,24 +1729,12 @@ static int bq256xx_probe(struct i2c_client *client)
return ret;
}
- ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
- if (ret)
- return ret;
+ INIT_WORK(&bq->usb_work, bq256xx_usb_work);
+ bq->usb_nb.notifier_call = bq256xx_usb_notifier;
/* OTG reporting */
bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
- if (!IS_ERR_OR_NULL(bq->usb2_phy)) {
- INIT_WORK(&bq->usb_work, bq256xx_usb_work);
- bq->usb_nb.notifier_call = bq256xx_usb_notifier;
- usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
- }
-
bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
- if (!IS_ERR_OR_NULL(bq->usb3_phy)) {
- INIT_WORK(&bq->usb_work, bq256xx_usb_work);
- bq->usb_nb.notifier_call = bq256xx_usb_notifier;
- usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
- }
ret = bq256xx_power_supply_init(bq, &psy_cfg, dev);
if (ret) {
@@ -1752,6 +1742,17 @@ static int bq256xx_probe(struct i2c_client *client)
return ret;
}
+ /* Register after the power supplies so devm runs it first. */
+ ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
+ if (ret)
+ return ret;
+
+ if (!IS_ERR_OR_NULL(bq->usb2_phy))
+ usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
+
+ if (!IS_ERR_OR_NULL(bq->usb3_phy))
+ usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
+
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq256xx_irq_handler_thread,
diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c
index ea824f13343c..b9699f62e110 100644
--- a/drivers/power/supply/bq25890_charger.c
+++ b/drivers/power/supply/bq25890_charger.c
@@ -1389,6 +1389,14 @@ static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
return 0;
}
+static void bq25890_release_secondary_chrg(void *data)
+{
+ struct bq25890_device *bq = data;
+
+ power_supply_put(bq->secondary_chrg);
+ bq->secondary_chrg = NULL;
+}
+
static int bq25890_fw_probe(struct bq25890_device *bq)
{
int ret;
@@ -1401,6 +1409,10 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
bq->secondary_chrg = power_supply_get_by_name(str);
if (!bq->secondary_chrg)
return -EPROBE_DEFER;
+
+ ret = devm_add_action_or_reset(bq->dev, bq25890_release_secondary_chrg, bq);
+ if (ret)
+ return ret;
}
/* Optional, left at 0 if property is not present */
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index ff01d5d850f9..77c80901910c 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -324,7 +324,7 @@ static u8
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
- [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTES] = 0x1a,
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_RC] = 0x10,
@@ -489,7 +489,7 @@ static u8
[BQ27XXX_REG_RC] = 0x10,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
- [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x22,
@@ -510,7 +510,7 @@ static u8
[BQ27XXX_REG_RC] = 0x10,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
- [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x22,
diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c
index 8008e31c0c09..a1614858a866 100644
--- a/drivers/power/supply/cros_usbpd-charger.c
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -136,6 +136,11 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
if (ret < 0)
return ret;
+ if (resp.port_count > EC_USB_PD_MAX_PORTS) {
+ dev_warn(charger->dev, "Charge port count out of bounds\n");
+ return EC_USB_PD_MAX_PORTS;
+ }
+
return resp.port_count;
}
@@ -149,6 +154,11 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger)
if (ret < 0)
return ret;
+ if (resp.num_ports > EC_USB_PD_MAX_PORTS) {
+ dev_warn(charger->dev, "USB PD port count out of bounds\n");
+ return EC_USB_PD_MAX_PORTS;
+ }
+
return resp.num_ports;
}
@@ -600,10 +610,13 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
/*
* Sanity checks on the number of ports:
- * there should be at most 1 dedicated port
+ * there should be at most 1 dedicated port, and the count must
+ * not exceed the maximum number of supported ports
+ * (EC_USB_PD_MAX_PORTS).
*/
if (charger->num_charger_ports < charger->num_usbpd_ports ||
- charger->num_charger_ports > (charger->num_usbpd_ports + 1)) {
+ charger->num_charger_ports > (charger->num_usbpd_ports + 1) ||
+ charger->num_charger_ports > EC_USB_PD_MAX_PORTS) {
dev_err(dev, "Unexpected number of charge port count\n");
ret = -EPROTO;
goto fail_nowarn;
diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c
index b6efc454e4f0..2233dc9cefe0 100644
--- a/drivers/power/supply/isp1704_charger.c
+++ b/drivers/power/supply/isp1704_charger.c
@@ -482,6 +482,7 @@ static int isp1704_charger_remove(struct platform_device *pdev)
struct isp1704_charger *isp = platform_get_drvdata(pdev);
usb_unregister_notifier(isp->phy, &isp->nb);
+ cancel_work_sync(&isp->work);
power_supply_unregister(isp->psy);
isp1704_charger_set_power(isp, 0);
diff --git a/drivers/power/supply/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c
index 0875391f7ac6..d49af3ce830b 100644
--- a/drivers/power/supply/lp8727_charger.c
+++ b/drivers/power/supply/lp8727_charger.c
@@ -280,10 +280,10 @@ static int lp8727_setup_irq(struct lp8727_chg *pchg)
static void lp8727_release_irq(struct lp8727_chg *pchg)
{
- cancel_delayed_work_sync(&pchg->work);
-
if (pchg->irq)
free_irq(pchg->irq, pchg);
+
+ cancel_delayed_work_sync(&pchg->work);
}
static enum power_supply_property lp8727_charger_prop[] = {
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
index ff42db672899..2814327c8e7d 100644
--- a/drivers/power/supply/max17040_battery.c
+++ b/drivers/power/supply/max17040_battery.c
@@ -433,16 +433,12 @@ static const struct power_supply_desc max17040_battery_desc = {
static int max17040_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
- struct i2c_adapter *adapter = client->adapter;
struct power_supply_config psy_cfg = {};
struct max17040_chip *chip;
enum chip_id chip_id;
bool enable_irq = false;
int ret;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- return -EIO;
-
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@@ -532,7 +528,7 @@ static int max17040_suspend(struct device *dev)
// disable soc alert to prevent wakeup
max17040_set_soc_alert(chip, 0);
else
- cancel_delayed_work(&chip->work);
+ cancel_delayed_work_sync(&chip->work);
if (client->irq && device_may_wakeup(dev))
enable_irq_wake(client->irq);
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index e5eade132816..653f70403f44 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -972,7 +972,7 @@ static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src)
memcpy(dest, src + 1, len);
dest[len] = '\0';
} else {
- memcpy(dest, src, BATTMGR_STRING_LEN);
+ strscpy(dest, src, BATTMGR_STRING_LEN);
}
}
diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c
index 248dc2b5e1f7..4ff513e92598 100644
--- a/drivers/power/supply/rt9455_charger.c
+++ b/drivers/power/supply/rt9455_charger.c
@@ -1582,6 +1582,19 @@ static const struct regmap_config rt9455_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static void rt9455_cancel_all_delayed_works(void *data)
+{
+ struct rt9455_info *info = data;
+
+ /*
+ * Both pwr_rdy_work and batt_presence_work can queue
+ * max_charging_time_work, so cancel them first.
+ */
+ cancel_delayed_work_sync(&info->pwr_rdy_work);
+ cancel_delayed_work_sync(&info->batt_presence_work);
+ cancel_delayed_work_sync(&info->max_charging_time_work);
+}
+
static int rt9455_probe(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
@@ -1672,6 +1685,10 @@ static int rt9455_probe(struct i2c_client *client)
goto put_usb_notifier;
}
+ ret = devm_add_action_or_reset(dev, rt9455_cancel_all_delayed_works, info);
+ if (ret)
+ goto put_usb_notifier;
+
ret = devm_request_threaded_irq(dev, client->irq, NULL,
rt9455_irq_handler_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
@@ -1712,10 +1729,6 @@ static void rt9455_remove(struct i2c_client *client)
if (info->nb.notifier_call)
usb_unregister_notifier(info->usb_phy, &info->nb);
#endif
-
- cancel_delayed_work_sync(&info->pwr_rdy_work);
- cancel_delayed_work_sync(&info->max_charging_time_work);
- cancel_delayed_work_sync(&info->batt_presence_work);
}
static const struct i2c_device_id rt9455_i2c_id_table[] = {
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index f30a542d4716..f1c07259afd7 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -216,6 +216,7 @@ struct sbs_info {
u32 flags;
int technology;
char strings[NR_STRING_BUFFERS][I2C_SMBUS_BLOCK_MAX + 1];
+ char serial[5];
};
static char *sbs_get_string_buf(struct sbs_info *chip,
@@ -820,18 +821,18 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
return 0;
}
-static char sbs_serial[5];
static int sbs_get_battery_serial_number(struct i2c_client *client,
union power_supply_propval *val)
{
+ struct sbs_info *chip = i2c_get_clientdata(client);
int ret;
ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr);
if (ret < 0)
return ret;
- sprintf(sbs_serial, "%04x", ret);
- val->strval = sbs_serial;
+ sprintf(chip->serial, "%04x", ret);
+ val->strval = chip->serial;
return 0;
}
diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c
index 9ac17cf7a126..0b3153eed8c4 100644
--- a/drivers/power/supply/sc2731_charger.c
+++ b/drivers/power/supply/sc2731_charger.c
@@ -466,6 +466,7 @@ static int sc2731_charger_probe(struct platform_device *pdev)
mutex_init(&info->lock);
info->dev = &pdev->dev;
INIT_WORK(&info->work, sc2731_charger_work);
+ platform_set_drvdata(pdev, info);
info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!info->regmap) {
@@ -511,13 +512,12 @@ static int sc2731_charger_probe(struct platform_device *pdev)
return 0;
}
-static int sc2731_charger_remove(struct platform_device *pdev)
+static void sc2731_charger_remove(struct platform_device *pdev)
{
struct sc2731_charger_info *info = platform_get_drvdata(pdev);
usb_unregister_notifier(info->usb_phy, &info->usb_notify);
-
- return 0;
+ cancel_work_sync(&info->work);
}
static const struct of_device_id sc2731_charger_of_match[] = {
@@ -532,7 +532,7 @@ static struct platform_driver sc2731_charger_driver = {
.of_match_table = sc2731_charger_of_match,
},
.probe = sc2731_charger_probe,
- .remove = sc2731_charger_remove,
+ .remove_new = sc2731_charger_remove,
};
module_platform_driver(sc2731_charger_driver);
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index e78d061d8d32..1727a030b473 100644
--- a/drivers/power/supply/twl4030_charger.c
+++ b/drivers/power/supply/twl4030_charger.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/devm-helpers.h>
#include <linux/interrupt.h>
#include <linux/mfd/twl.h>
#include <linux/power_supply.h>
@@ -1003,8 +1004,15 @@ static int twl4030_bci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bci);
- INIT_WORK(&bci->work, twl4030_bci_usb_work);
- INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
+ ret = devm_delayed_work_autocancel(&pdev->dev, &bci->current_worker,
+ twl4030_current_worker);
+ if (ret)
+ return ret;
+
+ ret = devm_work_autocancel(&pdev->dev, &bci->work,
+ twl4030_bci_usb_work);
+ if (ret)
+ return ret;
bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
if (IS_ERR(bci->channel_vac)) {
diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c
index 7970843a4f48..89a57f845e1c 100644
--- a/drivers/power/supply/ucs1002_power.c
+++ b/drivers/power/supply/ucs1002_power.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/device.h>
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -643,7 +644,10 @@ static int ucs1002_probe(struct i2c_client *client)
}
info->health = POWER_SUPPLY_HEALTH_GOOD;
- INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll);
+ ret = devm_delayed_work_autocancel(dev, &info->health_poll,
+ ucs1002_health_poll);
+ if (ret)
+ return ret;
if (irq_a_det > 0) {
ret = devm_request_threaded_irq(dev, irq_a_det, NULL,
diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c
index 41e1fdbcda16..98dfab2dcdb7 100644
--- a/drivers/pps/clients/pps-gpio.c
+++ b/drivers/pps/clients/pps-gpio.c
@@ -32,34 +32,37 @@ struct pps_gpio_device_data {
struct gpio_desc *echo_pin;
struct timer_list echo_timer; /* timer to reset echo active state */
bool assert_falling_edge;
- bool capture_clear;
unsigned int echo_active_ms; /* PPS echo active duration */
unsigned long echo_timeout; /* timer timeout value in jiffies */
+ struct pps_event_time ts; /* timestamp captured in hardirq */
};
/*
* Report the PPS event
*/
-static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
+/*
+ * Primary hardirq handler -- runs in hardirq context even on PREEMPT_RT.
+ * Only captures the timestamp; all other work is deferred to the thread.
+ */
+static irqreturn_t pps_gpio_irq_hardirq(int irq, void *data)
{
- const struct pps_gpio_device_data *info;
- struct pps_event_time ts;
- int rising_edge;
+ struct pps_gpio_device_data *info = data;
- /* Get the time stamp first */
- pps_get_ts(&ts);
+ pps_get_ts(&info->ts);
- info = data;
+ return IRQ_WAKE_THREAD;
+}
+
+/*
+ * Threaded handler -- processes the PPS event using the timestamp
+ * captured in hardirq context above.
+ */
+static irqreturn_t pps_gpio_irq_thread(int irq, void *data)
+{
+ struct pps_gpio_device_data *info = data;
- rising_edge = gpiod_get_value(info->gpio_pin);
- if ((rising_edge && !info->assert_falling_edge) ||
- (!rising_edge && info->assert_falling_edge))
- pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
- else if (info->capture_clear &&
- ((rising_edge && info->assert_falling_edge) ||
- (!rising_edge && !info->assert_falling_edge)))
- pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
+ pps_event(info->pps, &info->ts, PPS_CAPTUREASSERT, data);
return IRQ_HANDLED;
}
@@ -75,11 +78,6 @@ static void pps_gpio_echo(struct pps_device *pps, int event, void *data)
if (pps->params.mode & PPS_ECHOASSERT)
gpiod_set_value(info->echo_pin, 1);
break;
-
- case PPS_CAPTURECLEAR:
- if (pps->params.mode & PPS_ECHOCLEAR)
- gpiod_set_value(info->echo_pin, 1);
- break;
}
/* fire the timer */
@@ -141,15 +139,8 @@ static int pps_gpio_setup(struct device *dev)
static unsigned long
get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
{
- unsigned long flags = data->assert_falling_edge ?
- IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
-
- if (data->capture_clear) {
- flags |= ((flags & IRQF_TRIGGER_RISING) ?
- IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
- }
-
- return flags;
+ return data->assert_falling_edge ? IRQF_TRIGGER_FALLING :
+ IRQF_TRIGGER_RISING;
}
static int pps_gpio_probe(struct platform_device *pdev)
@@ -182,9 +173,6 @@ static int pps_gpio_probe(struct platform_device *pdev)
/* initialize PPS specific parts of the bookkeeping data structure. */
data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
- if (data->capture_clear)
- data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
- PPS_ECHOCLEAR;
data->info.owner = THIS_MODULE;
snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
pdev->name, pdev->id);
@@ -196,8 +184,6 @@ static int pps_gpio_probe(struct platform_device *pdev)
/* register PPS source */
pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
- if (data->capture_clear)
- pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
data->pps = pps_register_source(&data->info, pps_default_params);
if (IS_ERR(data->pps)) {
dev_err(dev, "failed to register IRQ %d as PPS source\n",
@@ -206,8 +192,10 @@ static int pps_gpio_probe(struct platform_device *pdev)
}
/* register IRQ interrupt handler */
- ret = request_irq(data->irq, pps_gpio_irq_handler,
- get_irqf_trigger_flags(data), data->info.name, data);
+ ret = request_threaded_irq(data->irq,
+ pps_gpio_irq_hardirq, pps_gpio_irq_thread,
+ get_irqf_trigger_flags(data) | IRQF_ONESHOT,
+ data->info.name, data);
if (ret) {
pps_unregister_source(data->pps);
dev_err(dev, "failed to acquire IRQ %d\n", data->irq);
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 9539695b337a..f66a749b38b5 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -63,13 +63,19 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata)
err = wait_event_interruptible(pps->queue,
ev != pps->last_ev);
else {
+ struct timespec64 ts;
unsigned long ticks;
dev_dbg(&pps->dev, "timeout %lld.%09d\n",
(long long) fdata->timeout.sec,
fdata->timeout.nsec);
- ticks = fdata->timeout.sec * HZ;
- ticks += fdata->timeout.nsec / (NSEC_PER_SEC / HZ);
+
+ if (fdata->timeout.sec < 0)
+ return -ETIMEDOUT;
+
+ ts.tv_sec = fdata->timeout.sec;
+ ts.tv_nsec = fdata->timeout.nsec;
+ ticks = timespec64_to_jiffies(&ts);
if (ticks != 0) {
err = wait_event_interruptible_timeout(
diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c
index ca5d06060e05..fcc16b2e2341 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -582,9 +582,13 @@ static void dma_req_free(struct kref *ref)
}
if (req->map) {
- mutex_lock(&req->map->md->buf_mutex);
- kref_put(&req->map->ref, mport_release_mapping);
- mutex_unlock(&req->map->md->buf_mutex);
+ struct rio_mport_mapping *map = req->map;
+ struct mport_dev *md = map->md;
+
+ mutex_lock(&md->buf_mutex);
+ req->map = NULL;
+ kref_put(&map->ref, mport_release_mapping);
+ mutex_unlock(&md->buf_mutex);
}
kref_put(&priv->dma_ref, mport_release_dma);
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index dcd6619a4b02..3cc25d053451 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -874,6 +874,7 @@ static struct rio_net *rio_scan_alloc_net(struct rio_mport *mport,
net->dev.release = rio_scan_release_dev;
if (rio_add_net(net)) {
put_device(&net->dev);
+ mport->net = NULL;
net = NULL;
}
}
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 965d4f0c18a6..9c61842d8367 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1543,13 +1543,15 @@ config REGULATOR_TPS6594
depends on MFD_TPS6594 && OF
default MFD_TPS6594
help
- This driver supports TPS6594 voltage regulator chips.
+ This driver supports TPS6594 series and TPS65224 voltage regulator chips.
TPS6594 series of PMICs have 5 BUCKs and 4 LDOs
voltage regulators.
BUCKs 1,2,3,4 can be used in single phase or multiphase mode.
Part number defines which single or multiphase mode is i used.
It supports software based voltage control
for different voltage domains.
+ TPS65224 PMIC has 4 BUCKs and 3 LDOs. BUCK12 can be used in dual phase.
+ All BUCKs and LDOs volatge can be controlled through software.
config REGULATOR_TPS6524X
tristate "TI TPS6524X Power regulators"
diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c
index da378bfdba40..85e2aaa1a06b 100644
--- a/drivers/regulator/as3722-regulator.c
+++ b/drivers/regulator/as3722-regulator.c
@@ -600,7 +600,6 @@ static int as3722_get_regulator_dt_data(struct platform_device *pdev,
ret = of_regulator_match(&pdev->dev, np, as3722_regulator_matches,
ARRAY_SIZE(as3722_regulator_matches));
- of_node_put(np);
if (ret < 0) {
dev_err(&pdev->dev, "Parsing of regulator node failed: %d\n",
ret);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 3f352d1c64b7..35729234c375 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -26,6 +26,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/module.h>
+#include <linux/workqueue.h>
#define CREATE_TRACE_POINTS
#include <trace/events/regulator.h>
@@ -6327,8 +6328,9 @@ static int __init regulator_init_complete(void)
* we'd only do this on systems that need it, and a kernel
* command line option might be useful.
*/
- schedule_delayed_work(®ulator_init_complete_work,
- msecs_to_jiffies(30000));
+ queue_delayed_work(system_freezable_wq,
+ ®ulator_init_complete_work,
+ msecs_to_jiffies(30000));
return 0;
}
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
index fadb4717384a..ef38f8630201 100644
--- a/drivers/regulator/max8998.c
+++ b/drivers/regulator/max8998.c
@@ -611,7 +611,6 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev,
}
pdata->num_regulators = rdata - pdata->regulators;
- of_node_put(reg_np);
of_node_put(regulators_np);
ret = max8998_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np);
diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c
index 063e12c08e75..240fecbf31e5 100644
--- a/drivers/regulator/qcom-refgen-regulator.c
+++ b/drivers/regulator/qcom-refgen-regulator.c
@@ -66,7 +66,7 @@ static struct regulator_desc sdm845_refgen_desc = {
.enable_time = 5,
.name = "refgen",
.owner = THIS_MODULE,
- .type = REGULATOR_VOLTAGE,
+ .type = REGULATOR_CURRENT,
.ops = &(const struct regulator_ops) {
.enable = qcom_sdm845_refgen_enable,
.disable = qcom_sdm845_refgen_disable,
@@ -82,7 +82,7 @@ static struct regulator_desc sm8250_refgen_desc = {
.enable_time = 5,
.name = "refgen",
.owner = THIS_MODULE,
- .type = REGULATOR_VOLTAGE,
+ .type = REGULATOR_CURRENT,
.ops = &(const struct regulator_ops) {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
index b2e359ac3169..4bdab1abf7ed 100644
--- a/drivers/regulator/qcom-rpmh-regulator.c
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -75,6 +75,7 @@ enum rpmh_regulator_type {
* @hpm_min_load_uA: Minimum load current in microamps that requires
* high power mode (HPM) operation. This is used
* for LDO hardware type regulators only.
+ * @pmic_bypass_mode: The PMIC bypass mode value.
* @pmic_mode_map: Array indexed by regulator framework mode
* containing PMIC hardware modes. Must be large
* enough to index all framework modes supported
@@ -88,6 +89,7 @@ struct rpmh_vreg_hw_data {
const struct linear_range voltage_range;
int n_voltages;
int hpm_min_load_uA;
+ int pmic_bypass_mode;
const int *pmic_mode_map;
unsigned int (*of_map_mode)(unsigned int mode);
};
@@ -274,7 +276,7 @@ static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
return pmic_mode;
if (bypassed)
- cmd.data = PMIC4_BOB_MODE_PASS;
+ cmd.data = vreg->hw_data->pmic_bypass_mode;
else
cmd.data = pmic_mode;
@@ -663,6 +665,7 @@ static const struct rpmh_vreg_hw_data pmic4_bob = {
.ops = &rpmh_regulator_vrm_bypass_ops,
.voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
.n_voltages = 84,
+ .pmic_bypass_mode = PMIC4_BOB_MODE_PASS,
.pmic_mode_map = pmic_mode_map_pmic4_bob,
.of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
};
@@ -800,6 +803,7 @@ static const struct rpmh_vreg_hw_data pmic5_bob = {
.ops = &rpmh_regulator_vrm_bypass_ops,
.voltage_range = REGULATOR_LINEAR_RANGE(3000000, 0, 31, 32000),
.n_voltages = 32,
+ .pmic_bypass_mode = PMIC5_BOB_MODE_PASS,
.pmic_mode_map = pmic_mode_map_pmic5_bob,
.of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
};
diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c
index 5fad61785e72..9ede06398648 100644
--- a/drivers/regulator/tps6594-regulator.c
+++ b/drivers/regulator/tps6594-regulator.c
@@ -18,10 +18,9 @@
#include <linux/mfd/tps6594.h>
-#define BUCK_NB 5
-#define LDO_NB 4
-#define MULTI_PHASE_NB 4
-#define REGS_INT_NB 4
+#define BUCK_NB 5
+#define LDO_NB 4
+#define MULTI_PHASE_NB 4
enum tps6594_regulator_id {
/* DCDC's */
@@ -53,7 +52,7 @@ struct tps6594_regulator_irq_type {
unsigned long event;
};
-static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = {
{ TPS6594_IRQ_NAME_VCCA_OV, "VCCA", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_VCCA_UV, "VCCA", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_VMON1_OV, "VMON1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
@@ -66,15 +65,24 @@ static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = {
REGULATOR_EVENT_OVER_VOLTAGE_WARN },
};
+static const struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = {
+ { TPS65224_IRQ_NAME_VCCA_UVOV, "VCCA", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+ { TPS65224_IRQ_NAME_VMON1_UVOV, "VMON1", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+ { TPS65224_IRQ_NAME_VMON2_UVOV, "VMON2", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
struct tps6594_regulator_irq_data {
struct device *dev;
- struct tps6594_regulator_irq_type *type;
+ const struct tps6594_regulator_irq_type *type;
struct regulator_dev *rdev;
};
struct tps6594_ext_regulator_irq_data {
struct device *dev;
- struct tps6594_regulator_irq_type *type;
+ const struct tps6594_regulator_irq_type *type;
};
#define TPS6594_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \
@@ -122,6 +130,27 @@ static const struct linear_range ldos_4_ranges[] = {
REGULATOR_LINEAR_RANGE(1200000, 0x20, 0x74, 25000),
};
+/* Voltage range for TPS65224 Bucks and LDOs */
+static const struct linear_range tps65224_bucks_1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0a, 0x0e, 20000),
+ REGULATOR_LINEAR_RANGE(600000, 0x0f, 0x72, 5000),
+ REGULATOR_LINEAR_RANGE(1100000, 0x73, 0xaa, 10000),
+ REGULATOR_LINEAR_RANGE(1660000, 0xab, 0xfd, 20000),
+};
+
+static const struct linear_range tps65224_bucks_2_3_4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0, 0x1a, 25000),
+ REGULATOR_LINEAR_RANGE(1200000, 0x1b, 0x45, 50000),
+};
+
+static const struct linear_range tps65224_ldos_1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1200000, 0xC, 0x36, 50000),
+};
+
+static const struct linear_range tps65224_ldos_2_3_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x38, 50000),
+};
+
/* Operations permitted on BUCK1/2/3/4/5 */
static const struct regulator_ops tps6594_bucks_ops = {
.is_enabled = regulator_is_enabled_regmap,
@@ -159,7 +188,7 @@ static const struct regulator_ops tps6594_ldos_4_ops = {
.map_voltage = regulator_map_voltage_linear_range,
};
-static const struct regulator_desc buck_regs[] = {
+static const struct regulator_desc tps6594_buck_regs[] = {
TPS6594_REGULATOR("BUCK1", "buck1", TPS6594_BUCK_1,
REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS6594_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_VOUT_1(0),
@@ -197,7 +226,39 @@ static const struct regulator_desc buck_regs[] = {
4, 0, 0, NULL, 0, 0),
};
-static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = {
+/* Buck configuration for TPS65224 */
+static const struct regulator_desc tps65224_buck_regs[] = {
+ TPS6594_REGULATOR("BUCK1", "buck1", TPS6594_BUCK_1,
+ REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCK1_VSET,
+ TPS6594_REG_BUCKX_VOUT_1(0),
+ TPS65224_MASK_BUCK1_VSET,
+ TPS6594_REG_BUCKX_CTRL(0),
+ TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_1_ranges,
+ 4, 0, 0, NULL, 0, 0),
+ TPS6594_REGULATOR("BUCK2", "buck2", TPS6594_BUCK_2,
+ REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_VOUT_1(1),
+ TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_CTRL(1),
+ TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
+ 4, 0, 0, NULL, 0, 0),
+ TPS6594_REGULATOR("BUCK3", "buck3", TPS6594_BUCK_3,
+ REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_VOUT_1(2),
+ TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_CTRL(2),
+ TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
+ 4, 0, 0, NULL, 0, 0),
+ TPS6594_REGULATOR("BUCK4", "buck4", TPS6594_BUCK_4,
+ REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_VOUT_1(3),
+ TPS65224_MASK_BUCKS_VSET,
+ TPS6594_REG_BUCKX_CTRL(3),
+ TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
+ 4, 0, 0, NULL, 0, 0),
+};
+
+static const struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = {
{ TPS6594_IRQ_NAME_BUCK1_OV, "BUCK1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_BUCK1_UV, "BUCK1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_BUCK1_SC, "BUCK1", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -205,7 +266,7 @@ static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = {
{ TPS6594_IRQ_NAME_BUCK2_OV, "BUCK2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_BUCK2_UV, "BUCK2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_BUCK2_SC, "BUCK2", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -213,7 +274,7 @@ static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = {
{ TPS6594_IRQ_NAME_BUCK3_OV, "BUCK3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_BUCK3_UV, "BUCK3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_BUCK3_SC, "BUCK3", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -221,7 +282,7 @@ static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = {
{ TPS6594_IRQ_NAME_BUCK4_OV, "BUCK4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_BUCK4_UV, "BUCK4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_BUCK4_SC, "BUCK4", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -229,7 +290,7 @@ static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = {
{ TPS6594_IRQ_NAME_BUCK5_OV, "BUCK5", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_BUCK5_UV, "BUCK5", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_BUCK5_SC, "BUCK5", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -237,7 +298,7 @@ static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = {
{ TPS6594_IRQ_NAME_LDO1_OV, "LDO1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_LDO1_UV, "LDO1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_LDO1_SC, "LDO1", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -245,7 +306,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = {
{ TPS6594_IRQ_NAME_LDO2_OV, "LDO2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_LDO2_UV, "LDO2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_LDO2_SC, "LDO2", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -253,7 +314,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = {
{ TPS6594_IRQ_NAME_LDO3_OV, "LDO3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_LDO3_UV, "LDO3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_LDO3_SC, "LDO3", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -261,7 +322,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = {
{ TPS6594_IRQ_NAME_LDO4_OV, "LDO4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN },
{ TPS6594_IRQ_NAME_LDO4_UV, "LDO4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE },
{ TPS6594_IRQ_NAME_LDO4_SC, "LDO4", "short circuit", REGULATOR_EVENT_REGULATION_OUT },
@@ -269,7 +330,42 @@ static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = {
REGULATOR_EVENT_OVER_CURRENT },
};
-static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = {
+static const struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = {
+ { TPS65224_IRQ_NAME_BUCK1_UVOV, "BUCK1", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = {
+ { TPS65224_IRQ_NAME_BUCK2_UVOV, "BUCK2", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = {
+ { TPS65224_IRQ_NAME_BUCK3_UVOV, "BUCK3", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = {
+ { TPS65224_IRQ_NAME_BUCK4_UVOV, "BUCK4", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = {
+ { TPS65224_IRQ_NAME_LDO1_UVOV, "LDO1", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = {
+ { TPS65224_IRQ_NAME_LDO2_UVOV, "LDO2", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = {
+ { TPS65224_IRQ_NAME_LDO3_UVOV, "LDO3", "voltage out of range",
+ REGULATOR_EVENT_REGULATION_OUT },
+};
+
+static const struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = {
tps6594_buck1_irq_types,
tps6594_buck2_irq_types,
tps6594_buck3_irq_types,
@@ -277,14 +373,27 @@ static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = {
tps6594_buck5_irq_types,
};
-static struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = {
+static const struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = {
tps6594_ldo1_irq_types,
tps6594_ldo2_irq_types,
tps6594_ldo3_irq_types,
tps6594_ldo4_irq_types,
};
-static const struct regulator_desc multi_regs[] = {
+static const struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = {
+ tps65224_buck1_irq_types,
+ tps65224_buck2_irq_types,
+ tps65224_buck3_irq_types,
+ tps65224_buck4_irq_types,
+};
+
+static const struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = {
+ tps65224_ldo1_irq_types,
+ tps65224_ldo2_irq_types,
+ tps65224_ldo3_irq_types,
+};
+
+static const struct regulator_desc tps6594_multi_regs[] = {
TPS6594_REGULATOR("BUCK12", "buck12", TPS6594_BUCK_1,
REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS6594_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_VOUT_1(0),
@@ -315,7 +424,17 @@ static const struct regulator_desc multi_regs[] = {
4, 4000, 0, NULL, 0, 0),
};
-static const struct regulator_desc ldo_regs[] = {
+static const struct regulator_desc tps65224_multi_regs[] = {
+ TPS6594_REGULATOR("BUCK12", "buck12", TPS6594_BUCK_1,
+ REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCK1_VSET,
+ TPS6594_REG_BUCKX_VOUT_1(0),
+ TPS65224_MASK_BUCK1_VSET,
+ TPS6594_REG_BUCKX_CTRL(0),
+ TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_1_ranges,
+ 4, 4000, 0, NULL, 0, 0),
+};
+
+static const struct regulator_desc tps6594_ldo_regs[] = {
TPS6594_REGULATOR("LDO1", "ldo1", TPS6594_LDO_1,
REGULATOR_VOLTAGE, tps6594_ldos_1_2_3_ops, TPS6594_MASK_LDO123_VSET,
TPS6594_REG_LDOX_VOUT(0),
@@ -346,6 +465,30 @@ static const struct regulator_desc ldo_regs[] = {
1, 0, 0, NULL, 0, 0),
};
+static const struct regulator_desc tps65224_ldo_regs[] = {
+ TPS6594_REGULATOR("LDO1", "ldo1", TPS6594_LDO_1,
+ REGULATOR_VOLTAGE, tps6594_ldos_1_2_3_ops, TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_VOUT(0),
+ TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_CTRL(0),
+ TPS6594_BIT_LDO_EN, 0, 0, tps65224_ldos_1_ranges,
+ 1, 0, 0, NULL, 0, TPS6594_BIT_LDO_BYPASS),
+ TPS6594_REGULATOR("LDO2", "ldo2", TPS6594_LDO_2,
+ REGULATOR_VOLTAGE, tps6594_ldos_1_2_3_ops, TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_VOUT(1),
+ TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_CTRL(1),
+ TPS6594_BIT_LDO_EN, 0, 0, tps65224_ldos_2_3_ranges,
+ 1, 0, 0, NULL, 0, TPS6594_BIT_LDO_BYPASS),
+ TPS6594_REGULATOR("LDO3", "ldo3", TPS6594_LDO_3,
+ REGULATOR_VOLTAGE, tps6594_ldos_1_2_3_ops, TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_VOUT(2),
+ TPS6594_MASK_LDO123_VSET,
+ TPS6594_REG_LDOX_CTRL(2),
+ TPS6594_BIT_LDO_EN, 0, 0, tps65224_ldos_2_3_ranges,
+ 1, 0, 0, NULL, 0, TPS6594_BIT_LDO_BYPASS),
+};
+
static irqreturn_t tps6594_regulator_irq_handler(int irq, void *data)
{
struct tps6594_regulator_irq_data *irq_data = data;
@@ -369,17 +512,18 @@ static irqreturn_t tps6594_regulator_irq_handler(int irq, void *data)
static int tps6594_request_reg_irqs(struct platform_device *pdev,
struct regulator_dev *rdev,
struct tps6594_regulator_irq_data *irq_data,
- struct tps6594_regulator_irq_type *tps6594_regs_irq_types,
+ const struct tps6594_regulator_irq_type *regs_irq_types,
+ size_t interrupt_cnt,
int *irq_idx)
{
- struct tps6594_regulator_irq_type *irq_type;
+ const struct tps6594_regulator_irq_type *irq_type;
struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
- int j;
+ size_t j;
int irq;
int error;
- for (j = 0; j < REGS_INT_NB; j++) {
- irq_type = &tps6594_regs_irq_types[j];
+ for (j = 0; j < interrupt_cnt; j++) {
+ irq_type = ®s_irq_types[j];
irq = platform_get_irq_byname(pdev, irq_type->irq_name);
if (irq < 0)
return -EINVAL;
@@ -401,6 +545,63 @@ static int tps6594_request_reg_irqs(struct platform_device *pdev,
return 0;
}
+struct tps6594_regulator_desc {
+ const struct regulator_desc *multi_phase_regs;
+ unsigned int num_multi_phase_regs;
+
+ const struct regulator_desc *buck_regs;
+ int num_buck_regs;
+
+ const struct regulator_desc *ldo_regs;
+ int num_ldo_regs;
+
+ const struct tps6594_regulator_irq_type **bucks_irq_types;
+ const struct tps6594_regulator_irq_type **ldos_irq_types;
+ int num_irq_types;
+
+ const struct tps6594_regulator_irq_type *ext_irq_types;
+ int num_ext_irqs;
+};
+
+static const struct tps6594_regulator_desc tps65224_reg_desc = {
+ .multi_phase_regs = tps65224_multi_regs,
+ .num_multi_phase_regs = ARRAY_SIZE(tps65224_multi_regs),
+ .buck_regs = tps65224_buck_regs,
+ .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs),
+ .ldo_regs = tps65224_ldo_regs,
+ .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs),
+ .bucks_irq_types = tps65224_bucks_irq_types,
+ .ldos_irq_types = tps65224_ldos_irq_types,
+ .num_irq_types = 1, /* OV or UV */
+ .ext_irq_types = tps65224_ext_regulator_irq_types,
+ .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types),
+};
+
+static const struct tps6594_regulator_desc tps6594_reg_desc = {
+ .multi_phase_regs = tps6594_multi_regs,
+ .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs),
+ .buck_regs = tps6594_buck_regs,
+ .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs),
+ .ldo_regs = tps6594_ldo_regs,
+ .num_ldo_regs = ARRAY_SIZE(tps6594_ldo_regs),
+ .bucks_irq_types = tps6594_bucks_irq_types,
+ .ldos_irq_types = tps6594_ldos_irq_types,
+ .num_irq_types = 4, /* OV, UV, SC and ILIM */
+ .ext_irq_types = tps6594_ext_regulator_irq_types,
+ .num_ext_irqs = 2, /* only VCCA OV and UV */
+};
+
+static const struct tps6594_regulator_desc lp8764_reg_desc = {
+ .multi_phase_regs = tps6594_multi_regs,
+ .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs),
+ .buck_regs = tps6594_buck_regs,
+ .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs),
+ .bucks_irq_types = tps6594_bucks_irq_types,
+ .num_irq_types = 4, /* OV, UV, SC and ILIM */
+ .ext_irq_types = tps6594_ext_regulator_irq_types,
+ .num_ext_irqs = ARRAY_SIZE(tps6594_ext_regulator_irq_types),
+};
+
static int tps6594_regulator_probe(struct platform_device *pdev)
{
struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
@@ -410,24 +611,39 @@ static int tps6594_regulator_probe(struct platform_device *pdev)
struct regulator_config config = {};
struct tps6594_regulator_irq_data *irq_data;
struct tps6594_ext_regulator_irq_data *irq_ext_reg_data;
- struct tps6594_regulator_irq_type *irq_type;
- u8 buck_configured[BUCK_NB] = { 0 };
- u8 buck_multi[MULTI_PHASE_NB] = { 0 };
- static const char * const multiphases[] = {"buck12", "buck123", "buck1234", "buck34"};
+ const struct tps6594_regulator_irq_type *irq_type;
+ bool buck_configured[BUCK_NB] = { false };
+ bool buck_multi[MULTI_PHASE_NB] = { false };
+ const struct tps6594_regulator_desc *desc;
+ const struct regulator_desc *multi_regs;
+
static const char *npname;
- int error, i, irq, multi, delta;
+ int error, i, irq, multi;
int irq_idx = 0;
int buck_idx = 0;
- size_t ext_reg_irq_nb = 2;
size_t reg_irq_nb;
+
+ switch (tps->chip_id) {
+ case TPS65224:
+ desc = &tps65224_reg_desc;
+ break;
+ case TPS6594:
+ case TPS6593:
+ desc = &tps6594_reg_desc;
+ break;
+ case LP8764:
+ desc = &lp8764_reg_desc;
+ break;
+ default:
+ dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id);
+ return -EINVAL;
+ };
+
enum {
MULTI_BUCK12,
+ MULTI_BUCK12_34,
MULTI_BUCK123,
MULTI_BUCK1234,
- MULTI_BUCK12_34,
- MULTI_FIRST = MULTI_BUCK12,
- MULTI_LAST = MULTI_BUCK12_34,
- MULTI_NUM = MULTI_LAST - MULTI_FIRST + 1
};
config.dev = tps->dev;
@@ -442,143 +658,148 @@ static int tps6594_regulator_probe(struct platform_device *pdev)
* In case of Multiphase configuration, value should be defined for
* buck_configured to avoid creating bucks for every buck in multiphase
*/
- for (multi = MULTI_FIRST; multi < MULTI_NUM; multi++) {
- np = of_find_node_by_name(tps->dev->of_node, multiphases[multi]);
- npname = of_node_full_name(np);
- np_pmic_parent = of_get_parent(of_get_parent(np));
- if (of_node_cmp(of_node_full_name(np_pmic_parent), tps->dev->of_node->full_name))
+ for (multi = 0; multi < desc->num_multi_phase_regs; multi++) {
+ struct device_node *np_parent;
+
+ multi_regs = &desc->multi_phase_regs[multi];
+ np = of_find_node_by_name(tps->dev->of_node, multi_regs->supply_name);
+ if (!np)
continue;
- delta = strcmp(npname, multiphases[multi]);
- if (!delta) {
+
+ npname = of_node_full_name(np);
+ np_parent = of_get_parent(np);
+ np_pmic_parent = of_get_parent(np_parent);
+
+ if (np_pmic_parent &&
+ !of_node_cmp(of_node_full_name(np_pmic_parent), tps->dev->of_node->full_name) &&
+ strcmp(npname, multi_regs->supply_name) == 0) {
switch (multi) {
case MULTI_BUCK12:
- buck_multi[0] = 1;
- buck_configured[0] = 1;
- buck_configured[1] = 1;
+ buck_multi[0] = true;
+ buck_configured[0] = true;
+ buck_configured[1] = true;
break;
/* multiphase buck34 is supported only with buck12 */
case MULTI_BUCK12_34:
- buck_multi[0] = 1;
- buck_multi[1] = 1;
- buck_configured[0] = 1;
- buck_configured[1] = 1;
- buck_configured[2] = 1;
- buck_configured[3] = 1;
+ buck_multi[0] = true;
+ buck_multi[1] = true;
+ buck_configured[0] = true;
+ buck_configured[1] = true;
+ buck_configured[2] = true;
+ buck_configured[3] = true;
break;
case MULTI_BUCK123:
- buck_multi[2] = 1;
- buck_configured[0] = 1;
- buck_configured[1] = 1;
- buck_configured[2] = 1;
+ buck_multi[2] = true;
+ buck_configured[0] = true;
+ buck_configured[1] = true;
+ buck_configured[2] = true;
break;
case MULTI_BUCK1234:
- buck_multi[3] = 1;
- buck_configured[0] = 1;
- buck_configured[1] = 1;
- buck_configured[2] = 1;
- buck_configured[3] = 1;
+ buck_multi[3] = true;
+ buck_configured[0] = true;
+ buck_configured[1] = true;
+ buck_configured[2] = true;
+ buck_configured[3] = true;
break;
}
}
- }
- if (tps->chip_id == LP8764) {
- /* There is only 4 buck on LP8764 */
- buck_configured[4] = 1;
- reg_irq_nb = size_mul(REGS_INT_NB, (BUCK_NB - 1));
- } else {
- reg_irq_nb = size_mul(REGS_INT_NB, (size_add(BUCK_NB, LDO_NB)));
+ of_node_put(np_pmic_parent);
+ of_node_put(np_parent);
+ of_node_put(np);
}
+ reg_irq_nb = desc->num_irq_types * (desc->num_buck_regs + desc->num_ldo_regs);
+
irq_data = devm_kmalloc_array(tps->dev, reg_irq_nb,
sizeof(struct tps6594_regulator_irq_data), GFP_KERNEL);
if (!irq_data)
return -ENOMEM;
- for (i = 0; i < MULTI_PHASE_NB; i++) {
- if (buck_multi[i] == 0)
+ for (i = 0; i < desc->num_multi_phase_regs; i++) {
+ if (!buck_multi[i])
continue;
- rdev = devm_regulator_register(&pdev->dev, &multi_regs[i], &config);
+ rdev = devm_regulator_register(&pdev->dev, &desc->multi_phase_regs[i],
+ &config);
if (IS_ERR(rdev))
return dev_err_probe(tps->dev, PTR_ERR(rdev),
"failed to register %s regulator\n",
pdev->name);
/* config multiphase buck12+buck34 */
- if (i == 1)
+ if (i == MULTI_BUCK12_34)
buck_idx = 2;
+
error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_bucks_irq_types[buck_idx], &irq_idx);
+ desc->bucks_irq_types[buck_idx],
+ desc->num_irq_types, &irq_idx);
if (error)
return error;
+
error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_bucks_irq_types[buck_idx + 1], &irq_idx);
+ desc->bucks_irq_types[buck_idx + 1],
+ desc->num_irq_types, &irq_idx);
if (error)
return error;
- if (i == 2 || i == 3) {
+ if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) {
error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_bucks_irq_types[buck_idx + 2],
+ desc->bucks_irq_types[buck_idx + 2],
+ desc->num_irq_types,
&irq_idx);
if (error)
return error;
}
- if (i == 3) {
+ if (i == MULTI_BUCK1234) {
error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_bucks_irq_types[buck_idx + 3],
+ desc->bucks_irq_types[buck_idx + 3],
+ desc->num_irq_types,
&irq_idx);
if (error)
return error;
}
}
- for (i = 0; i < BUCK_NB; i++) {
- if (buck_configured[i] == 1)
+ for (i = 0; i < desc->num_buck_regs; i++) {
+ if (buck_configured[i])
continue;
- rdev = devm_regulator_register(&pdev->dev, &buck_regs[i], &config);
+ rdev = devm_regulator_register(&pdev->dev, &desc->buck_regs[i], &config);
if (IS_ERR(rdev))
return dev_err_probe(tps->dev, PTR_ERR(rdev),
- "failed to register %s regulator\n",
- pdev->name);
+ "failed to register %s regulator\n", pdev->name);
error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_bucks_irq_types[i], &irq_idx);
+ desc->bucks_irq_types[i],
+ desc->num_irq_types, &irq_idx);
if (error)
return error;
}
- /* LP8764 dosen't have LDO */
- if (tps->chip_id != LP8764) {
- for (i = 0; i < ARRAY_SIZE(ldo_regs); i++) {
- rdev = devm_regulator_register(&pdev->dev, &ldo_regs[i], &config);
- if (IS_ERR(rdev))
- return dev_err_probe(tps->dev, PTR_ERR(rdev),
- "failed to register %s regulator\n",
- pdev->name);
+ for (i = 0; i < desc->num_ldo_regs; i++) {
+ rdev = devm_regulator_register(&pdev->dev, &desc->ldo_regs[i], &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(tps->dev, PTR_ERR(rdev),
+ "failed to register %s regulator\n",
+ pdev->name);
- error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
- tps6594_ldos_irq_types[i],
- &irq_idx);
- if (error)
- return error;
- }
+ error = tps6594_request_reg_irqs(pdev, rdev, irq_data,
+ desc->ldos_irq_types[i],
+ desc->num_irq_types, &irq_idx);
+ if (error)
+ return error;
}
- if (tps->chip_id == LP8764)
- ext_reg_irq_nb = ARRAY_SIZE(tps6594_ext_regulator_irq_types);
-
irq_ext_reg_data = devm_kmalloc_array(tps->dev,
- ext_reg_irq_nb,
- sizeof(struct tps6594_ext_regulator_irq_data),
- GFP_KERNEL);
+ desc->num_ext_irqs,
+ sizeof(struct tps6594_ext_regulator_irq_data),
+ GFP_KERNEL);
if (!irq_ext_reg_data)
return -ENOMEM;
- for (i = 0; i < ext_reg_irq_nb; ++i) {
- irq_type = &tps6594_ext_regulator_irq_types[i];
-
+ for (i = 0; i < desc->num_ext_irqs; ++i) {
+ irq_type = &desc->ext_irq_types[i];
irq = platform_get_irq_byname(pdev, irq_type->irq_name);
if (irq < 0)
return -EINVAL;
@@ -596,6 +817,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev)
"failed to request %s IRQ %d\n",
irq_type->irq_name, irq);
}
+
return 0;
}
@@ -610,5 +832,6 @@ module_platform_driver(tps6594_regulator_driver);
MODULE_ALIAS("platform:tps6594-regulator");
MODULE_AUTHOR("Jerome Neanne <jneanne@baylibre.com>");
+MODULE_AUTHOR("Nirmala Devi Mal Nadar <m.nirmaladevi@ltts.com>");
MODULE_DESCRIPTION("TPS6594 voltage regulator driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index bf7a4b975e7e..103ee9d60ca3 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *scp_node;
struct platform_device *scp_pdev;
+ struct mtk_scp *scp;
scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0);
if (!scp_node) {
@@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_device *pdev)
return NULL;
}
- return platform_get_drvdata(scp_pdev);
+ scp = platform_get_drvdata(scp_pdev);
+ if (!scp) {
+ put_device(&scp_pdev->dev);
+ return NULL;
+ }
+
+ return scp;
}
EXPORT_SYMBOL_GPL(scp_get);
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index e5b1f0e66019..301cd1672370 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -240,8 +240,10 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
return;
glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
- if (!glink->ssr_name)
+ if (!glink->ssr_name) {
+ of_node_put(glink->node);
return;
+ }
glink->dev = dev;
glink->subdev.start = glink_subdev_start;
diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
index 6c67514cc493..00a98de6d112 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -362,6 +362,7 @@ static int adsp_map_carveout(struct rproc *rproc)
return ret;
sid = args.args[0] & SID_MASK_DEFAULT;
+ of_node_put(args.np);
/* Add SID configuration for ADSP Firmware to SMMU */
iova = adsp->mem_phys | (sid << 32);
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 2d4ae3b5af86..1e4bae2786db 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1015,60 +1015,55 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
[RSC_VDEV] = rproc_handle_vdev,
};
-/* handle firmware resource entries before booting the remote processor */
-static int rproc_handle_resources(struct rproc *rproc,
- rproc_handle_resource_t handlers[RSC_LAST])
+struct rproc_rsc_cb_data {
+ struct rproc *rproc;
+ rproc_handle_resource_t *handlers;
+};
+
+static int rproc_handle_rsc_entry(u32 type, void *rsc, int offset,
+ int avail, void *data)
{
+ struct rproc_rsc_cb_data *d = data;
+ struct rproc *rproc = d->rproc;
struct device *dev = &rproc->dev;
rproc_handle_resource_t handler;
- int ret = 0, i;
-
- if (!rproc->table_ptr)
- return 0;
-
- for (i = 0; i < rproc->table_ptr->num; i++) {
- int offset = rproc->table_ptr->offset[i];
- struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
- int avail = rproc->table_sz - offset - sizeof(*hdr);
- void *rsc = (void *)hdr + sizeof(*hdr);
+ int ret;
- /* make sure table isn't truncated */
- if (avail < 0) {
- dev_err(dev, "rsc table is truncated\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "rsc: type %d\n", type);
- dev_dbg(dev, "rsc: type %d\n", hdr->type);
+ if (type >= RSC_VENDOR_START && type <= RSC_VENDOR_END) {
+ ret = rproc_handle_rsc(rproc, type, rsc, offset, avail);
+ if (ret == RSC_HANDLED)
+ return 0;
+ if (ret < 0)
+ return ret;
+ dev_warn(dev, "unsupported vendor resource %d\n", type);
+ return 0;
+ }
- if (hdr->type >= RSC_VENDOR_START &&
- hdr->type <= RSC_VENDOR_END) {
- ret = rproc_handle_rsc(rproc, hdr->type, rsc,
- offset + sizeof(*hdr), avail);
- if (ret == RSC_HANDLED)
- continue;
- else if (ret < 0)
- break;
+ if (type >= RSC_LAST) {
+ dev_warn(dev, "unsupported resource %d\n", type);
+ return 0;
+ }
- dev_warn(dev, "unsupported vendor resource %d\n",
- hdr->type);
- continue;
- }
+ handler = d->handlers[type];
+ if (!handler)
+ return 0;
- if (hdr->type >= RSC_LAST) {
- dev_warn(dev, "unsupported resource %d\n", hdr->type);
- continue;
- }
+ return handler(rproc, rsc, offset, avail);
+}
- handler = handlers[hdr->type];
- if (!handler)
- continue;
+/* handle firmware resource entries before booting the remote processor */
+static int rproc_handle_resources(struct rproc *rproc,
+ rproc_handle_resource_t handlers[RSC_LAST])
+{
+ struct rproc_rsc_cb_data d = { .rproc = rproc, .handlers = handlers };
- ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
- if (ret)
- break;
- }
+ if (!rproc->table_ptr)
+ return 0;
- return ret;
+ return rsc_table_for_each_entry(rproc->table_ptr, rproc->table_sz,
+ &rproc->dev, rproc_handle_rsc_entry, &d);
}
static int rproc_prepare_subdevices(struct rproc *rproc)
@@ -1677,18 +1672,21 @@ static void rproc_auto_boot_callback(const struct firmware *fw, void *context)
release_firmware(fw);
}
+static void rproc_attach_work(struct work_struct *work)
+{
+ struct rproc *rproc = container_of(work, struct rproc, attach_work);
+
+ rproc_boot(rproc);
+}
+
static int rproc_trigger_auto_boot(struct rproc *rproc)
{
int ret;
- /*
- * Since the remote processor is in a detached state, it has already
- * been booted by another entity. As such there is no point in waiting
- * for a firmware image to be loaded, we can simply initiate the process
- * of attaching to it immediately.
- */
- if (rproc->state == RPROC_DETACHED)
- return rproc_boot(rproc);
+ if (rproc->state == RPROC_DETACHED) {
+ schedule_work(&rproc->attach_work);
+ return 0;
+ }
/*
* We're initiating an asynchronous firmware loading, so we can
@@ -1837,6 +1835,11 @@ int rproc_trigger_recovery(struct rproc *rproc)
if (ret)
return ret;
+ if (READ_ONCE(rproc->deleting)) {
+ ret = -ENODEV;
+ goto unlock_mutex;
+ }
+
/* State could have changed before we got the mutex */
if (rproc->state != RPROC_CRASHED)
goto unlock_mutex;
@@ -1869,6 +1872,11 @@ static void rproc_crash_handler_work(struct work_struct *work)
mutex_lock(&rproc->lock);
+ if (READ_ONCE(rproc->deleting)) {
+ mutex_unlock(&rproc->lock);
+ goto out;
+ }
+
if (rproc->state == RPROC_CRASHED) {
/* handle only the first crash detected */
mutex_unlock(&rproc->lock);
@@ -1924,9 +1932,9 @@ int rproc_boot(struct rproc *rproc)
return ret;
}
- if (rproc->state == RPROC_DELETED) {
+ if (READ_ONCE(rproc->deleting)) {
ret = -ENODEV;
- dev_err(dev, "can't boot deleted rproc %s\n", rproc->name);
+ dev_err(dev, "can't boot deleting rproc %s\n", rproc->name);
goto unlock_mutex;
}
@@ -1988,7 +1996,8 @@ EXPORT_SYMBOL(rproc_boot);
int rproc_shutdown(struct rproc *rproc)
{
struct device *dev = &rproc->dev;
- int ret = 0;
+ bool crashed;
+ int ret;
ret = mutex_lock_interruptible(&rproc->lock);
if (ret) {
@@ -1997,16 +2006,18 @@ int rproc_shutdown(struct rproc *rproc)
}
if (rproc->state != RPROC_RUNNING &&
- rproc->state != RPROC_ATTACHED) {
+ rproc->state != RPROC_ATTACHED &&
+ rproc->state != RPROC_CRASHED) {
ret = -EINVAL;
goto out;
}
+ crashed = rproc->state == RPROC_CRASHED;
/* if the remote proc is still needed, bail out */
if (!atomic_dec_and_test(&rproc->power))
goto out;
- ret = rproc_stop(rproc, false);
+ ret = rproc_stop(rproc, crashed);
if (ret) {
atomic_inc(&rproc->power);
goto out;
@@ -2496,7 +2507,9 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->subdevs);
INIT_LIST_HEAD(&rproc->dump_segments);
+ INIT_WORK(&rproc->attach_work, rproc_attach_work);
INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
+ spin_lock_init(&rproc->crash_handler_lock);
rproc->state = RPROC_OFFLINE;
@@ -2556,16 +2569,21 @@ EXPORT_SYMBOL(rproc_put);
*/
int rproc_del(struct rproc *rproc)
{
+ unsigned long flags;
+
if (!rproc)
return -EINVAL;
+ spin_lock_irqsave(&rproc->crash_handler_lock, flags);
+ WRITE_ONCE(rproc->deleting, true);
+ spin_unlock_irqrestore(&rproc->crash_handler_lock, flags);
+
+ if (cancel_work_sync(&rproc->crash_handler))
+ pm_relax(rproc->dev.parent);
+
/* TODO: make sure this works with rproc->power > 1 */
rproc_shutdown(rproc);
- mutex_lock(&rproc->lock);
- rproc->state = RPROC_DELETED;
- mutex_unlock(&rproc->lock);
-
rproc_delete_debug_dir(rproc);
/* the rproc is downref'ed as soon as it's removed from the klist */
@@ -2677,18 +2695,26 @@ EXPORT_SYMBOL(rproc_get_by_child);
*/
void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
{
+ unsigned long flags;
+
if (!rproc) {
pr_err("NULL rproc pointer\n");
return;
}
+ spin_lock_irqsave(&rproc->crash_handler_lock, flags);
+ if (READ_ONCE(rproc->deleting)) {
+ spin_unlock_irqrestore(&rproc->crash_handler_lock, flags);
+ return;
+ }
+
/* Prevent suspend while the remoteproc is being recovered */
pm_stay_awake(rproc->dev.parent);
+ queue_work(rproc_recovery_wq, &rproc->crash_handler);
+ spin_unlock_irqrestore(&rproc->crash_handler_lock, flags);
dev_err(&rproc->dev, "crash detected in %s: type %s\n",
rproc->name, rproc_crash_to_string(type));
-
- queue_work(rproc_recovery_wq, &rproc->crash_handler);
}
EXPORT_SYMBOL(rproc_report_crash);
diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c
index 8c7ea8922638..23de0490ba16 100644
--- a/drivers/remoteproc/remoteproc_sysfs.c
+++ b/drivers/remoteproc/remoteproc_sysfs.c
@@ -168,7 +168,6 @@ static const char * const rproc_state_string[] = {
[RPROC_SUSPENDED] = "suspended",
[RPROC_RUNNING] = "running",
[RPROC_CRASHED] = "crashed",
- [RPROC_DELETED] = "deleted",
[RPROC_ATTACHED] = "attached",
[RPROC_DETACHED] = "detached",
[RPROC_LAST] = "invalid",
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index f5909e454893..cc30cae95b11 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -1313,26 +1313,28 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev)
return 0;
}
-static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
+static void qcom_glink_remove_rpmsg_device(struct qcom_glink *glink, struct glink_channel *channel)
{
- struct glink_channel *channel = to_glink_channel(ept);
- struct qcom_glink *glink = channel->glink;
struct rpmsg_channel_info chinfo;
- unsigned long flags;
-
- spin_lock_irqsave(&channel->recv_lock, flags);
- channel->ept.cb = NULL;
- spin_unlock_irqrestore(&channel->recv_lock, flags);
- /* Decouple the potential rpdev from the channel */
if (channel->rpdev) {
strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));
chinfo.src = RPMSG_ADDR_ANY;
chinfo.dst = RPMSG_ADDR_ANY;
-
rpmsg_unregister_device(glink->dev, &chinfo);
}
channel->rpdev = NULL;
+}
+
+static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ struct glink_channel *channel = to_glink_channel(ept);
+ struct qcom_glink *glink = channel->glink;
+ unsigned long flags;
+
+ spin_lock_irqsave(&channel->recv_lock, flags);
+ channel->ept.cb = NULL;
+ spin_unlock_irqrestore(&channel->recv_lock, flags);
qcom_glink_send_close_req(glink, channel);
}
@@ -1610,7 +1612,6 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
{
- struct rpmsg_channel_info chinfo;
struct glink_channel *channel;
unsigned long flags;
@@ -1623,14 +1624,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
/* cancel pending rx_done work */
cancel_work_sync(&channel->intent_work);
- if (channel->rpdev) {
- strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));
- chinfo.src = RPMSG_ADDR_ANY;
- chinfo.dst = RPMSG_ADDR_ANY;
-
- rpmsg_unregister_device(glink->dev, &chinfo);
- }
- channel->rpdev = NULL;
+ qcom_glink_remove_rpmsg_device(glink, channel);
qcom_glink_send_close_ack(glink, channel->rcid);
@@ -1644,7 +1638,6 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
{
- struct rpmsg_channel_info chinfo;
struct glink_channel *channel;
unsigned long flags;
@@ -1663,14 +1656,7 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
spin_unlock_irqrestore(&glink->idr_lock, flags);
/* Decouple the potential rpdev from the channel */
- if (channel->rpdev) {
- strscpy(chinfo.name, channel->name, sizeof(chinfo.name));
- chinfo.src = RPMSG_ADDR_ANY;
- chinfo.dst = RPMSG_ADDR_ANY;
-
- rpmsg_unregister_device(glink->dev, &chinfo);
- }
- channel->rpdev = NULL;
+ qcom_glink_remove_rpmsg_device(glink, channel);
kref_put(&channel->refcount, qcom_glink_channel_release);
}
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 7a982c60a8dd..17c885f4ed50 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcom_glink_pipe *np,
if (tail >= pipe->native.length)
tail -= pipe->native.length;
+ /*
+ * Order the availability (head) read in glink_smem_rx_avail()
+ * against the FIFO payload read below, so APPS never consumes
+ * stale data the remote has not yet published.
+ */
+ rmb();
+
len = min_t(size_t, count, pipe->native.length - tail);
if (len)
memcpy_fromio(data, pipe->fifo + tail, len);
diff --git a/drivers/rtc/rtc-gamecube.c b/drivers/rtc/rtc-gamecube.c
index 045d5d45ab4b..889028cecf4a 100644
--- a/drivers/rtc/rtc-gamecube.c
+++ b/drivers/rtc/rtc-gamecube.c
@@ -355,9 +355,7 @@ static int gamecube_rtc_probe(struct platform_device *pdev)
rtc->range_max = U32_MAX;
rtc->ops = &gamecube_rtc_ops;
- devm_rtc_register_device(rtc);
-
- return 0;
+ return devm_rtc_register_device(rtc);
}
static const struct of_device_id gamecube_rtc_of_match[] = {
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index 540042b9eec8..ccc7834e5759 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -426,8 +426,8 @@ static int pcf85363_probe(struct i2c_client *client)
err = pcf85363_load_capacitance(pcf85363, client->dev.of_node);
if (err < 0)
- dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
- err);
+ return dev_err_probe(&client->dev, err,
+ "failed to set xtal load capacitance\n");
pcf85363->rtc->ops = &rtc_ops;
pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
@@ -436,9 +436,16 @@ static int pcf85363_probe(struct i2c_client *client)
wakeup_source = device_property_read_bool(&client->dev,
"wakeup-source");
if (client->irq > 0 || wakeup_source) {
- regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
- regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
- PIN_IO_INTAPM, PIN_IO_INTA_OUT);
+ err = regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
+ if (err)
+ return dev_err_probe(&client->dev, err,
+ "failed to clear flags\n");
+
+ err = regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
+ PIN_IO_INTAPM, PIN_IO_INTA_OUT);
+ if (err)
+ return dev_err_probe(&client->dev, err,
+ "failed to set interrupt pin mode\n");
}
if (client->irq > 0) {
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 61be91c25cf1..f524e8c1df18 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -494,7 +494,9 @@ static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw);
if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ devm_of_clk_add_hw_provider(pcf8563->rtc->dev.parent,
+ of_clk_hw_simple_get,
+ &pcf8563->clkout_hw);
return clk;
}
diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c
index 8570c8e63d70..c64eaf50bb39 100644
--- a/drivers/rtc/rtc-rzn1.c
+++ b/drivers/rtc/rtc-rzn1.c
@@ -219,7 +219,6 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time, tm_now;
unsigned long alarm, farest;
- unsigned int days_ahead, wday;
int ret;
ret = rzn1_rtc_read_time(dev, &tm_now);
@@ -232,13 +231,14 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
if (time_after(alarm, farest))
return -ERANGE;
- /* Convert alarm day into week day */
- days_ahead = tm->tm_mday - tm_now.tm_mday;
- wday = (tm_now.tm_wday + days_ahead) % 7;
+ /* Disable alarm interrupts before reprogramming the alarm. */
+ ret = rzn1_rtc_alarm_irq_enable(dev, 0);
+ if (ret)
+ return ret;
writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM);
writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH);
- writel(BIT(wday), rtc->base + RZN1_RTC_ALW);
+ writel(BIT(tm->tm_wday), rtc->base + RZN1_RTC_ALW);
rzn1_rtc_alarm_irq_enable(dev, alrm->enabled);
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index f49af7d963fb..e77fb1433401 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -320,10 +320,9 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
/* Getting the rtc info */
xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc");
- if (IS_ERR(xrtcdev->rtc_clk)) {
- if (PTR_ERR(xrtcdev->rtc_clk) != -EPROBE_DEFER)
- dev_warn(&pdev->dev, "Device clock not found.\n");
- }
+ if (IS_ERR(xrtcdev->rtc_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(xrtcdev->rtc_clk),
+ "Failed to get rtc clock\n");
xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk);
if (!xrtcdev->freq) {
ret = of_property_read_u32(pdev->dev.of_node, "calibration",
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 0bd880f5475b..21c5a0dd6fb0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1737,8 +1737,10 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
if (rq_data_dir(req) == READ) {
- device->discipline->ese_read(cqr, irb);
- cqr->status = DASD_CQR_SUCCESS;
+ if (device->discipline->ese_read(cqr, irb))
+ cqr->status = DASD_CQR_ERROR;
+ else
+ cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now;
dasd_device_clear_timer(device);
dasd_schedule_device_bh(device);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 9e3c4d2de89c..2960caa929db 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1506,6 +1506,8 @@ static void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm)
struct dasd_eckd_private *private = device->private;
unsigned long flags;
+ if (!private)
+ return;
if (!private->fcx_max_data)
private->fcx_max_data = get_fcx_max_data(device);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
@@ -1661,6 +1663,9 @@ static int dasd_eckd_is_ese(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->vsq.vol_info.ese;
}
@@ -1668,6 +1673,9 @@ static int dasd_eckd_ext_pool_id(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->vsq.extent_pool_id;
}
@@ -1681,6 +1689,9 @@ static int dasd_eckd_space_configured(struct dasd_device *device)
struct dasd_eckd_private *private = device->private;
int rc;
+ if (!private)
+ return 0;
+
rc = dasd_eckd_read_vol_info(device);
return rc ? : private->vsq.space_configured;
@@ -1695,6 +1706,9 @@ static int dasd_eckd_space_allocated(struct dasd_device *device)
struct dasd_eckd_private *private = device->private;
int rc;
+ if (!private)
+ return 0;
+
rc = dasd_eckd_read_vol_info(device);
return rc ? : private->vsq.space_allocated;
@@ -1704,6 +1718,9 @@ static int dasd_eckd_logical_capacity(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->vsq.logical_capacity;
}
@@ -1846,7 +1863,11 @@ static int dasd_eckd_read_ext_pool_info(struct dasd_device *device)
static int dasd_eckd_ext_size(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
- struct dasd_ext_pool_sum eps = private->eps;
+ struct dasd_ext_pool_sum eps;
+
+ if (!private)
+ return 0;
+ eps = private->eps;
if (!eps.flags.extent_size_valid)
return 0;
@@ -1862,6 +1883,9 @@ static int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->eps.warn_thrshld;
}
@@ -1869,6 +1893,9 @@ static int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->eps.flags.capacity_at_warnlevel;
}
@@ -1879,6 +1906,9 @@ static int dasd_eckd_ext_pool_oos(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->eps.flags.pool_oos;
}
@@ -5988,8 +6018,11 @@ static int dasd_eckd_query_host_access(struct dasd_device *device,
struct ccw1 *ccw;
int rc;
+ if (!private)
+ return -ENODEV;
+
/* not available for HYPER PAV alias devices */
- if (!device->block && private->lcu->pav == HYPER_PAV)
+ if (!device->block && private->lcu && private->lcu->pav == HYPER_PAV)
return -EOPNOTSUPP;
/* may not be supported by the storage server */
@@ -6854,6 +6887,9 @@ static int dasd_eckd_hpf_enabled(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
+ if (!private)
+ return 0;
+
return private->fcx_max_data ? 1 : 0;
}
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index c07e6e713518..b11cec62f95c 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -127,6 +127,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
int success;
unsigned long startclk, stopclk;
struct dasd_device *startdev;
+ unsigned int proc_bytes;
BUG_ON(cqr->refers == NULL || cqr->function == NULL);
@@ -134,6 +135,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
startclk = cqr->startclk;
stopclk = cqr->stopclk;
startdev = cqr->startdev;
+ proc_bytes = cqr->proc_bytes;
/* free all ERPs - but NOT the original cqr */
while (cqr->refers != NULL) {
@@ -151,6 +153,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
cqr->startclk = startclk;
cqr->stopclk = stopclk;
cqr->startdev = startdev;
+ cqr->proc_bytes = proc_bytes;
if (success)
cqr->status = DASD_CQR_DONE;
else {
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index d4151f519e8b..f82c9d56f07b 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -48,15 +48,19 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q);
* 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
* guest's APCB.
* 2. kvm->lock: required to update a guest's APCB
- * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
+ * 3. kvm->arch.crypto.pqap_hook_rwsem: required to update pqap_hook and
+ * serialize against PQAP intercepts
+ * 4. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
*
- * Note: If @kvm is NULL, the KVM lock will not be taken.
+ * Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be taken.
*/
static inline void get_update_locks_for_kvm(struct kvm *kvm)
{
mutex_lock(&matrix_dev->guests_lock);
- if (kvm)
+ if (kvm) {
mutex_lock(&kvm->lock);
+ down_write(&kvm->arch.crypto.pqap_hook_rwsem);
+ }
mutex_lock(&matrix_dev->mdevs_lock);
}
@@ -68,16 +72,19 @@ static inline void get_update_locks_for_kvm(struct kvm *kvm)
*
* The proper unlocking order is:
* 1. matrix_dev->mdevs_lock
- * 2. kvm->lock
- * 3. matrix_dev->guests_lock
+ * 2. kvm->arch.crypto.pqap_hook_rwsem
+ * 3. kvm->lock
+ * 4. matrix_dev->guests_lock
*
- * Note: If @kvm is NULL, the KVM lock will not be released.
+ * Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be released.
*/
static inline void release_update_locks_for_kvm(struct kvm *kvm)
{
mutex_unlock(&matrix_dev->mdevs_lock);
- if (kvm)
+ if (kvm) {
+ up_write(&kvm->arch.crypto.pqap_hook_rwsem);
mutex_unlock(&kvm->lock);
+ }
mutex_unlock(&matrix_dev->guests_lock);
}
@@ -1609,26 +1616,17 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
struct kvm *kvm)
{
- struct ap_matrix_mdev *m;
-
if (kvm->arch.crypto.crycbd) {
- down_write(&kvm->arch.crypto.pqap_hook_rwsem);
- kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
- up_write(&kvm->arch.crypto.pqap_hook_rwsem);
-
get_update_locks_for_kvm(kvm);
-
- list_for_each_entry(m, &matrix_dev->mdev_list, node) {
- if (m != matrix_mdev && m->kvm == kvm) {
- release_update_locks_for_kvm(kvm);
- return -EPERM;
- }
+ if (kvm->arch.crypto.pqap_hook) {
+ release_update_locks_for_kvm(kvm);
+ return -EPERM;
}
+ kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
kvm_get_kvm(kvm);
matrix_mdev->kvm = kvm;
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
-
release_update_locks_for_kvm(kvm);
}
@@ -1671,18 +1669,15 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
struct kvm *kvm = matrix_mdev->kvm;
if (kvm && kvm->arch.crypto.crycbd) {
- down_write(&kvm->arch.crypto.pqap_hook_rwsem);
- kvm->arch.crypto.pqap_hook = NULL;
- up_write(&kvm->arch.crypto.pqap_hook_rwsem);
-
get_update_locks_for_kvm(kvm);
+ kvm->arch.crypto.pqap_hook = NULL;
kvm_arch_crypto_clear_masks(kvm);
vfio_ap_mdev_reset_queues(matrix_mdev);
- kvm_put_kvm(kvm);
matrix_mdev->kvm = NULL;
release_update_locks_for_kvm(kvm);
+ kvm_put_kvm(kvm);
}
}
@@ -2070,6 +2065,23 @@ static ssize_t status_show(struct device *dev,
mutex_lock(&matrix_dev->mdevs_lock);
q = dev_get_drvdata(&apdev->device);
+
+ /*
+ * Make sure the drvdata has been set before proceeding. There is a
+ * possibility that the drvdata was not set if the vfio_ap_queue object
+ * could not be allocated when the queue device was probed. In that case,
+ * the locks used in vfio_ap_mdev_probe_queue() are released prior to
+ * removing the sysfs status attribute to avoid a lockdep
+ * splat. That opens a very small window where the status attribute is
+ * still available without the vfio_ap_queue object having been
+ * stored in the device drvdata. In that case, indicate the queue is not
+ * assigned.
+ */
+ if (!q) {
+ nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED);
+ goto done;
+ }
+
matrix_mdev = vfio_ap_mdev_for_queue(q);
/* If the queue is assigned to the matrix mediated device, then
@@ -2097,6 +2109,7 @@ static ssize_t status_show(struct device *dev,
AP_QUEUE_UNASSIGNED);
}
+done:
mutex_unlock(&matrix_dev->mdevs_lock);
return nchars;
@@ -2170,14 +2183,17 @@ void vfio_ap_mdev_unregister(void)
int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
{
- int ret;
+ int ret, apqn;
struct vfio_ap_queue *q;
DECLARE_BITMAP(apm_filtered, AP_DEVICES);
struct ap_matrix_mdev *matrix_mdev;
+ apqn = to_ap_queue(&apdev->device)->qid;
+ matrix_mdev = get_update_locks_by_apqn(apqn);
+
ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group);
if (ret)
- return ret;
+ goto err_release_locks;
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (!q) {
@@ -2185,11 +2201,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
goto err_remove_group;
}
- q->apqn = to_ap_queue(&apdev->device)->qid;
+ q->apqn = apqn;
q->saved_isc = VFIO_AP_ISC_INVALID;
memset(&q->reset_status, 0, sizeof(q->reset_status));
INIT_WORK(&q->reset_work, apq_reset_check);
- matrix_mdev = get_update_locks_by_apqn(q->apqn);
if (matrix_mdev) {
vfio_ap_mdev_link_queue(matrix_mdev, q);
@@ -2218,8 +2233,13 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
return ret;
err_remove_group:
+ release_update_locks_for_mdev(matrix_mdev);
sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group);
return ret;
+
+err_release_locks:
+ release_update_locks_for_mdev(matrix_mdev);
+ return ret;
}
void vfio_ap_mdev_remove_queue(struct ap_device *apdev)
@@ -2314,24 +2334,28 @@ static void vfio_ap_mdev_hot_unplug_cfg(struct ap_matrix_mdev *matrix_mdev,
unsigned long *aqrem,
unsigned long *cdrem)
{
- int do_hotplug = 0;
+ bool do_hotplug = false;
- if (!bitmap_empty(aprem, AP_DEVICES)) {
- do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.apm,
- matrix_mdev->shadow_apcb.apm,
- aprem, AP_DEVICES);
+ if (bitmap_intersects(matrix_mdev->shadow_apcb.apm, aprem, AP_DEVICES)) {
+ bitmap_andnot(matrix_mdev->shadow_apcb.apm,
+ matrix_mdev->shadow_apcb.apm,
+ aprem, AP_DEVICES);
+ do_hotplug = true;
}
- if (!bitmap_empty(aqrem, AP_DOMAINS)) {
- do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.aqm,
- matrix_mdev->shadow_apcb.aqm,
- aqrem, AP_DEVICES);
+ if (bitmap_intersects(matrix_mdev->shadow_apcb.aqm, aqrem, AP_DOMAINS)) {
+ bitmap_andnot(matrix_mdev->shadow_apcb.aqm,
+ matrix_mdev->shadow_apcb.aqm,
+ aqrem, AP_DOMAINS);
+ do_hotplug = true;
}
- if (!bitmap_empty(cdrem, AP_DOMAINS))
- do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.adm,
- matrix_mdev->shadow_apcb.adm,
- cdrem, AP_DOMAINS);
+ if (bitmap_intersects(matrix_mdev->shadow_apcb.adm, cdrem, AP_DOMAINS)) {
+ bitmap_andnot(matrix_mdev->shadow_apcb.adm,
+ matrix_mdev->shadow_apcb.adm,
+ cdrem, AP_DOMAINS);
+ do_hotplug = true;
+ }
if (do_hotplug)
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
@@ -2358,28 +2382,41 @@ static void vfio_ap_mdev_cfg_remove(unsigned long *ap_remove,
DECLARE_BITMAP(aprem, AP_DEVICES);
DECLARE_BITMAP(aqrem, AP_DOMAINS);
DECLARE_BITMAP(cdrem, AP_DOMAINS);
- int do_remove = 0;
+ int do_remove;
+ /*
+ * It is safe to traverse this list here because the
+ * required guard - matrix_dev->guests_lock - is taken in the
+ * vfio_ap_on_cfg_changed function prior to this function getting
+ * called.
+ */
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
- mutex_lock(&matrix_mdev->kvm->lock);
+ /*
+ * The mdevs_lock must be held to access fields within matrix_mdev,
+ * and kvm->lock must be taken before mdevs_lock to satisfy the lock
+ * ordering requirement and prevent a lockdep splat.
+ */
+ if (matrix_mdev->kvm)
+ mutex_lock(&matrix_mdev->kvm->lock);
mutex_lock(&matrix_dev->mdevs_lock);
- do_remove |= bitmap_and(aprem, ap_remove,
- matrix_mdev->matrix.apm,
- AP_DEVICES);
+ do_remove = bitmap_and(aprem, ap_remove,
+ matrix_mdev->matrix.apm,
+ AP_DEVICES);
do_remove |= bitmap_and(aqrem, aq_remove,
matrix_mdev->matrix.aqm,
AP_DOMAINS);
- do_remove |= bitmap_andnot(cdrem, cd_remove,
- matrix_mdev->matrix.adm,
- AP_DOMAINS);
+ do_remove |= bitmap_and(cdrem, cd_remove,
+ matrix_mdev->matrix.adm,
+ AP_DOMAINS);
if (do_remove)
vfio_ap_mdev_hot_unplug_cfg(matrix_mdev, aprem, aqrem,
cdrem);
mutex_unlock(&matrix_dev->mdevs_lock);
- mutex_unlock(&matrix_mdev->kvm->lock);
+ if (matrix_mdev->kvm)
+ mutex_unlock(&matrix_mdev->kvm->lock);
}
}
@@ -2504,12 +2541,20 @@ static void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add,
vfio_ap_filter_apid_by_qtype(apm_add, aqm_add);
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+ /*
+ * The mdevs_lock must be held in order to access fields
+ * within matrix_mdev
+ */
+ mutex_lock(&matrix_dev->mdevs_lock);
+
bitmap_and(matrix_mdev->apm_add,
matrix_mdev->matrix.apm, apm_add, AP_DEVICES);
bitmap_and(matrix_mdev->aqm_add,
matrix_mdev->matrix.aqm, aqm_add, AP_DOMAINS);
bitmap_and(matrix_mdev->adm_add,
matrix_mdev->matrix.adm, adm_add, AP_DEVICES);
+
+ mutex_unlock(&matrix_dev->mdevs_lock);
}
}
@@ -2576,8 +2621,14 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
DECLARE_BITMAP(apm_filtered, AP_DEVICES);
bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false;
- mutex_lock(&matrix_mdev->kvm->lock);
- mutex_lock(&matrix_dev->mdevs_lock);
+ /*
+ * Zero out the apm_filtered bitmap in case there are no adapters or
+ * domains to be added, but only control domains. In that case,
+ * vfio_ap_mdev_filter_matrix() - which initializes apm_filtered - will
+ * not get called and the reset_queues_for_apids will crash because it
+ * will access an uninitialized bitmap.
+ */
+ bitmap_zero(apm_filtered, AP_DEVICES);
filter_adapters = bitmap_intersects(matrix_mdev->matrix.apm,
matrix_mdev->apm_add, AP_DEVICES);
@@ -2596,9 +2647,6 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
reset_queues_for_apids(matrix_mdev, apm_filtered);
-
- mutex_unlock(&matrix_dev->mdevs_lock);
- mutex_unlock(&matrix_mdev->kvm->lock);
}
void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
@@ -2609,15 +2657,29 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
mutex_lock(&matrix_dev->guests_lock);
list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+ /*
+ * The mdevs_lock must be held to access fields within matrix_mdev,
+ * and kvm->lock must be taken before mdevs_lock to satisfy the lock
+ * ordering requirement and prevent a lockdep splat.
+ */
+ if (matrix_mdev->kvm)
+ mutex_lock(&matrix_mdev->kvm->lock);
+ mutex_lock(&matrix_dev->mdevs_lock);
+
if (bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) &&
bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS) &&
bitmap_empty(matrix_mdev->adm_add, AP_DOMAINS))
- continue;
+ goto do_unlock;
vfio_ap_mdev_hot_plug_cfg(matrix_mdev);
bitmap_clear(matrix_mdev->apm_add, 0, AP_DEVICES);
bitmap_clear(matrix_mdev->aqm_add, 0, AP_DOMAINS);
bitmap_clear(matrix_mdev->adm_add, 0, AP_DOMAINS);
+
+do_unlock:
+ mutex_unlock(&matrix_dev->mdevs_lock);
+ if (matrix_mdev->kvm)
+ mutex_unlock(&matrix_mdev->kvm->lock);
}
mutex_unlock(&matrix_dev->guests_lock);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index cd00f1967035..3e2e0bc49623 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -5863,6 +5863,8 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)
if (ioc->pcie_sgl_dma_pool) {
for (i = 0; i < ioc->scsiio_depth; i++) {
+ if (!ioc->pcie_sg_lookup[i].pcie_sgl)
+ continue;
dma_pool_free(ioc->pcie_sgl_dma_pool,
ioc->pcie_sg_lookup[i].pcie_sgl,
ioc->pcie_sg_lookup[i].pcie_sgl_dma);
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 00664bd2caab..e01c574c2cc0 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -1014,8 +1014,8 @@ static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha)
&(pm8001_ha->irq_vector[i]));
if (rc) {
for (j = 0; j < i; j++) {
- free_irq(pci_irq_vector(pm8001_ha->pdev, i),
- &(pm8001_ha->irq_vector[i]));
+ free_irq(pci_irq_vector(pm8001_ha->pdev, j),
+ &pm8001_ha->irq_vector[j]);
}
pci_free_irq_vectors(pm8001_ha->pdev);
break;
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 45077ce9bfdd..83a1fd47126d 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
mutex_unlock(&ha->optrom_mutex);
- ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
skip:
return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
}
@@ -816,7 +815,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
"Unable to allocate memory for VPD information update.\n");
return -ENOMEM;
}
+ mutex_lock(&ha->optrom_mutex);
ha->isp_ops->get_flash_version(vha, tmp_data);
+ mutex_unlock(&ha->optrom_mutex);
vfree(tmp_data);
break;
}
@@ -1667,10 +1668,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
rval = qla2x00_get_firmware_state(vha, state);
mutex_unlock(&vha->hw->optrom_mutex);
out:
- if (rval != QLA_SUCCESS) {
+ if (rval != QLA_SUCCESS)
memset(state, -1, sizeof(state));
- rval = qla2x00_get_firmware_state(vha, state);
- }
return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
state[0], state[1], state[2], state[3], state[4], state[5]);
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index f43969ed87bf..3982f7d576a8 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1560,12 +1560,12 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
- uint8_t bsg[DMA_POOL_SIZE];
+ uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_image_version_list *list = (void *)bsg;
struct qla_image_version *image;
uint32_t count;
dma_addr_t sfp_dma;
- void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1578,6 +1578,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
image = list->version;
count = list->count;
+
+ if (struct_size(list, version, count) > sizeof(bsg)) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_INVALID_PARAM;
+ goto dealloc;
+ }
+
while (count--) {
memcpy(sfp, &image->field_info, sizeof(image->field_info));
rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
@@ -1613,10 +1620,10 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
- uint8_t bsg[DMA_POOL_SIZE];
+ uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_status_reg *sr = (void *)bsg;
dma_addr_t sfp_dma;
- uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1664,10 +1671,10 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
- uint8_t bsg[DMA_POOL_SIZE];
+ uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_status_reg *sr = (void *)bsg;
dma_addr_t sfp_dma;
- uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1711,10 +1718,10 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
- uint8_t bsg[DMA_POOL_SIZE];
+ uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_i2c_access *i2c = (void *)bsg;
dma_addr_t sfp_dma;
- uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1725,6 +1732,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
+ if (i2c->length > sizeof(i2c->buffer)) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_INVALID_PARAM;
+ goto dealloc;
+ }
+
memcpy(sfp, i2c->buffer, i2c->length);
rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -1757,10 +1770,10 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
- uint8_t bsg[DMA_POOL_SIZE];
+ uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_i2c_access *i2c = (void *)bsg;
dma_addr_t sfp_dma;
- uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1771,6 +1784,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
+ if (i2c->length > sizeof(i2c->buffer)) {
+ bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_INVALID_PARAM;
+ goto dealloc;
+ }
+
rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 691ef827a5ab..37a308c020f9 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -16,7 +16,7 @@
* | | | 0x2127-0x2128 |
* | Queue Command and IO tracing | 0x3074 | 0x300b |
* | | | 0x3027-0x3028 |
- * | | | 0x303d-0x3041 |
+ * | | | 0x303e-0x3041 |
* | | | 0x302e,0x3033 |
* | | | 0x3036,0x3038 |
* | | | 0x303a |
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 4a82b377928d..de7e4243d380 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -518,7 +518,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
return PTR_ERR(buf);
}
- enable = kstrtoul(buf, 0, 0);
+ rc = kstrtoul(buf, 0, &enable);
+ if (rc)
+ goto out_free;
rc = count;
mutex_lock(&ha->fce_mutex);
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index be20e2c457b8..dd5b64e73fd8 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -3491,6 +3491,9 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
uint32_t handle;
+ if (!cmd)
+ return;
+
handle = (uint32_t)LSW(sts24->handle);
/* find out if this status iosb is for a scsi read */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 4cef9a349343..cdf50f71f76e 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -157,8 +157,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
break;
case CS_PORT_LOGGED_OUT:
if (IS_FWI2_CAPABLE(ha)) {
- if (le16_to_cpu(ms_pkt->loop_id.extended) ==
- NPH_SNS)
+ if (le16_to_cpu(((struct ct_entry_24xx *)
+ ms_pkt)->nport_handle) == NPH_SNS)
lid_is_sns = true;
} else {
if (le16_to_cpu(ms_pkt->loop_id.extended) ==
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index b736575e2d9f..c89ad5d7b4d5 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
srb_t *sp = data;
fc_port_t *fcport = sp->fcport;
struct srb_iocb *lio = &sp->u.iocb_cmd;
- int rc, h;
+ int rc, h, found;
unsigned long flags;
if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
lio->u.logio.data[1] =
lio->u.logio.flags & SRB_LOGIN_RETRIED ?
QLA_LOGIO_LOGIN_RETRIED : 0;
+ found = 0;
spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
sp) {
sp->qpair->req->outstanding_cmds[h] =
NULL;
+ found = 1;
break;
}
}
spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
- sp->done(sp, QLA_FUNCTION_TIMEOUT);
+ /*
+ * Only complete the command if this path removed it
+ * from outstanding_cmds. Otherwise the ISR already
+ * completed it and a second sp->done() would race the
+ * submitter's freeing of the on-stack completion.
+ */
+ if (found)
+ sp->done(sp, QLA_FUNCTION_TIMEOUT);
}
break;
case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
default:
rc = qla24xx_async_abort_cmd(sp, false);
if (rc) {
+ found = 0;
spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
sp) {
sp->qpair->req->outstanding_cmds[h] =
NULL;
+ found = 1;
break;
}
}
spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
- sp->done(sp, QLA_FUNCTION_TIMEOUT);
+ /*
+ * Only complete the command if this path removed it
+ * from outstanding_cmds. Otherwise the ISR already
+ * completed it and a second sp->done() would race the
+ * submitter's freeing of the on-stack completion.
+ */
+ if (found)
+ sp->done(sp, QLA_FUNCTION_TIMEOUT);
}
break;
}
@@ -3764,11 +3782,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
void qla2x00_free_fce_trace(struct qla_hw_data *ha)
{
- if (!ha->fce)
+ void *fce;
+ dma_addr_t fce_dma;
+ unsigned long flags;
+
+ /*
+ * Unpublish ha->fce under hardware_lock so a firmware dump in
+ * progress (which reads ha->fce under the same lock) cannot race
+ * with the buffer being freed.
+ */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!ha->fce) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
- dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
+ }
+ fce = ha->fce;
+ fce_dma = ha->fce_dma;
ha->fce = NULL;
ha->fce_dma = 0;
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
}
static void
@@ -9837,11 +9871,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
{
int ret = QLA_FUNCTION_FAILED;
struct qla_hw_data *ha = qpair->hw;
+ struct rsp_que *rsp = qpair->rsp;
qpair->delete_in_progress = 1;
qla_free_buf_pool(qpair);
+ /*
+ * The response-queue interrupt schedules qla_do_work(), which
+ * dereferences qpair->rsp->req. Release the interrupt and flush
+ * any pending work before the request queue is freed below so a
+ * late completion cannot touch the freed request queue. The
+ * firmware queue-delete order (request then response) is kept.
+ */
+ if (rsp && rsp->msix && rsp->msix->have_irq) {
+ free_irq(rsp->msix->vector, rsp->msix->handle);
+ rsp->msix->have_irq = 0;
+ rsp->msix->in_use = 0;
+ rsp->msix->handle = NULL;
+ }
+ if (rsp && ha->wq)
+ cancel_work_sync(&qpair->q_work);
+
ret = qla25xx_delete_req_que(vha, qpair->req);
if (ret != QLA_SUCCESS)
goto fail;
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index ef4b3cc1cd77..09ada9dfb5b8 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -54,6 +54,19 @@ qla2x00_debounce_register(volatile __le16 __iomem *addr)
return (first);
}
+static inline u8
+qla_calc_queue_count(u16 msix_count)
+{
+ /*
+ * Request/response queues are bounded by the MSI-X vector count less
+ * the mailbox vector. These counters are u8, so a board advertising
+ * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
+ * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
+ * ha->req_q_map[0] store. Clamp into [1, QLA_MAX_QUEUES - 1].
+ */
+ return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
+}
+
static inline void
qla2x00_poll(struct rsp_que *rsp)
{
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 38214f8e30fa..2df5cd3d8262 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -205,6 +205,17 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
struct purex_entry_24xx *purex = *pkt;
entry_count_remaining = purex->entry_count;
+
+ /*
+ * The caller already advanced ring_ptr past the head IOCB, so mark
+ * the head processed and account for it here, then consume only the
+ * continuation IOCBs that follow.
+ */
+ ((response_t *)purex)->signature = RESPONSE_PROCESSED;
+ /* flush signature */
+ wmb();
+ --entry_count_remaining;
+
while (entry_count_remaining > 0) {
new_pkt = rsp_q->ring_ptr;
*pkt = new_pkt;
@@ -3412,6 +3423,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
return;
}
+ /* Everything below is the SCSI fast path; reject other SRB types. */
+ if (sp->type != SRB_SCSI_CMD) {
+ ql_dbg(ql_dbg_io, vha, 0x303d,
+ "Unexpected SRB type %x for status IOCB, sp %p.\n",
+ sp->type, sp);
+ return;
+ }
+
/* Fast path completion. */
qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
sp->qpair->cmd_completion_cnt++;
@@ -3469,6 +3488,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
/* Sense data lies beyond any FCP RESPONSE data. */
if (IS_FWI2_CAPABLE(ha)) {
+ /*
+ * A hostile or buggy target may report an
+ * rsp_info_len larger than the IOCB data area.
+ * Clamp it so the par_sense_len subtraction cannot
+ * underflow and walk sense_data out of bounds.
+ */
+ if (rsp_info_len > par_sense_len) {
+ ql_log(ql_log_warn, fcport->vha, 0x3107,
+ "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
+ rsp_info_len, par_sense_len);
+ rsp_info_len = par_sense_len;
+ }
sense_data += rsp_info_len;
par_sense_len -= rsp_info_len;
}
@@ -3784,10 +3815,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
"iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
- if (que >= ha->max_req_queues || !ha->req_q_map[que])
+ if (que >= ha->max_req_queues)
goto fatal;
req = ha->req_q_map[que];
+ if (!req)
+ goto fatal;
if (pkt->entry_status & RF_BUSY)
res = DID_BUS_BUSY << 16;
@@ -4554,10 +4587,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
ha->msix_count = ret;
/* Recalculate queue values */
if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
- ha->max_req_queues = ha->msix_count - 1;
+ ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
/* ATIOQ needs 1 vector. That's 1 less QPair */
- if (QLA_TGT_MODE_ENABLED())
+ if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
ha->max_req_queues--;
ha->max_rsp_queues = ha->max_req_queues;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 41435e98092a..5bb9241ebebd 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2214,6 +2214,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
if (!ha->flags.fw_started)
return QLA_FUNCTION_FAILED;
+ memset(&mc, 0, sizeof(mc));
+
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
if (IS_FWI2_CAPABLE(vha->hw))
@@ -4146,6 +4148,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
list_for_each_entry(vp, &ha->vp_list, list) {
if (rptid_entry->vp_idx == vp->vp_idx) {
found = 1;
+ atomic_inc(&vp->vref_count);
break;
}
}
@@ -4154,7 +4157,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (!found)
return;
+ spin_lock_irqsave(&ha->vport_slock, flags);
qla_update_host_map(vp, id);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
/*
* Cannot configure here as we are still sitting on the
@@ -4163,6 +4168,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
}
set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
@@ -4273,10 +4282,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x10bd,
"Failed to issue VP config IOCB (%x).\n", rval);
- } else if (vpmod->comp_status != 0) {
+ } else if (vpmod->entry_status != 0) {
ql_dbg(ql_dbg_mbx, vha, 0x10be,
"Failed to complete IOCB -- error status (%x).\n",
- vpmod->comp_status);
+ vpmod->entry_status);
rval = QLA_FUNCTION_FAILED;
} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
ql_dbg(ql_dbg_mbx, vha, 0x10bf,
@@ -6433,6 +6442,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
dma_addr_t dd_dma;
+ void *dd;
if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
!IS_QLA28XX(vha->hw))
@@ -6441,15 +6451,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
"Entered %s.\n", __func__);
- dd_dma = dma_map_single(&vha->hw->pdev->dev,
- dd_buf, size, DMA_FROM_DEVICE);
- if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
- ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
+ dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
+ if (!dd) {
+ ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
return QLA_MEMORY_ALLOC_FAILED;
}
- memset(dd_buf, 0, size);
-
mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
mcp->mb[1] = options;
mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6471,8 +6478,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
"Done %s.\n", __func__);
}
- dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
- size, DMA_FROM_DEVICE);
+ memcpy(dd_buf, dd, size);
+
+ dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
return rval;
}
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 38cb3281c635..23481b5d42b3 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -375,6 +375,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
srb_t *sp;
int rval = QLA_FUNCTION_FAILED;
uint8_t cnt = 0;
+ unsigned long flags;
if (!fcport || fcport->deleted)
goto out;
@@ -441,7 +442,11 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
- qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+ if (ha->flags.fw_started) {
+ spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
+ qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+ spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+ }
kfree(uctx);
return rval;
}
@@ -464,8 +469,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
- INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
- schedule_work(&priv->abort_work);
+ if (!schedule_work(&priv->abort_work))
+ kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
}
static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -502,6 +507,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
priv->sp = sp;
kref_init(&sp->cmd_kref);
spin_lock_init(&priv->cmd_lock);
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
nvme = &sp->u.iocb_cmd;
priv->fd = fd;
nvme->u.nvme.desc = fd;
@@ -546,8 +552,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
- INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
- schedule_work(&priv->abort_work);
+ if (!schedule_work(&priv->abort_work))
+ kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
}
static inline int qla2x00_start_nvme_mq(srb_t *sp)
@@ -812,6 +818,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
kref_init(&sp->cmd_kref);
spin_lock_init(&priv->cmd_lock);
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
sp->priv = priv;
priv->sp = sp;
sp->type = SRB_NVME_CMD;
@@ -1128,6 +1135,10 @@ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
lsrjt_iocb->rx_byte_count = 0;
}
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
static int
qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
@@ -1184,6 +1195,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
{
struct qla_nvme_unsol_ctx *uctx = item->purls_context;
struct qla_nvme_lsrjt_pt_arg a;
+ unsigned long flags;
int ret = 1;
#if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1196,7 +1208,14 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
- qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+ if (vha->hw->flags.fw_started) {
+ spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
+ flags);
+ qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
+ true);
+ spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
+ flags);
+ }
list_del(&uctx->elem);
kfree(uctx);
}
diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c
index 41ff6fbdb933..04d7ab6ad037 100644
--- a/drivers/scsi/qla2xxx/qla_nx2.c
+++ b/drivers/scsi/qla2xxx/qla_nx2.c
@@ -3507,7 +3507,7 @@ qla8044_poll_flash_status_reg(struct scsi_qla_host *vha)
msleep(QLA8044_FLASH_STATUS_REG_POLL_DELAY);
}
- if (!retries)
+ if (retries == -1)
ret_val = QLA_FUNCTION_FAILED;
return ret_val;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index b08abdf5ebf4..ba61b5ae433d 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2126,7 +2126,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
ha->msix_count = msix + 1;
/* Max queues are bounded by available msix vectors */
/* MB interrupt uses 1 vector */
- ha->max_req_queues = ha->msix_count - 1;
+ ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
ha->max_rsp_queues = ha->max_req_queues;
/* Queue pairs is the max value minus the base queue pair */
ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2212,10 +2212,10 @@ qla83xx_iospace_config(struct qla_hw_data *ha)
*/
if (ql2xmqsupport || ql2xnvmeenable) {
/* MB interrupt uses 1 vector */
- ha->max_req_queues = ha->msix_count - 1;
+ ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
/* ATIOQ needs 1 vector. That's 1 less QPair */
- if (QLA_TGT_MODE_ENABLED())
+ if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
ha->max_req_queues--;
ha->max_rsp_queues = ha->max_req_queues;
@@ -3927,8 +3927,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
qla2x00_dfs_remove(base_vha);
- qla84xx_put_chip(base_vha);
-
/* Disable timer */
if (base_vha->timer_active)
qla2x00_stop_timer(base_vha);
@@ -3953,6 +3951,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
scsi_remove_host(base_vha->host);
+ qla84xx_put_chip(base_vha);
+
qla2x00_free_device(base_vha);
qla2x00_clear_drv_active(ha);
@@ -6824,8 +6824,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
qla2x00_dfs_remove(base_vha);
- qla84xx_put_chip(base_vha);
-
if (base_vha->timer_active)
qla2x00_stop_timer(base_vha);
@@ -6843,6 +6841,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
scsi_remove_host(base_vha->host);
+ qla84xx_put_chip(base_vha);
+
base_vha->flags.init_done = 0;
qla25xx_delete_queues(base_vha);
qla2x00_free_fcports(base_vha);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 2268e540f28a..f3514b45ef30 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1087,8 +1087,10 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd)
if (blk_rq_bytes(rq) & rq->q->dma_pad_mask) {
unsigned int pad_len =
(rq->q->dma_pad_mask & ~blk_rq_bytes(rq)) + 1;
+ unsigned int data_len = last_sg->length;
last_sg->length += pad_len;
+ sg_zero_buffer(last_sg, 1, pad_len, data_len);
cmd->extra_len += pad_len;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 8cb10cb78b1d..a6c8d786125e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2167,8 +2167,8 @@ static int sd_done(struct scsi_cmnd *SCpnt)
}
sdkp->medium_access_timed_out = 0;
- if (!scsi_status_is_check_condition(result) &&
- (!sense_valid || sense_deferred))
+ if (!scsi_status_is_check_condition(result) ||
+ !sense_valid || sense_deferred)
goto out;
switch (sshdr.sense_key) {
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index 78d2d9b9ebb4..441ad732ee80 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -65,6 +65,12 @@ static struct pqi_cmd_priv *pqi_cmd_priv(struct scsi_cmnd *cmd)
return scsi_cmd_priv(cmd);
}
+static int pqi_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
+{
+ memset(pqi_cmd_priv(cmd), 0, sizeof(struct pqi_cmd_priv));
+ return 0;
+}
+
static void pqi_verify_structures(void);
static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info,
enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason);
@@ -5898,6 +5904,17 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd)
struct pqi_scsi_dev *device;
struct completion *wait;
+ /*
+ * Clear the AIO-retry marker on final completion so the tag
+ * starts clean on its next dispatch. On DID_IMM_RETRY leave
+ * it intact: pqi_aio_io_complete() sets DID_IMM_RETRY and
+ * bumps the marker to steer the requeue onto the RAID path,
+ * and pqi_process_raid_io_error() consumes the non-zero
+ * marker to offline a misbehaving drive.
+ */
+ if (host_byte(scmd->result) != DID_IMM_RETRY)
+ pqi_cmd_priv(scmd)->this_residual = 0;
+
if (!scmd->device) {
set_host_byte(scmd, DID_NO_CONNECT);
return;
@@ -7512,6 +7529,7 @@ static const struct scsi_host_template pqi_driver_template = {
.sdev_groups = pqi_sdev_groups,
.shost_groups = pqi_shost_groups,
.cmd_size = sizeof(struct pqi_cmd_priv),
+ .init_cmd_priv = pqi_init_cmd_priv,
};
static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info)
diff --git a/drivers/soc/fsl/qe/qe_ic.c b/drivers/soc/fsl/qe/qe_ic.c
index bbae3d39c7be..f17de6000ff2 100644
--- a/drivers/soc/fsl/qe/qe_ic.c
+++ b/drivers/soc/fsl/qe/qe_ic.c
@@ -481,7 +481,6 @@ static struct platform_driver qe_ic_driver =
static int __init qe_ic_of_init(void)
{
- platform_driver_register(&qe_ic_driver);
- return 0;
+ return platform_driver_register(&qe_ic_driver);
}
subsys_initcall(qe_ic_of_init);
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index d0ec42ec0d04..2f1336af7794 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -944,17 +944,30 @@ static int rpmh_rsc_pd_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
+static void rpmh_rsc_pd_detach(void *data)
+{
+ dev_pm_genpd_remove_notifier(data);
+}
+
static int rpmh_rsc_pd_attach(struct rsc_drv *drv, struct device *dev)
{
int ret;
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
drv->genpd_nb.notifier_call = rpmh_rsc_pd_callback;
ret = dev_pm_genpd_add_notifier(dev, &drv->genpd_nb);
if (ret)
- pm_runtime_disable(dev);
+ return ret;
- return ret;
+ return devm_add_action_or_reset(dev, rpmh_rsc_pd_detach, dev);
+}
+
+static void rpmh_rsc_cpu_pm_unregister(void *data)
+{
+ cpu_pm_unregister_notifier(data);
}
static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv)
@@ -1110,7 +1123,15 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
return ret;
} else {
drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback;
- cpu_pm_register_notifier(&drv->rsc_pm);
+ ret = cpu_pm_register_notifier(&drv->rsc_pm);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rpmh_rsc_cpu_pm_unregister,
+ &drv->rsc_pm);
+ if (ret)
+ return ret;
}
}
@@ -1125,13 +1146,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, drv);
drv->dev = &pdev->dev;
- ret = devm_of_platform_populate(&pdev->dev);
- if (ret && pdev->dev.pm_domain) {
- dev_pm_genpd_remove_notifier(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- }
-
- return ret;
+ return devm_of_platform_populate(&pdev->dev);
}
static const struct of_device_id rpmh_drv_match[] = {
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index 250537d7cfd6..aea4afefb345 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -115,8 +115,8 @@ static const struct mfd_cell exynos_pmu_devs[] = {
struct regmap *exynos_get_pmu_regmap(void)
{
- struct device_node *np = of_find_matching_node(NULL,
- exynos_pmu_of_device_ids);
+ struct device_node *np __free(device_node) =
+ of_find_matching_node(NULL, exynos_pmu_of_device_ids);
if (np)
return syscon_node_to_regmap(np);
return ERR_PTR(-ENODEV);
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index e3ae4e4e07ac..093ed8febe16 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -1168,7 +1168,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl,
else
pn = find_first_zero_bit(port_mask, maxport);
- if (pn > maxport) {
+ if (pn >= maxport) {
dev_err(ctrl->dev, "All ports busy\n");
ret = -EBUSY;
goto err;
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 8df12efeea21..f5a8edef7b7d 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -921,7 +921,13 @@ static int bcm63xx_hsspi_resume(struct device *dev)
}
}
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ if (bs->pll_clk)
+ clk_disable_unprepare(bs->pll_clk);
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 746a61095ad4..6f7c4ab0057d 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -661,7 +661,11 @@ static int bcm63xx_spi_resume(struct device *dev)
if (ret)
return ret;
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/spi/spi-bcmbca-hsspi.c b/drivers/spi/spi-bcmbca-hsspi.c
index 6f71639aea03..81826692d4df 100644
--- a/drivers/spi/spi-bcmbca-hsspi.c
+++ b/drivers/spi/spi-bcmbca-hsspi.c
@@ -621,7 +621,13 @@ static int bcmbca_hsspi_resume(struct device *dev)
}
}
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ if (bs->pll_clk)
+ clk_disable_unprepare(bs->pll_clk);
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index c457b550d3ad..72f36ee4568b 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -862,7 +862,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
int ret = 0;
u32 spipc0;
- host = spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
+ host = devm_spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
if (host == NULL) {
ret = -ENOMEM;
goto err;
@@ -915,14 +915,11 @@ static int davinci_spi_probe(struct platform_device *pdev)
dspi->bitbang.master = host;
- dspi->clk = devm_clk_get(&pdev->dev, NULL);
+ dspi->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dspi->clk)) {
ret = -ENODEV;
goto free_host;
}
- ret = clk_prepare_enable(dspi->clk);
- if (ret)
- goto free_host;
host->use_gpio_descriptors = true;
host->dev.of_node = pdev->dev.of_node;
@@ -947,7 +944,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
ret = davinci_spi_request_dma(dspi);
if (ret == -EPROBE_DEFER) {
- goto free_clk;
+ goto free_host;
} else if (ret) {
dev_info(&pdev->dev, "DMA is not supported (%d)\n", ret);
dspi->dma_rx = NULL;
@@ -987,14 +984,14 @@ static int davinci_spi_probe(struct platform_device *pdev)
return ret;
free_dma:
+ /* This bit needs to be cleared to disable dpsi->clk */
+ clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
+
if (dspi->dma_rx) {
dma_release_channel(dspi->dma_rx);
dma_release_channel(dspi->dma_tx);
}
-free_clk:
- clk_disable_unprepare(dspi->clk);
free_host:
- spi_controller_put(host);
err:
return ret;
}
@@ -1018,14 +1015,15 @@ static void davinci_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&dspi->bitbang);
- clk_disable_unprepare(dspi->clk);
+ devm_free_irq(&pdev->dev, dspi->irq, dspi);
+
+ /* This bit needs to be cleared to disable dpsi->clk */
+ clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
if (dspi->dma_rx) {
dma_release_channel(dspi->dma_rx);
dma_release_channel(dspi->dma_tx);
}
-
- spi_controller_put(host);
}
static struct platform_driver davinci_spi_driver = {
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index 1e2a8cf9290f..07fd38ae959e 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -616,7 +616,7 @@ static int img_spfi_probe(struct platform_device *pdev)
ret = PTR_ERR(spfi->tx_ch);
spfi->tx_ch = NULL;
if (ret == -EPROBE_DEFER)
- goto disable_pm;
+ goto free_dma;
}
spfi->rx_ch = dma_request_chan(spfi->dev, "rx");
@@ -624,7 +624,7 @@ static int img_spfi_probe(struct platform_device *pdev)
ret = PTR_ERR(spfi->rx_ch);
spfi->rx_ch = NULL;
if (ret == -EPROBE_DEFER)
- goto disable_pm;
+ goto free_dma;
}
if (!spfi->tx_ch || !spfi->rx_ch) {
@@ -652,6 +652,7 @@ static int img_spfi_probe(struct platform_device *pdev)
disable_pm:
pm_runtime_disable(spfi->dev);
+free_dma:
if (spfi->rx_ch)
dma_release_channel(spfi->rx_ch);
if (spfi->tx_ch)
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index f25b34a91756..72f2f30e657e 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -553,6 +553,12 @@ static int sprd_adi_probe(struct platform_device *pdev)
return -ENXIO;
} else {
switch (ret) {
+ case 0:
+ /*
+ * Only reachable with CONFIG_HWSPINLOCK=n, where the
+ * of_hwspin_lock_get_id() stub returns 0.
+ */
+ fallthrough;
case -ENOENT:
dev_info(&pdev->dev, "no hardware spinlock supplied\n");
break;
diff --git a/drivers/staging/fbtft/fbtft-sysfs.c b/drivers/staging/fbtft/fbtft-sysfs.c
index 39e8d28066cb..809bf350af25 100644
--- a/drivers/staging/fbtft/fbtft-sysfs.c
+++ b/drivers/staging/fbtft/fbtft-sysfs.c
@@ -102,7 +102,7 @@ sprintf_gamma(struct fbtft_par *par, u32 *curves, char *buf)
mutex_lock(&par->gamma.lock);
for (i = 0; i < par->gamma.num_curves; i++) {
for (j = 0; j < par->gamma.num_values; j++)
- len += scnprintf(&buf[len], PAGE_SIZE,
+ len += sysfs_emit_at(buf, len,
"%04x ", curves[i * par->gamma.num_values + j]);
buf[len - 1] = '\n';
}
diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c
index 9d8994fdb41a..144591f1a512 100644
--- a/drivers/staging/greybus/audio_gb.c
+++ b/drivers/staging/greybus/audio_gb.c
@@ -37,6 +37,19 @@ int gb_audio_gb_get_topology(struct gb_connection *connection,
return ret;
}
+ /*
+ * The size_* fields are supplied by the module and are used by
+ * gbaudio_tplg_parse_data() to compute offsets into the blob; make
+ * sure the sections fit within the fetched topology, so walking it
+ * cannot read out of bounds.
+ */
+ if ((u64)le32_to_cpu(topo->size_dais) + le32_to_cpu(topo->size_controls) +
+ le32_to_cpu(topo->size_widgets) + le32_to_cpu(topo->size_routes) >
+ size - sizeof(*topo)) {
+ kfree(topo);
+ return -EINVAL;
+ }
+
*topology = topo;
return 0;
diff --git a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c
index fcd518f9540c..ff323ca8154f 100644
--- a/drivers/staging/greybus/audio_manager_sysfs.c
+++ b/drivers/staging/greybus/audio_manager_sysfs.c
@@ -23,7 +23,7 @@ static ssize_t manager_sysfs_add_store(struct kobject *kobj,
desc.name, &desc.vid, &desc.pid, &desc.intf_id,
&desc.ip_devices, &desc.op_devices);
- if (num != 7)
+ if (num != 6)
return -EINVAL;
num = gb_audio_manager_add(&desc);
diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index e761411ccf64..9912868b6e9e 100644
--- a/drivers/staging/greybus/hid.c
+++ b/drivers/staging/greybus/hid.c
@@ -256,7 +256,7 @@ static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
if (report_id && ret >= 0)
ret++; /* add report_id to the number of transferred bytes */
- return 0;
+ return ret;
}
static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6efbc7fa6cab..6783f6e09a2a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -505,8 +505,8 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size,
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt_out = find_format(fmts, size, pixmp->pixelformat);
if (!fmt_out) {
- pixmp->pixelformat = V4L2_PIX_FMT_MPEG2;
- fmt_out = find_format(fmts, size, pixmp->pixelformat);
+ pixmp->pixelformat = fmts[0].pixfmt;
+ fmt_out = &fmts[0];
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index dc9ed1d0ff3a..be71528554a7 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -285,8 +285,10 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1;
ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
- if (!ctx->ctrls)
+ if (!ctx->ctrls) {
+ v4l2_ctrl_handler_free(hdl);
return -ENOMEM;
+ }
j = 0;
for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 9c0d4661edf5..d4ee7e7487d4 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1262,6 +1262,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
struct device_node *parent;
struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
unsigned int lanes;
+ int err;
int ret = 0;
ports = of_get_child_by_name(node, "ports");
@@ -1272,8 +1273,8 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
if (!of_node_name_eq(port, "port"))
continue;
- ret = of_property_read_u32(port, "reg", &port_num);
- if (ret < 0)
+ err = of_property_read_u32(port, "reg", &port_num);
+ if (err < 0)
continue;
if (port_num > vi->soc->vi_max_channels) {
@@ -1294,10 +1295,10 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
ep = of_graph_get_endpoint_by_regs(parent, 0, 0);
of_node_put(parent);
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep),
+ err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep),
&v4l2_ep);
of_node_put(ep);
- if (ret)
+ if (err)
continue;
lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c
index 191ecd19a6a7..26ec721cc642 100644
--- a/drivers/staging/media/tegra-video/vip.c
+++ b/drivers/staging/media/tegra-video/vip.c
@@ -126,7 +126,7 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip)
if (!ep) {
err = -EINVAL;
dev_err_probe(dev, err, "%pOF: error getting endpoint node\n", np);
- goto err_node_put;
+ return err;
}
fwh = of_fwnode_handle(ep);
@@ -134,14 +134,14 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip)
of_node_put(ep);
if (err) {
dev_err_probe(dev, err, "%pOF: failed to parse v4l2 endpoint\n", np);
- goto err_node_put;
+ return err;
}
num_pads = of_graph_get_endpoint_count(np);
if (num_pads != TEGRA_VIP_PADS_NUM) {
err = -EINVAL;
dev_err_probe(dev, err, "%pOF: need 2 pads, got %d\n", np, num_pads);
- goto err_node_put;
+ return err;
}
vip->chan.of_node = of_node_get(np);
@@ -149,10 +149,6 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip)
vip->chan.pads[TEGRA_VIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
return 0;
-
-err_node_put:
- of_node_put(np);
- return err;
}
static int tegra_vip_channel_init(struct tegra_vip *vip)
diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c
index 532594957ebc..68c3ef984e56 100644
--- a/drivers/staging/octeon/ethernet-mem.c
+++ b/drivers/staging/octeon/ethernet-mem.c
@@ -5,6 +5,7 @@
* Copyright (c) 2003-2010 Cavium Networks
*/
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
@@ -40,11 +41,13 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
/**
* cvm_oct_free_hw_skbuff- free hardware pool skbuffs
+ * @pdev: Platform device for logging
* @pool: Pool to allocate an skbuff for
* @size: Size of the buffer needed for the pool
* @elements: Number of buffers to allocate
*/
-static void cvm_oct_free_hw_skbuff(int pool, int size, int elements)
+static void cvm_oct_free_hw_skbuff(struct platform_device *pdev,
+ int pool, int size, int elements)
{
char *memory;
@@ -59,22 +62,24 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements)
} while (memory);
if (elements < 0)
- pr_warn("Freeing of pool %u had too many skbuffs (%d)\n",
- pool, elements);
+ dev_warn(&pdev->dev, "Freeing of pool %u had too many skbuffs (%d)\n",
+ pool, elements);
else if (elements > 0)
- pr_warn("Freeing of pool %u is missing %d skbuffs\n",
- pool, elements);
+ dev_warn(&pdev->dev, "Freeing of pool %u is missing %d skbuffs\n",
+ pool, elements);
}
/**
* cvm_oct_fill_hw_memory - fill a hardware pool with memory.
+ * @pdev: Platform device for logging
* @pool: Pool to populate
* @size: Size of each buffer in the pool
* @elements: Number of buffers to allocate
*
* Returns the actual number of buffers allocated.
*/
-static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
+static int cvm_oct_fill_hw_memory(struct platform_device *pdev, int pool, int size,
+ int elements)
{
char *memory;
char *fpa;
@@ -93,8 +98,8 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
*/
memory = kmalloc(size + 256, GFP_ATOMIC);
if (unlikely(!memory)) {
- pr_warn("Unable to allocate %u bytes for FPA pool %d\n",
- elements * size, pool);
+ dev_warn(&pdev->dev, "Unable to allocate %u bytes for FPA pool %d\n",
+ elements * size, pool);
break;
}
fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL);
@@ -107,11 +112,13 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
/**
* cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory
+ * @pdev: Platform device for logging
* @pool: FPA pool to free
* @size: Size of each buffer in the pool
* @elements: Number of buffers that should be in the pool
*/
-static void cvm_oct_free_hw_memory(int pool, int size, int elements)
+static void cvm_oct_free_hw_memory(struct platform_device *pdev,
+ int pool, int size, int elements)
{
char *memory;
char *fpa;
@@ -127,28 +134,28 @@ static void cvm_oct_free_hw_memory(int pool, int size, int elements)
} while (fpa);
if (elements < 0)
- pr_warn("Freeing of pool %u had too many buffers (%d)\n",
- pool, elements);
+ dev_warn(&pdev->dev, "Freeing of pool %u had too many buffers (%d)\n",
+ pool, elements);
else if (elements > 0)
- pr_warn("Warning: Freeing of pool %u is missing %d buffers\n",
- pool, elements);
+ dev_warn(&pdev->dev, "Freeing of pool %u is missing %d buffers\n",
+ pool, elements);
}
-int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
+int cvm_oct_mem_fill_fpa(struct platform_device *pdev, int pool, int size, int elements)
{
int freed;
if (pool == CVMX_FPA_PACKET_POOL)
freed = cvm_oct_fill_hw_skbuff(pool, size, elements);
else
- freed = cvm_oct_fill_hw_memory(pool, size, elements);
+ freed = cvm_oct_fill_hw_memory(pdev, pool, size, elements);
return freed;
}
-void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
+void cvm_oct_mem_empty_fpa(struct platform_device *pdev, int pool, int size, int elements)
{
if (pool == CVMX_FPA_PACKET_POOL)
- cvm_oct_free_hw_skbuff(pool, size, elements);
+ cvm_oct_free_hw_skbuff(pdev, pool, size, elements);
else
- cvm_oct_free_hw_memory(pool, size, elements);
+ cvm_oct_free_hw_memory(pdev, pool, size, elements);
}
diff --git a/drivers/staging/octeon/ethernet-mem.h b/drivers/staging/octeon/ethernet-mem.h
index 692dcdb7154d..9279bb0de2db 100644
--- a/drivers/staging/octeon/ethernet-mem.h
+++ b/drivers/staging/octeon/ethernet-mem.h
@@ -5,5 +5,9 @@
* Copyright (c) 2003-2007 Cavium Networks
*/
-int cvm_oct_mem_fill_fpa(int pool, int size, int elements);
-void cvm_oct_mem_empty_fpa(int pool, int size, int elements);
+struct platform_device;
+
+int cvm_oct_mem_fill_fpa(struct platform_device *pdev, int pool, int size,
+ int elements);
+void cvm_oct_mem_empty_fpa(struct platform_device *pdev, int pool, int size,
+ int elements);
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 965330eec80a..3e9d58d32156 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -5,6 +5,7 @@
* Copyright (c) 2003-2010 Cavium Networks
*/
+#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cache.h>
@@ -31,12 +32,6 @@
static atomic_t oct_rx_ready = ATOMIC_INIT(0);
-static struct oct_rx_group {
- int irq;
- int group;
- struct napi_struct napi;
-} oct_rx_group[16];
-
/**
* cvm_oct_do_interrupt - interrupt handler.
* @irq: Interrupt number.
@@ -397,7 +392,7 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
/* Restore the scratch area */
cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
}
- cvm_oct_rx_refill_pool(0);
+ cvm_oct_rx_refill_pool(rx_group->pdev, 0);
return rx_count;
}
@@ -434,24 +429,28 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
*/
void cvm_oct_poll_controller(struct net_device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev->dev.parent);
+ struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev);
int i;
if (!atomic_read(&oct_rx_ready))
return;
- for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) {
+ for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) {
if (!(pow_receive_groups & BIT(i)))
continue;
- cvm_oct_poll(&oct_rx_group[i], 16);
+ cvm_oct_poll(&plat->rx_group[i], 16);
}
}
#endif
-void cvm_oct_rx_initialize(void)
+void cvm_oct_rx_initialize(struct platform_device *pdev)
{
int i;
struct net_device *dev_for_napi = NULL;
+ struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev);
+ struct oct_rx_group *rx_group = plat->rx_group;
for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) {
if (cvm_oct_device[i]) {
@@ -463,27 +462,28 @@ void cvm_oct_rx_initialize(void)
if (!dev_for_napi)
panic("No net_devices were allocated.");
- for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) {
+ for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) {
int ret;
if (!(pow_receive_groups & BIT(i)))
continue;
- netif_napi_add_weight(dev_for_napi, &oct_rx_group[i].napi,
+ netif_napi_add_weight(dev_for_napi, &rx_group[i].napi,
cvm_oct_napi_poll, rx_napi_weight);
- napi_enable(&oct_rx_group[i].napi);
+ napi_enable(&rx_group[i].napi);
- oct_rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i;
- oct_rx_group[i].group = i;
+ rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i;
+ rx_group[i].group = i;
+ rx_group[i].pdev = pdev;
/* Register an IRQ handler to receive POW interrupts */
- ret = request_irq(oct_rx_group[i].irq, cvm_oct_do_interrupt, 0,
- "Ethernet", &oct_rx_group[i].napi);
+ ret = request_irq(rx_group[i].irq, cvm_oct_do_interrupt, 0,
+ "Ethernet", &rx_group[i].napi);
if (ret)
panic("Could not acquire Ethernet IRQ %d\n",
- oct_rx_group[i].irq);
+ rx_group[i].irq);
- disable_irq_nosync(oct_rx_group[i].irq);
+ disable_irq_nosync(rx_group[i].irq);
/* Enable POW interrupt when our port has at least one packet */
if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
@@ -515,16 +515,17 @@ void cvm_oct_rx_initialize(void)
/* Schedule NAPI now. This will indirectly enable the
* interrupt.
*/
- napi_schedule(&oct_rx_group[i].napi);
+ napi_schedule(&rx_group[i].napi);
}
atomic_inc(&oct_rx_ready);
}
-void cvm_oct_rx_shutdown(void)
+void cvm_oct_rx_shutdown(struct platform_device *pdev)
{
+ struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) {
+ for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) {
if (!(pow_receive_groups & BIT(i)))
continue;
@@ -534,9 +535,11 @@ void cvm_oct_rx_shutdown(void)
else
cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0);
+ napi_disable(&plat->rx_group[i].napi);
+
/* Free the interrupt handler */
- free_irq(oct_rx_group[i].irq, cvm_oct_device);
+ free_irq(plat->rx_group[i].irq, &plat->rx_group[i].napi);
- netif_napi_del(&oct_rx_group[i].napi);
+ netif_napi_del(&plat->rx_group[i].napi);
}
}
diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h
index ff6482fa20d6..6093694326cb 100644
--- a/drivers/staging/octeon/ethernet-rx.h
+++ b/drivers/staging/octeon/ethernet-rx.h
@@ -5,11 +5,14 @@
* Copyright (c) 2003-2007 Cavium Networks
*/
+struct platform_device;
+
void cvm_oct_poll_controller(struct net_device *dev);
-void cvm_oct_rx_initialize(void);
-void cvm_oct_rx_shutdown(void);
+void cvm_oct_rx_initialize(struct platform_device *pdev);
+void cvm_oct_rx_shutdown(struct platform_device *pdev);
-static inline void cvm_oct_rx_refill_pool(int fill_threshold)
+static inline void cvm_oct_rx_refill_pool(struct platform_device *pdev,
+ int fill_threshold)
{
int number_to_free;
int num_freed;
@@ -20,7 +23,7 @@ static inline void cvm_oct_rx_refill_pool(int fill_threshold)
if (number_to_free > fill_threshold) {
cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
-number_to_free);
- num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
+ num_freed = cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_PACKET_POOL,
CVMX_FPA_PACKET_POOL_SIZE,
number_to_free);
if (num_freed != number_to_free) {
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index bbf33b88bb7c..e54b4a413dc4 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -711,4 +711,6 @@ void cvm_oct_tx_shutdown(void)
{
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
+
+ tasklet_kill(&cvm_oct_tx_cleanup_tasklet);
}
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 9eee28f2940c..4a43e5589cfa 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -104,11 +104,10 @@ struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
u64 cvm_oct_tx_poll_interval;
-static void cvm_oct_rx_refill_worker(struct work_struct *work);
-static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
-
static void cvm_oct_rx_refill_worker(struct work_struct *work)
{
+ struct octeon_ethernet_platform *plat = container_of(work,
+ struct octeon_ethernet_platform, rx_refill_work.work);
/*
* FPA 0 may have been drained, try to refill it if we need
* more than num_packet_buffers / 2, otherwise normal receive
@@ -116,10 +115,10 @@ static void cvm_oct_rx_refill_worker(struct work_struct *work)
* could be received so cvm_oct_napi_poll would never be
* invoked to do the refill.
*/
- cvm_oct_rx_refill_pool(num_packet_buffers / 2);
+ cvm_oct_rx_refill_pool(plat->pdev, num_packet_buffers / 2);
if (!atomic_read(&cvm_oct_poll_queue_stopping))
- schedule_delayed_work(&cvm_oct_rx_refill_work, HZ);
+ schedule_delayed_work(&plat->rx_refill_work, HZ);
}
static void cvm_oct_periodic_worker(struct work_struct *work)
@@ -138,16 +137,16 @@ static void cvm_oct_periodic_worker(struct work_struct *work)
schedule_delayed_work(&priv->port_periodic_work, HZ);
}
-static void cvm_oct_configure_common_hw(void)
+static void cvm_oct_configure_common_hw(struct platform_device *pdev)
{
/* Setup the FPA */
cvmx_fpa_enable();
- cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
+ cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
num_packet_buffers);
- cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
+ cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
num_packet_buffers);
if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
- cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
+ cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_OUTPUT_BUFFER_POOL,
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 1024);
#ifdef __LITTLE_ENDIAN
@@ -678,6 +677,15 @@ static int cvm_oct_probe(struct platform_device *pdev)
int qos;
struct device_node *pip;
int mtu_overhead = ETH_HLEN + ETH_FCS_LEN;
+ struct octeon_ethernet_platform *plat;
+
+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ if (!plat)
+ return -ENOMEM;
+
+ plat->pdev = pdev;
+ INIT_DELAYED_WORK(&plat->rx_refill_work, cvm_oct_rx_refill_worker);
+ platform_set_drvdata(pdev, plat);
#if IS_ENABLED(CONFIG_VLAN_8021Q)
mtu_overhead += VLAN_HLEN;
@@ -689,7 +697,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
return -EINVAL;
}
- cvm_oct_configure_common_hw();
+ cvm_oct_configure_common_hw(pdev);
cvmx_helper_initialize_packet_io_global();
@@ -912,28 +920,29 @@ static int cvm_oct_probe(struct platform_device *pdev)
}
cvm_oct_tx_initialize();
- cvm_oct_rx_initialize();
+ cvm_oct_rx_initialize(pdev);
/*
* 150 uS: about 10 1500-byte packets at 1GE.
*/
cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
- schedule_delayed_work(&cvm_oct_rx_refill_work, HZ);
+ schedule_delayed_work(&plat->rx_refill_work, HZ);
return 0;
}
static void cvm_oct_remove(struct platform_device *pdev)
{
+ struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev);
int port;
cvmx_ipd_disable();
atomic_inc_return(&cvm_oct_poll_queue_stopping);
- cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
+ cancel_delayed_work_sync(&plat->rx_refill_work);
- cvm_oct_rx_shutdown();
+ cvm_oct_rx_shutdown(pdev);
cvm_oct_tx_shutdown();
cvmx_pko_disable();
@@ -958,12 +967,12 @@ static void cvm_oct_remove(struct platform_device *pdev)
cvmx_ipd_free_ptr();
/* Free the HW pools */
- cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
+ cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
num_packet_buffers);
- cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
+ cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
num_packet_buffers);
if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
- cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
+ cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_OUTPUT_BUFFER_POOL,
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
}
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index a6140705706f..0ac430db1e6e 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -11,6 +11,7 @@
#ifndef OCTEON_ETHERNET_H
#define OCTEON_ETHERNET_H
+#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/phy.h>
@@ -74,6 +75,19 @@ struct octeon_ethernet {
struct device_node *of_node;
};
+struct oct_rx_group {
+ int irq;
+ int group;
+ struct napi_struct napi;
+ struct platform_device *pdev;
+};
+
+struct octeon_ethernet_platform {
+ struct platform_device *pdev;
+ struct delayed_work rx_refill_work;
+ struct oct_rx_group rx_group[16];
+};
+
int cvm_oct_free_work(void *work_queue_entry);
int cvm_oct_rgmii_open(struct net_device *dev);
diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c
index d3f10a3cf972..fd112f3ad746 100644
--- a/drivers/staging/rtl8723bs/core/rtw_cmd.c
+++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c
@@ -895,7 +895,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 unicast_
if (enqueue) {
ph2c = rtw_zmalloc(sizeof(struct cmd_obj));
if (!ph2c) {
- kfree(psetstakey_para);
+ kfree_sensitive(psetstakey_para);
res = _FAIL;
goto exit;
}
@@ -903,7 +903,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 unicast_
psetstakey_rsp = rtw_zmalloc(sizeof(struct set_stakey_rsp));
if (!psetstakey_rsp) {
kfree(ph2c);
- kfree(psetstakey_para);
+ kfree_sensitive(psetstakey_para);
res = _FAIL;
goto exit;
}
@@ -914,7 +914,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 unicast_
res = rtw_enqueue_cmd(pcmdpriv, ph2c);
} else {
set_stakey_hdl(padapter, (u8 *)psetstakey_para);
- kfree(psetstakey_para);
+ kfree_sensitive(psetstakey_para);
}
exit:
return res;
@@ -954,7 +954,7 @@ u8 rtw_clearstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 enqueu
psetstakey_rsp = rtw_zmalloc(sizeof(struct set_stakey_rsp));
if (!psetstakey_rsp) {
kfree(ph2c);
- kfree(psetstakey_para);
+ kfree_sensitive(psetstakey_para);
res = _FAIL;
goto exit;
}
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index 003946633633..b1144c6c8c7a 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -745,6 +745,10 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att
u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
u16 attr_len = attr_data_len + 4;
+ /* Reject attributes whose claimed length runs past the IE */
+ if (attr_ptr + attr_len > wps_ie + wps_ielen)
+ break;
+
if (attr_id == target_attr_id) {
target_attr_ptr = attr_ptr;
@@ -1148,6 +1152,9 @@ int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *act
u8 c;
u8 a = ACT_PUBLIC_MAX;
+ if (frame_len < sizeof(struct ieee80211_hdr_3addr) + 2)
+ return false;
+
fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control);
if ((fc & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE))
diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
index 335e6002df70..3b5a6f685bce 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
@@ -310,8 +310,8 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct
status = _SUCCESS;
free_hal_data:
- if (status != _SUCCESS && padapter->HalData)
- kfree(padapter->HalData);
+ if (status != _SUCCESS)
+ rtw_hal_data_deinit(padapter);
if (status != _SUCCESS) {
rtw_wdev_unregister(padapter->rtw_wdev);
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 7d64ba7a4a05..86781bbd5ce0 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -599,7 +599,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par)
crtc = &par->crtc;
crtc->vidmem_size = sm750_dev->vidmem_size;
- if (sm750_dev->fb_count > 1)
+ if (g_dualview)
crtc->vidmem_size >>= 1;
/* setup crtc and output member */
@@ -927,7 +927,7 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
NO_PARAM:
if (sm750_dev->revid != SM750LE_REVISION_ID) {
- if (sm750_dev->fb_count > 1) {
+ if (g_dualview) {
if (swap)
sm750_dev->dataflow = sm750_dual_swap;
else
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 90b870f234f0..f0d7405d1b21 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -48,7 +48,7 @@ static struct iscsi_login *iscsi_login_init_conn(struct iscsit_conn *conn)
login->conn = conn;
login->first_request = 1;
- login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
+ login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS + 1, GFP_KERNEL);
if (!login->req_buf) {
pr_err("Unable to allocate memory for response buffer.\n");
goto out_login;
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 826358cbe810..fa78254a02d3 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -851,12 +851,12 @@ static int __maybe_unused imx_thermal_runtime_resume(struct device *dev)
ret = regmap_write(map, socdata->sensor_ctrl + REG_CLR,
socdata->power_down_mask);
if (ret)
- return ret;
+ goto disable_clk;
ret = regmap_write(map, socdata->sensor_ctrl + REG_SET,
socdata->measure_temp_mask);
if (ret)
- return ret;
+ goto disable_clk;
/*
* According to the temp sensor designers, it may require up to ~17us
@@ -865,6 +865,11 @@ static int __maybe_unused imx_thermal_runtime_resume(struct device *dev)
usleep_range(20, 50);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(data->thermal_clk);
+
+ return ret;
}
static const struct dev_pm_ops imx_thermal_pm_ops = {
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index f252bac676bc..1bf02f74d2c7 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -370,8 +370,10 @@ static void cleanup_odvp(struct int3400_thermal_priv *priv)
kfree(priv->odvp_attrs[i].attr.attr.name);
}
kfree(priv->odvp_attrs);
+ priv->odvp_attrs = NULL;
}
kfree(priv->odvp);
+ priv->odvp = NULL;
priv->odvp_count = 0;
}
@@ -653,7 +655,6 @@ static int int3400_thermal_probe(struct platform_device *pdev)
acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
int3400_notify);
free_sysfs:
- cleanup_odvp(priv);
if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
kfree(priv->data_vault);
@@ -667,6 +668,7 @@ static int int3400_thermal_probe(struct platform_device *pdev)
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
thermal_zone_device_unregister(priv->thermal);
free_art_trt:
+ cleanup_odvp(priv);
kfree(priv->trts);
kfree(priv->arts);
free_priv:
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index ff8657afb31d..ef00ef64a9a7 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -380,11 +380,20 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev)
if (data->ver > TMU_VER1) {
ret = regmap_clear_bits(data->regmap, REGS_TMR, TMR_CMD);
if (ret)
- return ret;
+ goto disable_clk;
}
/* Enable monitoring */
- return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
+ ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(data->clk);
+
+ return ret;
}
static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 293f8dd9fe0a..e442ce781b94 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -494,12 +494,6 @@ static int rcar_thermal_probe(struct platform_device *pdev)
"rcar_thermal", trips, ARRAY_SIZE(trips), 0, priv,
&rcar_thermal_zone_ops, NULL, 0,
idle);
-
- ret = thermal_zone_device_enable(priv->zone);
- if (ret) {
- thermal_zone_device_unregister(priv->zone);
- priv->zone = ERR_PTR(ret);
- }
}
if (IS_ERR(priv->zone)) {
dev_err(dev, "can't register thermal zone\n");
@@ -508,11 +502,12 @@ static int rcar_thermal_probe(struct platform_device *pdev)
goto error_unregister;
}
- if (chip->use_of_thermal) {
+ if (chip->use_of_thermal)
ret = thermal_add_hwmon_sysfs(priv->zone);
- if (ret)
- goto error_unregister;
- }
+ else
+ ret = thermal_zone_device_enable(priv->zone);
+ if (ret)
+ goto error_unregister;
rcar_thermal_irq_enable(priv);
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 4f9264d005c0..8bcc632ed408 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -79,7 +79,7 @@ config HVC_UDBG
config HVC_DCC
bool "ARM JTAG DCC console"
- depends on ARM || ARM64
+ depends on (ARM && (CPU_V6 || CPU_V6K || CPU_V7)) || ARM64
select HVC_DRIVER
select SERIAL_CORE_CONSOLE
help
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 4c107556ae31..17b8b5656519 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -2457,6 +2457,15 @@ static int pl011_console_setup(struct console *co, char *options)
return uart_set_options(&uap->port, co, baud, parity, bits, flow);
}
+static int pl011_console_exit(struct console *co)
+{
+ struct uart_amba_port *uap = amba_ports[co->index];
+
+ clk_unprepare(uap->clk);
+
+ return 0;
+}
+
/**
* pl011_console_match - non-standard console matching
* @co: registering console
@@ -2523,6 +2532,7 @@ static struct console amba_console = {
.write = pl011_console_write,
.device = uart_console_device,
.setup = pl011_console_setup,
+ .exit = pl011_console_exit,
.match = pl011_console_match,
.flags = CON_PRINTBUFFER | CON_ANYTIME,
.index = -1,
diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
index 99225f1e02ac..6e5458b08554 100644
--- a/drivers/tty/serial/ma35d1_serial.c
+++ b/drivers/tty/serial/ma35d1_serial.c
@@ -608,8 +608,14 @@ static int __init ma35d1serial_console_setup(struct console *co, char *options)
if (!np || !p)
return -ENODEV;
- if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0)
+ if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0) {
+ of_node_put(np);
+ ma35d1serial_uart_nodes[co->index] = NULL;
return -EINVAL;
+ }
+
+ of_node_put(np);
+ ma35d1serial_uart_nodes[co->index] = NULL;
p->port.iobase = val32[1];
p->port.membase = ioremap(p->port.iobase, MA35_UART_REG_SIZE);
@@ -648,8 +654,10 @@ static void ma35d1serial_console_init_port(void)
of_node_get(np);
ma35d1serial_uart_nodes[i] = np;
i++;
- if (i == MA35_UART_NR)
+ if (i == MA35_UART_NR) {
+ of_node_put(np);
break;
+ }
}
}
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 117abcf366d9..6781bac852cf 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3157,8 +3157,10 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
driver->cdevs[index]->ops = &tty_fops;
driver->cdevs[index]->owner = driver->owner;
err = cdev_add(driver->cdevs[index], dev, count);
- if (err)
+ if (err) {
kobject_put(&driver->cdevs[index]->kobj);
+ driver->cdevs[index] = NULL;
+ }
return err;
}
diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c
index e3baed6c70bd..6dece24baad1 100644
--- a/drivers/ufs/core/ufs-debugfs.c
+++ b/drivers/ufs/core/ufs-debugfs.c
@@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf,
char val_str[16] = { };
int val, ret;
- if (count > sizeof(val_str))
+ if (count >= sizeof(val_str))
return -EINVAL;
if (copy_from_user(val_str, buf, count))
return -EFAULT;
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 3bbe1088b7e4..368459dd8b3f 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -1187,11 +1187,13 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
{
const struct scsi_device *sdev;
+ unsigned long flags;
u32 pending = 0;
- lockdep_assert_held(hba->host->host_lock);
+ spin_lock_irqsave(hba->host->host_lock, flags);
__shost_for_each_device(sdev, hba->host)
pending += sbitmap_weight(&sdev->budget_map);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
return pending;
}
@@ -1205,7 +1207,6 @@ static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
u64 wait_timeout_us)
{
- unsigned long flags;
int ret = 0;
u32 tm_doorbell;
u32 tr_pending;
@@ -1213,7 +1214,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
ktime_t start;
ufshcd_hold(hba);
- spin_lock_irqsave(hba->host->host_lock, flags);
/*
* Wait for all the outstanding tasks/transfer requests.
* Verify by checking the doorbell registers are clear.
@@ -1234,7 +1234,7 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
break;
}
- spin_unlock_irqrestore(hba->host->host_lock, flags);
+ __set_current_state(TASK_UNINTERRUPTIBLE);
io_schedule_timeout(msecs_to_jiffies(20));
if (ktime_to_us(ktime_sub(ktime_get(), start)) >
wait_timeout_us) {
@@ -1246,7 +1246,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
*/
do_last_check = true;
}
- spin_lock_irqsave(hba->host->host_lock, flags);
} while (tm_doorbell || tr_pending);
if (timeout) {
@@ -1256,7 +1255,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
ret = -EBUSY;
}
out:
- spin_unlock_irqrestore(hba->host->host_lock, flags);
ufshcd_release(hba);
return ret;
}
@@ -7115,15 +7113,13 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq));
ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function);
- /* send command to the controller */
__set_bit(task_tag, &hba->outstanding_tasks);
- ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL);
- /* Make sure that doorbell is committed immediately */
- wmb();
-
spin_unlock_irqrestore(host->host_lock, flags);
+ /* send command to the controller */
+ ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL);
+
ufshcd_add_tm_upiu_trace(hba, task_tag, UFS_TM_SEND);
/* wait until the task management command is completed */
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 2d572f6c8ec8..887ba0082d16 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -990,6 +990,11 @@ int __uio_register_device(struct module *owner,
err_request_irq:
uio_dev_del_attributes(idev);
err_uio_dev_add_attributes:
+ mutex_lock(&idev->info_lock);
+ idev->info = NULL;
+ mutex_unlock(&idev->info_lock);
+ wake_up_interruptible(&idev->wait);
+ kill_fasync(&idev->async_queue, SIGIO, POLL_HUP);
device_del(&idev->dev);
err_device_create:
uio_free_minor(idev->minor);
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 1cdb8758ae01..bbee6bfed5b8 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -917,8 +917,8 @@ static int usbatm_atm_init(struct usbatm_data *instance)
instance->atm_dev = atm_dev;
- atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
- atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+ atm_dev->ci_range.vpi_bits = 8;
+ atm_dev->ci_range.vci_bits = 16;
atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
/* temp init ATM device, set to 128kbit */
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index b84efae26e15..532c1965392d 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -417,6 +417,9 @@ EXPORT_SYMBOL_GPL(usb_of_get_companion_dev);
struct dentry *usb_debug_root;
EXPORT_SYMBOL_GPL(usb_debug_root);
+DEFINE_MUTEX(usb_dynids_lock);
+EXPORT_SYMBOL_GPL(usb_dynids_lock);
+
static int __init usb_common_init(void)
{
usb_debug_root = debugfs_create_dir("usb", NULL);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f58a0299fb3b..a159c465708d 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -95,9 +95,9 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
}
}
- spin_lock(&dynids->lock);
+ mutex_lock(&usb_dynids_lock);
list_add_tail(&dynid->node, &dynids->list);
- spin_unlock(&dynids->lock);
+ mutex_unlock(&usb_dynids_lock);
retval = driver_attach(driver);
@@ -160,7 +160,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
if (fields < 2)
return -EINVAL;
- spin_lock(&usb_driver->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) {
struct usb_device_id *id = &dynid->id;
@@ -171,7 +171,6 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
break;
}
}
- spin_unlock(&usb_driver->dynids.lock);
return count;
}
@@ -189,13 +188,13 @@ static int usb_create_newid_files(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL) {
- error = driver_create_file(&usb_drv->drvwrap.driver,
+ error = driver_create_file(&usb_drv->driver,
&driver_attr_new_id);
if (error == 0) {
- error = driver_create_file(&usb_drv->drvwrap.driver,
+ error = driver_create_file(&usb_drv->driver,
&driver_attr_remove_id);
if (error)
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@@ -209,9 +208,9 @@ static void usb_remove_newid_files(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL) {
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_remove_id);
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@@ -220,27 +219,26 @@ static void usb_free_dynids(struct usb_driver *usb_drv)
{
struct usb_dynid *dynid, *n;
- spin_lock(&usb_drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
- spin_unlock(&usb_drv->dynids.lock);
}
static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
- struct usb_driver *drv)
+ const struct usb_driver *drv,
+ struct usb_device_id *id_copy)
{
struct usb_dynid *dynid;
- spin_lock(&drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (usb_match_one_id(intf, &dynid->id)) {
- spin_unlock(&drv->dynids.lock);
- return &dynid->id;
+ *id_copy = dynid->id;
+ return id_copy;
}
}
- spin_unlock(&drv->dynids.lock);
return NULL;
}
@@ -321,6 +319,7 @@ static int usb_probe_interface(struct device *dev)
struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
+ struct usb_device_id id_copy;
int error = -ENODEV;
int lpm_disable_error = -ENODEV;
@@ -340,7 +339,7 @@ static int usb_probe_interface(struct device *dev)
return error;
}
- id = usb_match_dynamic_id(intf, driver);
+ id = usb_match_dynamic_id(intf, driver, &id_copy);
if (!id)
id = usb_match_id(intf, driver->id_table);
if (!id)
@@ -549,7 +548,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
- dev->driver = &driver->drvwrap.driver;
+ dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
@@ -612,7 +611,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
struct device *dev = &iface->dev;
/* this should never happen, don't release something that's not ours */
- if (!dev->driver || dev->driver != &driver->drvwrap.driver)
+ if (!dev->driver || dev->driver != &driver->driver)
return;
/* don't release from within disconnect() */
@@ -877,8 +876,9 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
- struct usb_driver *usb_drv;
+ const struct usb_driver *usb_drv;
const struct usb_device_id *id;
+ struct usb_device_id id_copy;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
@@ -891,7 +891,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
if (id)
return 1;
- id = usb_match_dynamic_id(intf, usb_drv);
+ id = usb_match_dynamic_id(intf, usb_drv, &id_copy);
if (id)
return 1;
}
@@ -947,7 +947,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
int ret;
/* Don't reprobe if current driver isn't usb_generic_driver */
- if (dev->driver != &usb_generic_driver.drvwrap.driver)
+ if (dev->driver != &usb_generic_driver.driver)
return 0;
udev = to_usb_device(dev);
@@ -961,6 +961,11 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
return 0;
}
+bool is_usb_device_driver(const struct device_driver *drv)
+{
+ return drv->probe == usb_probe_device;
+}
+
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
@@ -980,15 +985,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
if (usb_disabled())
return -ENODEV;
- new_udriver->drvwrap.for_devices = 1;
- new_udriver->drvwrap.driver.name = new_udriver->name;
- new_udriver->drvwrap.driver.bus = &usb_bus_type;
- new_udriver->drvwrap.driver.probe = usb_probe_device;
- new_udriver->drvwrap.driver.remove = usb_unbind_device;
- new_udriver->drvwrap.driver.owner = owner;
- new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;
+ new_udriver->driver.name = new_udriver->name;
+ new_udriver->driver.bus = &usb_bus_type;
+ new_udriver->driver.probe = usb_probe_device;
+ new_udriver->driver.remove = usb_unbind_device;
+ new_udriver->driver.owner = owner;
+ new_udriver->driver.dev_groups = new_udriver->dev_groups;
- retval = driver_register(&new_udriver->drvwrap.driver);
+ retval = driver_register(&new_udriver->driver);
if (!retval) {
pr_info("%s: registered new device driver %s\n",
@@ -1020,7 +1024,7 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver)
pr_info("%s: deregistering device driver %s\n",
usbcore_name, udriver->name);
- driver_unregister(&udriver->drvwrap.driver);
+ driver_unregister(&udriver->driver);
}
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
@@ -1048,18 +1052,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
if (usb_disabled())
return -ENODEV;
- new_driver->drvwrap.for_devices = 0;
- new_driver->drvwrap.driver.name = new_driver->name;
- new_driver->drvwrap.driver.bus = &usb_bus_type;
- new_driver->drvwrap.driver.probe = usb_probe_interface;
- new_driver->drvwrap.driver.remove = usb_unbind_interface;
- new_driver->drvwrap.driver.owner = owner;
- new_driver->drvwrap.driver.mod_name = mod_name;
- new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
- spin_lock_init(&new_driver->dynids.lock);
+ new_driver->driver.name = new_driver->name;
+ new_driver->driver.bus = &usb_bus_type;
+ new_driver->driver.probe = usb_probe_interface;
+ new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.owner = owner;
+ new_driver->driver.mod_name = mod_name;
+ new_driver->driver.dev_groups = new_driver->dev_groups;
INIT_LIST_HEAD(&new_driver->dynids.list);
- retval = driver_register(&new_driver->drvwrap.driver);
+ retval = driver_register(&new_driver->driver);
if (retval)
goto out;
@@ -1074,7 +1076,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
return retval;
out_newid:
- driver_unregister(&new_driver->drvwrap.driver);
+ driver_unregister(&new_driver->driver);
pr_err("%s: error %d registering interface driver %s\n",
usbcore_name, retval, new_driver->name);
@@ -1099,7 +1101,7 @@ void usb_deregister(struct usb_driver *driver)
usbcore_name, driver->name);
usb_remove_newid_files(driver);
- driver_unregister(&driver->drvwrap.driver);
+ driver_unregister(&driver->driver);
usb_free_dynids(driver);
}
EXPORT_SYMBOL_GPL(usb_deregister);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index da6d5e5f79e7..477b05df35e7 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -431,7 +431,7 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
struct device *dev;
argb.minor = minor;
- argb.drv = &drv->drvwrap.driver;
+ argb.drv = &drv->driver;
dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 60363153fc3f..bfecb50773b6 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -175,13 +175,7 @@ static inline int is_root_hub(struct usb_device *udev)
return (udev->parent == NULL);
}
-/* Do the same for device drivers and interface drivers. */
-
-static inline int is_usb_device_driver(struct device_driver *drv)
-{
- return container_of(drv, struct usbdrv_wrap, driver)->
- for_devices;
-}
+extern bool is_usb_device_driver(const struct device_driver *drv);
/* for labeling diagnostics */
extern const char *usbcore_name;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index c4a06eeb61fe..aab4023eb1fe 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -4677,6 +4677,7 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
{
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
unsigned long flags;
+ int ret = 0;
dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on,
hsotg->op_state);
@@ -4688,6 +4689,13 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
}
spin_lock_irqsave(&hsotg->lock, flags);
+ if (hsotg->in_ppd) {
+ ret = dwc2_exit_partial_power_down(hsotg, 0, true);
+ if (ret) {
+ dev_err(hsotg->dev, "exit partial_power_down failed\n");
+ goto exit;
+ }
+ }
if (is_on) {
hsotg->enabled = 1;
dwc2_hsotg_core_init_disconnected(hsotg, false);
@@ -4701,9 +4709,10 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
}
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+exit:
spin_unlock_irqrestore(&hsotg->lock, flags);
- return 0;
+ return ret;
}
static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 9e8af571448b..75392bf7bd34 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1174,7 +1174,7 @@ static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page)
struct gadget_info *gi = os_desc_item_to_gadget_info(item);
int res;
- res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN,
+ res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN / 2,
UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1);
page[res++] = '\n';
@@ -1196,7 +1196,7 @@ static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page,
mutex_lock(&gi->lock);
res = utf8s_to_utf16s(page, l,
UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign,
- OS_STRING_QW_SIGN_LEN);
+ OS_STRING_QW_SIGN_LEN / 2);
if (res > 0)
res = len;
mutex_unlock(&gi->lock);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index f4658e898673..9a8c94bd9072 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -515,6 +515,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
return -EIDRM;
+retry:
/* Acquire mutex */
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
if (ret < 0)
@@ -549,10 +550,15 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
break;
}
- if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
- ffs->ev.count)) {
- ret = -EINTR;
- break;
+ if (!ffs->ev.count) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ mutex_unlock(&ffs->mutex);
+
+ if (wait_event_interruptible_exclusive(ffs->ev.waitq,
+ ffs->ev.count))
+ return -EINTR;
+
+ goto retry;
}
/* unlocks spinlock */
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index e01d57a5327c..01c35deaefe1 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2961,7 +2961,7 @@ EXPORT_SYMBOL_GPL(fsg_common_create_lun);
int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
{
- char buf[8]; /* enough for 100000000 different numbers, decimal */
+ char buf[14];
int i, rc;
fsg_common_remove_luns(common);
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index d7ed50ff380c..0f47cca32c3c 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -1145,7 +1145,7 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
if (!usb_ep->reqs)
return -EINVAL;
- for (i = 0; i < midi2->info.num_reqs; i++) {
+ for (i = 0; i < usb_ep->num_reqs; i++) {
if (usb_ep->reqs[i].req)
continue;
usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
@@ -1160,10 +1160,9 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
/* Free allocated requests */
static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
{
- struct f_midi2 *midi2 = usb_ep->card;
int i;
- for (i = 0; i < midi2->info.num_reqs; i++) {
+ for (i = 0; i < usb_ep->num_reqs; i++) {
if (!usb_ep->reqs[i].req)
continue;
free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
@@ -2181,13 +2180,13 @@ static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
/* generic show/store for string */
static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
- const char *str, char *page)
+ const char **strp, char *page)
{
int result = 0;
mutex_lock(&opts->lock);
- if (str)
- result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+ if (*strp)
+ result = scnprintf(page, PAGE_SIZE, "%s\n", *strp);
mutex_unlock(&opts->lock);
return result;
}
@@ -2281,7 +2280,7 @@ static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
{
struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
- return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+ return f_midi2_opts_str_show(opts->ep->opts, &opts->info.name, page);
}
static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
@@ -2438,7 +2437,7 @@ static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
- return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+ return f_midi2_opts_str_show(opts->opts, &opts->info.name, page);\
} \
\
static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
@@ -2476,6 +2475,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(&opts->group);
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2592,7 +2592,7 @@ static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
{
struct f_midi2_opts *opts = to_f_midi2_opts(item);
- return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+ return f_midi2_opts_str_show(opts, &opts->info.iface_name, page);
}
static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
@@ -2642,6 +2642,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 0f1cade591ab..96ff55f6bc2e 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1330,19 +1330,25 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
func_inst);
- mutex_lock(&opts->dep_lock);
- if (!opts->ready)
- goto unlock_dep;
+ if (!READ_ONCE(opts->ready))
+ goto unlock_inst;
if (opts->has_dep) {
if (!try_module_get(opts->dependent))
- goto unlock_dep;
+ goto unlock_inst;
} else {
+ /*
+ * configfs_depend_item_unlocked() may acquire the configfs
+ * root inode lock when the target belongs to a different
+ * subsystem. Calling it under dep_lock would create a
+ * circular dependency:
+ * dep_lock -> configfs inode lock -> su_mutex -> dep_lock
+ */
ret = configfs_depend_item_unlocked(
wwn->wwn_group.cg_subsys,
&opts->func_inst.group.cg_item);
if (ret)
- goto unlock_dep;
+ goto unlock_inst;
}
tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
@@ -1368,7 +1374,6 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
tpg_instances[i].tpg = tpg;
tpg->fi = tpg_instances[i].func_inst;
- mutex_unlock(&opts->dep_lock);
mutex_unlock(&tpg_instances_lock);
return &tpg->se_tpg;
@@ -1381,8 +1386,6 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
module_put(opts->dependent);
else
configfs_undepend_item_unlocked(&opts->func_inst.group.cg_item);
-unlock_dep:
- mutex_unlock(&opts->dep_lock);
unlock_inst:
mutex_unlock(&tpg_instances_lock);
@@ -2314,9 +2317,7 @@ static int tcm_set_name(struct usb_function_instance *f, const char *name)
pr_debug("tcm: Activating %s\n", name);
- mutex_lock(&opts->dep_lock);
- opts->ready = true;
- mutex_unlock(&opts->dep_lock);
+ WRITE_ONCE(opts->ready, true);
return 0;
}
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index f8ed471ab9a8..6b1efbb57072 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -888,60 +888,10 @@ UAC1_INT_ATTRIBUTE(req_buf_size);
UAC1_INT_ATTRIBUTE(req_count);
UAC1_INT_ATTRIBUTE(audio_buf_size);
-#define UAC1_STR_ATTRIBUTE(name) \
-static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
- char *page) \
-{ \
- struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \
- int result; \
- \
- mutex_lock(&opts->lock); \
- result = sprintf(page, "%s\n", opts->name); \
- mutex_unlock(&opts->lock); \
- \
- return result; \
-} \
- \
-static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
- const char *page, size_t len) \
-{ \
- struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \
- int ret = -EBUSY; \
- char *tmp; \
- \
- mutex_lock(&opts->lock); \
- if (opts->refcnt) \
- goto end; \
- \
- tmp = kstrndup(page, len, GFP_KERNEL); \
- if (tmp) { \
- ret = -ENOMEM; \
- goto end; \
- } \
- if (opts->name##_alloc) \
- kfree(opts->name); \
- opts->name##_alloc = true; \
- opts->name = tmp; \
- ret = len; \
- \
-end: \
- mutex_unlock(&opts->lock); \
- return ret; \
-} \
- \
-CONFIGFS_ATTR(f_uac1_opts_, name)
-
-UAC1_STR_ATTRIBUTE(fn_play);
-UAC1_STR_ATTRIBUTE(fn_cap);
-UAC1_STR_ATTRIBUTE(fn_cntl);
-
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_req_buf_size,
&f_uac1_opts_attr_req_count,
&f_uac1_opts_attr_audio_buf_size,
- &f_uac1_opts_attr_fn_play,
- &f_uac1_opts_attr_fn_cap,
- &f_uac1_opts_attr_fn_cntl,
NULL,
};
@@ -956,12 +906,6 @@ static void f_audio_free_inst(struct usb_function_instance *f)
struct f_uac1_legacy_opts *opts;
opts = container_of(f, struct f_uac1_legacy_opts, func_inst);
- if (opts->fn_play_alloc)
- kfree(opts->fn_play);
- if (opts->fn_cap_alloc)
- kfree(opts->fn_cap);
- if (opts->fn_cntl_alloc)
- kfree(opts->fn_cntl);
kfree(opts);
}
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index e0182d4eca2c..cce995b29259 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -876,9 +876,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
v4l2_error:
v4l2_device_unregister(&uvc->v4l2_dev);
error:
- if (uvc->control_req)
+ if (uvc->control_req) {
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+ uvc->control_req = NULL;
+ }
kfree(uvc->control_buf);
+ uvc->control_buf = NULL;
usb_free_all_descriptors(f);
return ret;
@@ -1060,7 +1063,9 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvc->vdev_release_done = NULL;
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+ uvc->control_req = NULL;
kfree(uvc->control_buf);
+ uvc->control_buf = NULL;
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 0be0966973c7..6cbcb8c4710d 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -1177,6 +1177,20 @@ static struct snd_kcontrol_new u_audio_controls[] = {
},
};
+static void u_audio_card_free(struct snd_card *card)
+{
+ struct snd_uac_chip *uac = card->private_data;
+
+ if (!uac)
+ return;
+
+ kfree(uac->p_prm.reqs);
+ kfree(uac->c_prm.reqs);
+ kfree(uac->p_prm.rbuf);
+ kfree(uac->c_prm.rbuf);
+ kfree(uac);
+}
+
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
const char *card_name)
{
@@ -1258,6 +1272,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
goto fail;
uac->card = card;
+ card->private_data = uac;
+ card->private_free = u_audio_card_free;
/*
* Create first PCM device
@@ -1425,6 +1441,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
snd_fail:
snd_card_free(card);
+ return err;
+
fail:
kfree(uac->p_prm.reqs);
kfree(uac->c_prm.reqs);
@@ -1450,12 +1468,6 @@ void g_audio_cleanup(struct g_audio *g_audio)
card = uac->card;
if (card)
snd_card_free_when_closed(card);
-
- kfree(uac->p_prm.reqs);
- kfree(uac->c_prm.reqs);
- kfree(uac->p_prm.rbuf);
- kfree(uac->c_prm.rbuf);
- kfree(uac);
}
EXPORT_SYMBOL_GPL(g_audio_cleanup);
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h
index b5df9bcbbeba..b9ddae550ff3 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.h
+++ b/drivers/usb/gadget/function/u_uac1_legacy.h
@@ -62,9 +62,6 @@ struct f_uac1_legacy_opts {
char *fn_cap;
char *fn_cntl;
unsigned bound:1;
- unsigned fn_play_alloc:1;
- unsigned fn_cap_alloc:1;
- unsigned fn_cntl_alloc:1;
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
index 203361a64212..70e31c40e267 100644
--- a/drivers/usb/gadget/functions.c
+++ b/drivers/usb/gadget/functions.c
@@ -70,7 +70,7 @@ void usb_put_function_instance(struct usb_function_instance *fi)
{
struct module *mod;
- if (!fi)
+ if (!fi || !fi->fd)
return;
mod = fi->fd->mod;
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index ce9e31f3d26b..07b1efc94428 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1247,10 +1247,11 @@ ep0_poll (struct file *fd, poll_table *wait)
static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
{
struct dev_data *dev = fd->private_data;
- struct usb_gadget *gadget = dev->gadget;
+ struct usb_gadget *gadget;
long ret = -ENOTTY;
spin_lock_irq(&dev->lock);
+ gadget = dev->gadget;
if (dev->state == STATE_DEV_OPENED ||
dev->state == STATE_DEV_UNBOUND) {
/* Not bound to a UDC */
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 4868286574a1..efcceb886bb4 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -1434,16 +1434,12 @@ static void ast_udc_init_hw(struct ast_udc_dev *udc)
ast_udc_write(udc, 0, AST_UDC_EP0_CTRL);
}
-static int ast_udc_remove(struct platform_device *pdev)
+static void ast_udc_cleanup(struct platform_device *pdev)
{
struct ast_udc_dev *udc = platform_get_drvdata(pdev);
unsigned long flags;
u32 ctrl;
- usb_del_gadget_udc(&udc->gadget);
- if (udc->driver)
- return -EBUSY;
-
spin_lock_irqsave(&udc->lock, flags);
/* Disable upstream port connection */
@@ -1461,8 +1457,26 @@ static int ast_udc_remove(struct platform_device *pdev)
udc->ep0_buf_dma);
udc->ep0_buf = NULL;
+}
- return 0;
+static void ast_udc_remove(struct platform_device *pdev)
+{
+ struct ast_udc_dev *udc = platform_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&udc->gadget);
+ if (udc->driver) {
+ /*
+ * This is broken as only some cleanup is skipped, *udev is
+ * freed and the register mapping goes away. Any further usage
+ * probably crashes. Also the device is unbound, so the skipped
+ * cleanup is never catched up later.
+ */
+ dev_alert(&pdev->dev,
+ "Driver is busy and still going away. Fasten your seat belts!\n");
+ return;
+ }
+
+ ast_udc_cleanup(pdev);
}
static int ast_udc_probe(struct platform_device *pdev)
@@ -1517,6 +1531,12 @@ static int ast_udc_probe(struct platform_device *pdev)
AST_UDC_NUM_ENDPOINTS,
&udc->ep0_buf_dma, GFP_KERNEL);
+ if (!udc->ep0_buf) {
+ clk_disable_unprepare(udc->clk);
+ rc = -ENOMEM;
+ goto err;
+ }
+
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.max_speed = USB_SPEED_HIGH;
udc->creq = udc->reg + AST_UDC_SETUP0;
@@ -1546,20 +1566,20 @@ static int ast_udc_probe(struct platform_device *pdev)
udc->irq = platform_get_irq(pdev, 0);
if (udc->irq < 0) {
rc = udc->irq;
- goto err;
+ goto err_cleanup;
}
rc = devm_request_irq(&pdev->dev, udc->irq, ast_udc_isr, 0,
KBUILD_MODNAME, udc);
if (rc) {
dev_err(&pdev->dev, "Failed to request interrupt\n");
- goto err;
+ goto err_cleanup;
}
rc = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (rc) {
dev_err(&pdev->dev, "Failed to add gadget udc\n");
- goto err;
+ goto err_cleanup;
}
dev_info(&pdev->dev, "Initialized udc in USB%s mode\n",
@@ -1567,9 +1587,10 @@ static int ast_udc_probe(struct platform_device *pdev)
return 0;
+err_cleanup:
+ ast_udc_cleanup(pdev);
err:
dev_err(&pdev->dev, "Failed to udc probe, rc:0x%x\n", rc);
- ast_udc_remove(pdev);
return rc;
}
@@ -1583,7 +1604,7 @@ MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids);
static struct platform_driver ast_udc_driver = {
.probe = ast_udc_probe,
- .remove = ast_udc_remove,
+ .remove_new = ast_udc_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ast_udc_of_dt_ids,
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 51b665f15c8e..91fe432fc674 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1951,7 +1951,6 @@ static int r8a66597_probe(struct platform_device *pdev)
return 0;
err_add_udc:
- r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
clean_up2:
if (r8a66597->pdata->on_chip)
clk_disable_unprepare(r8a66597->clk);
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
index 547af2ed9e5e..835f1c3aa707 100644
--- a/drivers/usb/gadget/udc/snps_udc_plat.c
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -160,10 +160,9 @@ static int udc_plat_probe(struct platform_device *pdev)
if (of_property_present(dev->of_node, "extcon")) {
udc->edev = extcon_get_edev_by_phandle(dev, 0);
if (IS_ERR(udc->edev)) {
- if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- dev_err(dev, "Invalid or missing extcon\n");
ret = PTR_ERR(udc->edev);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Invalid or missing extcon\n");
goto exit_phy;
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 2f20faa90b24..d06f48ec3045 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -802,21 +802,18 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
usb_hcd_giveback_urb(hcd, urb, status);
}
-static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
- struct xhci_ring *ring, struct xhci_td *td)
+static void xhci_unmap_one_bounce_buffer(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_td *td,
+ struct xhci_segment *seg)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- struct xhci_segment *seg = td->bounce_seg;
struct urb *urb = td->urb;
size_t len;
- if (!ring || !seg || !urb)
- return;
-
if (usb_urb_dir_out(urb)) {
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
DMA_TO_DEVICE);
- return;
+ goto done;
}
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
@@ -832,10 +829,29 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
memcpy(urb->transfer_buffer + seg->bounce_offs, seg->bounce_buf,
seg->bounce_len);
}
+done:
seg->bounce_len = 0;
seg->bounce_offs = 0;
}
+static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_td *td)
+{
+ struct xhci_segment *seg;
+ int i = 0;
+
+ if (!td->bounce_seg || !ring || !td->urb)
+ return;
+
+ /* td->bounce_seg is the last one bounced, unmap them all */
+ for (seg = td->start_seg; i++ < ring->num_segs; seg = seg->next) {
+ if (seg->bounce_len)
+ xhci_unmap_one_bounce_buffer(xhci, ring, td, seg);
+ if (seg == td->bounce_seg)
+ break;
+ }
+}
+
static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_ring *ep_ring, int status)
{
@@ -3787,7 +3803,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
&trb_buff_len,
ring->enq_seg)) {
send_addr = ring->enq_seg->bounce_dma;
- /* assuming TD won't span 2 segs */
+ /* TD bounced at least, and last on this seg */
td->bounce_seg = ring->enq_seg;
}
}
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 4d9583d21202..2660dae79c30 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -1001,13 +1001,13 @@ static int __init usb_mdc800_init (void)
mdc800->downloaded = 0;
mdc800->written = 0;
- mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL);
+ mdc800->irq_urb_buffer=kzalloc (8, GFP_KERNEL);
if (!mdc800->irq_urb_buffer)
goto cleanup_on_fail;
mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL);
if (!mdc800->write_urb_buffer)
goto cleanup_on_fail;
- mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL);
+ mdc800->download_urb_buffer=kzalloc (64, GFP_KERNEL);
if (!mdc800->download_urb_buffer)
goto cleanup_on_fail;
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
index d72130eda57d..cce3bd4da8e2 100644
--- a/drivers/usb/misc/onboard_usb_hub.c
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -233,7 +233,7 @@ static void onboard_hub_attach_usb_driver(struct work_struct *work)
{
int err;
- err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver);
+ err = driver_attach(&onboard_hub_usbdev_driver.driver);
if (err)
pr_err("Failed to attach USB driver: %d\n", err);
}
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index a3a6282893d0..ca8cb6c3a01b 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -1037,9 +1037,14 @@ int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg)
if (!mtu->gadget_driver)
return 0;
- if (mtu->connected)
+ /* Prevent runtime suspend when active connection exists */
+ if (mtu->connected && PMSG_IS_AUTO(msg))
return -EBUSY;
+ /* Perform soft disconnect for system suspend */
+ if (mtu->softconnect && !PMSG_IS_AUTO(msg))
+ mtu3_dev_on_off(mtu, 0);
+
mtu3_dev_suspend(mtu);
synchronize_irq(mtu->irq);
@@ -1055,5 +1060,9 @@ int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg)
mtu3_dev_resume(mtu);
+ /* Restore soft connect for system resume */
+ if (mtu->softconnect && !PMSG_IS_AUTO(msg))
+ mtu3_dev_on_off(mtu, 1);
+
return 0;
}
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 79617bb0a70e..b4984a24a7e0 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -45,7 +45,7 @@
static const char driver_name[] = "fsl-usb2-otg";
-const pm_message_t otg_suspend_state = {
+static const pm_message_t otg_suspend_state = {
.event = 1,
};
@@ -56,11 +56,11 @@ static struct fsl_otg *fsl_otg_dev;
static int srp_wait_done;
/* FSM timers */
-struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
+static struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
*b_ase0_brst_tmr, *b_se0_srp_tmr;
/* Driver specific timers */
-struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
+static struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
*b_srp_wait_tmr, *a_wait_enum_tmr;
static struct list_head active_timers;
@@ -101,7 +101,7 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
#define fsl_writel(val, addr) writel(val, addr)
#endif /* CONFIG_PPC32 */
-int write_ulpi(u8 addr, u8 data)
+static int write_ulpi(u8 addr, u8 data)
{
u32 temp;
@@ -114,7 +114,7 @@ int write_ulpi(u8 addr, u8 data)
/* Operations that will be called from OTG Finite State Machine */
/* Charge vbus for vbus pulsing in SRP */
-void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
+static void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
{
u32 tmp;
@@ -132,7 +132,7 @@ void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
}
/* Discharge vbus through a resistor to ground */
-void fsl_otg_dischrg_vbus(int on)
+static void fsl_otg_dischrg_vbus(int on)
{
u32 tmp;
@@ -150,7 +150,7 @@ void fsl_otg_dischrg_vbus(int on)
}
/* A-device driver vbus, controlled through PP bit in PORTSC */
-void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
+static void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
{
u32 tmp;
@@ -168,7 +168,7 @@ void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
* Pull-up D+, signalling connect by periperal. Also used in
* data-line pulsing in SRP
*/
-void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
+static void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
{
u32 tmp;
@@ -187,7 +187,7 @@ void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
* port. In host mode, controller will automatically send SOF.
* Suspend will block the data on the port.
*/
-void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
+static void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
{
u32 tmp;
@@ -202,7 +202,7 @@ void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
}
/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
-void fsl_otg_start_pulse(struct otg_fsm *fsm)
+static void fsl_otg_start_pulse(struct otg_fsm *fsm)
{
u32 tmp;
@@ -218,7 +218,7 @@ void fsl_otg_start_pulse(struct otg_fsm *fsm)
fsl_otg_add_timer(fsm, b_data_pulse_tmr);
}
-void b_data_pulse_end(unsigned long foo)
+static void b_data_pulse_end(unsigned long foo)
{
#ifdef HA_DATA_PULSE
#else
@@ -229,7 +229,7 @@ void b_data_pulse_end(unsigned long foo)
fsl_otg_pulse_vbus();
}
-void fsl_otg_pulse_vbus(void)
+static void fsl_otg_pulse_vbus(void)
{
srp_wait_done = 0;
fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
@@ -237,7 +237,7 @@ void fsl_otg_pulse_vbus(void)
fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
}
-void b_vbus_pulse_end(unsigned long foo)
+static void b_vbus_pulse_end(unsigned long foo)
{
fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
@@ -250,7 +250,7 @@ void b_vbus_pulse_end(unsigned long foo)
fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
}
-void b_srp_end(unsigned long foo)
+static void b_srp_end(unsigned long foo)
{
fsl_otg_dischrg_vbus(0);
srp_wait_done = 1;
@@ -265,7 +265,7 @@ void b_srp_end(unsigned long foo)
* a_host will start by SRP. It needs to set b_hnp_enable before
* actually suspending to start HNP
*/
-void a_wait_enum(unsigned long foo)
+static void a_wait_enum(unsigned long foo)
{
VDBG("a_wait_enum timeout\n");
if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
@@ -275,13 +275,13 @@ void a_wait_enum(unsigned long foo)
}
/* The timeout callback function to set time out bit */
-void set_tmout(unsigned long indicator)
+static void set_tmout(unsigned long indicator)
{
*(int *)indicator = 1;
}
/* Initialize timers */
-int fsl_otg_init_timers(struct otg_fsm *fsm)
+static int fsl_otg_init_timers(struct otg_fsm *fsm)
{
/* FSM used timers */
a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
@@ -338,7 +338,7 @@ int fsl_otg_init_timers(struct otg_fsm *fsm)
}
/* Uninitialize timers */
-void fsl_otg_uninit_timers(void)
+static void fsl_otg_uninit_timers(void)
{
/* FSM used timers */
kfree(a_wait_vrise_tmr);
@@ -390,7 +390,7 @@ static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t)
}
/* Add timer to timer list */
-void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
+static void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
{
struct fsl_otg_timer *timer = gtimer;
struct fsl_otg_timer *tmp_timer;
@@ -420,7 +420,7 @@ static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
}
/* Remove timer from the timer list; clear timeout status */
-void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
+static void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
{
struct fsl_otg_timer *timer = gtimer;
struct fsl_otg_timer *tmp_timer, *del_tmp;
@@ -442,7 +442,7 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
}
/* Reset controller, not reset the bus */
-void otg_reset_controller(void)
+static void otg_reset_controller(void)
{
u32 command;
@@ -454,7 +454,7 @@ void otg_reset_controller(void)
}
/* Call suspend/resume routines in host driver */
-int fsl_otg_start_host(struct otg_fsm *fsm, int on)
+static int fsl_otg_start_host(struct otg_fsm *fsm, int on)
{
struct usb_otg *otg = fsm->otg;
struct device *dev;
@@ -521,7 +521,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
* Call suspend and resume function in udc driver
* to stop and start udc driver.
*/
-int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
+static int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
{
struct usb_otg *otg = fsm->otg;
struct device *dev;
@@ -703,7 +703,7 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
* intact. It needs to have knowledge of some USB interrupts
* such as port change.
*/
-irqreturn_t fsl_otg_isr(int irq, void *dev_id)
+static irqreturn_t fsl_otg_isr(int irq, void *dev_id)
{
struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
@@ -829,7 +829,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
}
/* OTG Initialization */
-int usb_otg_start(struct platform_device *pdev)
+static int usb_otg_start(struct platform_device *pdev)
{
struct fsl_otg *p_otg;
struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
@@ -1000,7 +1000,7 @@ static void fsl_otg_remove(struct platform_device *pdev)
pdata->exit(pdev);
}
-struct platform_driver fsl_otg_driver = {
+static struct platform_driver fsl_otg_driver = {
.probe = fsl_otg_probe,
.remove_new = fsl_otg_remove,
.driver = {
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index d70341ae5a92..cc841c4e4762 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -373,6 +373,6 @@ struct fsl_otg_config {
#define FSL_OTG_NAME "fsl-usb2-otg"
-void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
-void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
-void fsl_otg_pulse_vbus(void);
+static void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
+static void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
+static void fsl_otg_pulse_vbus(void);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 4fdc1d0c2451..cceb818c0848 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -798,9 +798,6 @@ static void usbhs_remove(struct platform_device *pdev)
flush_delayed_work(&priv->notify_hotplug_work);
- usbhs_platform_call(priv, hardware_exit, pdev);
- reset_control_assert(priv->rsts);
-
/*
* Explicitly free the IRQ to ensure the interrupt handler is
* disabled and synchronized before freeing resources.
@@ -817,6 +814,9 @@ static void usbhs_remove(struct platform_device *pdev)
if (!usbhs_get_dparam(priv, runtime_pwctrl))
usbhsc_power_ctrl(priv, 0);
+ usbhs_platform_call(priv, hardware_exit, pdev);
+ reset_control_assert(priv->rsts);
+
usbhsc_clk_put(priv);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 3eb8dc3a1a8f..a1dde939f311 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -113,7 +113,7 @@ static ssize_t new_id_store(struct device_driver *driver,
if (retval >= 0 && usb_drv->usb_driver != NULL)
retval = usb_store_new_id(&usb_drv->usb_driver->dynids,
usb_drv->usb_driver->id_table,
- &usb_drv->usb_driver->drvwrap.driver,
+ &usb_drv->usb_driver->driver,
buf, count);
return retval;
}
@@ -136,12 +136,11 @@ static void free_dynids(struct usb_serial_driver *drv)
{
struct usb_dynid *dynid, *n;
- spin_lock(&drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
- spin_unlock(&drv->dynids.lock);
}
const struct bus_type usb_serial_bus_type = {
@@ -157,7 +156,6 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
int retval;
driver->driver.bus = &usb_serial_bus_type;
- spin_lock_init(&driver->dynids.lock);
INIT_LIST_HEAD(&driver->dynids.list);
retval = driver_register(&driver->driver);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index be81d4a2f79f..bd90af9b3dbc 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -706,14 +706,12 @@ static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf,
{
struct usb_dynid *dynid;
- spin_lock(&drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (usb_match_one_id(intf, &dynid->id)) {
- spin_unlock(&drv->dynids.lock);
return &dynid->id;
}
}
- spin_unlock(&drv->dynids.lock);
return NULL;
}
@@ -1516,7 +1514,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[]
/* Now set udriver's id_table and look for matches */
udriver->id_table = id_table;
- rc = driver_attach(&udriver->drvwrap.driver);
+ rc = driver_attach(&udriver->driver);
return 0;
err_deregister_drivers:
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 2cffc559ace5..13b332d1dbfe 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -2358,7 +2358,9 @@ static int ene_ub6250_probe(struct usb_interface *intf,
return result;
/* probe card type */
+ mutex_lock(&us->dev_mutex);
result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf);
+ mutex_unlock(&us->dev_mutex);
if (result != USB_STOR_XFER_GOOD) {
usb_stor_disconnect(intf);
return USB_STOR_TRANSPORT_ERROR;
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index b54b4bfeb45e..00b1b6f5ca62 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -1257,7 +1257,7 @@ static struct usb_driver uas_driver = {
.suspend = uas_suspend,
.resume = uas_resume,
.reset_resume = uas_reset_resume,
- .drvwrap.driver.shutdown = uas_shutdown,
+ .driver.shutdown = uas_shutdown,
.id_table = uas_usb_ids,
};
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
index b2f65556f3f7..85e3ddcdae51 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
@@ -464,6 +464,8 @@ void qcom_pmic_typec_pdphy_stop(struct pmic_typec_pdphy *pmic_typec_pdphy)
for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++)
disable_irq(pmic_typec_pdphy->irq_data[i].irq);
+ cancel_work_sync(&pmic_typec_pdphy->reset_work);
+
qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy);
regulator_disable(pmic_typec_pdphy->vdd_pdphy);
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
index f67733cecfdf..2f5a3ae1fd85 100644
--- a/drivers/usb/typec/ucsi/debugfs.c
+++ b/drivers/usb/typec/ucsi/debugfs.c
@@ -89,6 +89,7 @@ void ucsi_debugfs_unregister(struct ucsi *ucsi)
debugfs_remove_recursive(ucsi->debugfs->dentry);
kfree(ucsi->debugfs);
+ ucsi->debugfs = NULL;
}
void ucsi_debugfs_init(void)
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 09693934d6c5..5d78f9ba3b02 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -74,7 +74,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
cur = 0xff;
}
- if (cur != 0xff) {
+ if (cur < UCSI_MAX_ALTMODES) {
ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
goto err_unlock;
}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index b0d5f071664f..46fe16b5a2a0 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -17,16 +17,6 @@
#include "ucsi.h"
#include "trace.h"
-/*
- * UCSI_TIMEOUT_MS - PPM communication timeout
- *
- * Ideally we could use MIN_TIME_TO_RESPOND_WITH_BUSY (which is defined in UCSI
- * specification) here as reference, but unfortunately we can't. It is very
- * difficult to estimate the time it takes for the system to process the command
- * before it is actually passed to the PPM.
- */
-#define UCSI_TIMEOUT_MS 10000
-
/*
* UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests
*
@@ -1770,6 +1760,8 @@ void ucsi_unregister(struct ucsi *ucsi)
cancel_delayed_work_sync(&ucsi->work);
cancel_work_sync(&ucsi->resume_work);
+ ucsi_debugfs_unregister(ucsi);
+
/* Disable notifications */
ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 9ac5ad87f401..f0cf52c6ddeb 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -26,6 +26,9 @@ struct dentry;
#define UCSI_MESSAGE_OUT 32
#define UCSIv2_MESSAGE_OUT 272
+/* PPM communication timeout in milliseconds */
+#define UCSI_TIMEOUT_MS 10000
+
/* UCSI versions */
#define UCSI_VERSION_1_2 0x0120
#define UCSI_VERSION_2_0 0x0200
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index e40f3bf650ad..1054947497bb 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -88,7 +88,8 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
if (ret)
goto out_clear_bit;
- if (!wait_for_completion_timeout(&ua->complete, 5 * HZ))
+ if (!wait_for_completion_timeout(&ua->complete,
+ msecs_to_jiffies(UCSI_TIMEOUT_MS)))
ret = -ETIMEDOUT;
out_clear_bit:
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index 0a6624d37929..79110a69d697 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -377,14 +377,14 @@ static int __init usbip_host_init(void)
goto err_usb_register;
}
- ret = driver_create_file(&stub_driver.drvwrap.driver,
+ ret = driver_create_file(&stub_driver.driver,
&driver_attr_match_busid);
if (ret) {
pr_err("driver_create_file failed\n");
goto err_create_file;
}
- ret = driver_create_file(&stub_driver.drvwrap.driver,
+ ret = driver_create_file(&stub_driver.driver,
&driver_attr_rebind);
if (ret) {
pr_err("driver_create_file failed\n");
@@ -402,10 +402,10 @@ static int __init usbip_host_init(void)
static void __exit usbip_host_exit(void)
{
- driver_remove_file(&stub_driver.drvwrap.driver,
+ driver_remove_file(&stub_driver.driver,
&driver_attr_match_busid);
- driver_remove_file(&stub_driver.drvwrap.driver,
+ driver_remove_file(&stub_driver.driver,
&driver_attr_rebind);
/*
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index 165730656934..f102b9ff967b 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -343,7 +343,7 @@ static int add_direct_chain(struct mlx5_vdpa_dev *mvdev, u64 start, u64 size, u8
return 0;
err_alloc:
- list_for_each_entry_safe(dmr, n, &mr->head, list) {
+ list_for_each_entry_safe(dmr, n, &tmp, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index 421ab01ef06b..58cb711e981d 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -229,8 +229,11 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
kthread_init_work(&vdpasim->work, vdpasim_work_fn);
vdpasim->worker = kthread_create_worker(0, "vDPA sim worker: %s",
dev_attr->name);
- if (IS_ERR(vdpasim->worker))
+ if (IS_ERR(vdpasim->worker)) {
+ ret = PTR_ERR(vdpasim->worker);
+ vdpasim->worker = NULL;
goto err_iommu;
+ }
mutex_init(&vdpasim->mutex);
spin_lock_init(&vdpasim->iommu_lock);
@@ -709,18 +712,24 @@ static void vdpasim_free(struct vdpa_device *vdpa)
struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
int i;
- kthread_cancel_work_sync(&vdpasim->work);
- kthread_destroy_worker(vdpasim->worker);
+ if (vdpasim->worker) {
+ kthread_cancel_work_sync(&vdpasim->work);
+ kthread_destroy_worker(vdpasim->worker);
+ }
- for (i = 0; i < vdpasim->dev_attr.nvqs; i++) {
- vringh_kiov_cleanup(&vdpasim->vqs[i].out_iov);
- vringh_kiov_cleanup(&vdpasim->vqs[i].in_iov);
+ if (vdpasim->vqs) {
+ for (i = 0; i < vdpasim->dev_attr.nvqs; i++) {
+ vringh_kiov_cleanup(&vdpasim->vqs[i].out_iov);
+ vringh_kiov_cleanup(&vdpasim->vqs[i].in_iov);
+ }
}
vdpasim->dev_attr.free(vdpasim);
- for (i = 0; i < vdpasim->dev_attr.nas; i++)
- vhost_iotlb_reset(&vdpasim->iommu[i]);
+ if (vdpasim->iommu) {
+ for (i = 0; i < vdpasim->dev_attr.nas; i++)
+ vhost_iotlb_reset(&vdpasim->iommu[i]);
+ }
kfree(vdpasim->iommu);
kfree(vdpasim->iommu_pt);
kfree(vdpasim->vqs);
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index d9eb8733a324..ad1e5ef5c60c 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -1257,6 +1257,7 @@ static int vfio_msi_cap_len(struct vfio_pci_core_device *vdev, u8 pos)
ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags);
if (ret) {
kfree(vdev->msi_perm);
+ vdev->msi_perm = NULL;
return ret;
}
diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c
index af6c0581d3e2..868b028d1234 100644
--- a/drivers/video/fbdev/kyro/fbdev.c
+++ b/drivers/video/fbdev/kyro/fbdev.c
@@ -369,6 +369,9 @@ static int kyro_dev_overlay_create(u32 ulWidth,
static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight)
{
+ u32 right;
+ u32 bottom;
+
if (deviceInfo.ulOverlayOffset == 0)
/* probably haven't called CreateOverlay yet */
return -EINVAL;
@@ -378,11 +381,30 @@ static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight
(x < 2 && ulWidth + 2 == 0))
return -EINVAL;
+ /*
+ * SetOverlayViewPort() adjusts X coordinates by +2 (left) and +1
+ * (right) before packing them into 16-bit register fields.
+ */
+ if (x > U16_MAX - 2 || y > U16_MAX)
+ return -EINVAL;
+
+ right = x + ulWidth;
+ bottom = y + ulHeight;
+
+ if (right < x || bottom < y)
+ return -EINVAL;
+
+ right--;
+ bottom--;
+
+ if (right > U16_MAX - 1 || bottom > U16_MAX)
+ return -EINVAL;
+
/* Stop Ramdac Output */
DisableRamdacOutput(deviceInfo.pSTGReg);
SetOverlayViewPort(deviceInfo.pSTGReg,
- x, y, x + ulWidth - 1, y + ulHeight - 1);
+ x, y, right, bottom);
EnableOverlayPlane(deviceInfo.pSTGReg);
/* Start Ramdac Output */
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
index 77fce1223a64..ea6e19454a4c 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -1155,14 +1155,14 @@ static int dsicm_probe(struct platform_device *pdev)
dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+ mutex_init(&ddata->lock);
+
r = omapdss_register_display(dssdev);
if (r) {
dev_err(dev, "Failed to register panel\n");
goto err_reg;
}
- mutex_init(&ddata->lock);
-
atomic_set(&ddata->do_update, 0);
ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c
index 7ae37f91b42c..1483166626a0 100644
--- a/drivers/video/fbdev/pvr2fb.c
+++ b/drivers/video/fbdev/pvr2fb.c
@@ -639,7 +639,7 @@ static irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id)
}
#ifdef CONFIG_PVR2_DMA
-static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
+static ssize_t pvr2fb_write(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long dst, start, end, len;
@@ -1076,7 +1076,7 @@ static struct pvr2_board {
#ifdef CONFIG_PCI
{ pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" },
#endif
- { 0, },
+ { },
};
static int __init pvr2fb_init(void)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index bf8ec4861b45..219160aa6e9c 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/pwm.h>
+#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/regulator/consumer.h>
@@ -72,6 +73,13 @@ struct ssd1307fb_par {
struct i2c_client *client;
u32 height;
struct fb_info *info;
+ /* Pending damage, with exclusive x2/y2, protected by damage_lock. */
+ spinlock_t damage_lock;
+ bool damage_pending;
+ u32 damage_x1;
+ u32 damage_x2;
+ u32 damage_y1;
+ u32 damage_y2;
u8 lookup_table[4];
u32 page_offset;
u32 col_offset;
@@ -302,19 +310,49 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
}
+static void ssd1307fb_schedule_damage(struct fb_info *info, u32 x, u32 y,
+ u32 width, u32 height)
+{
+ struct ssd1307fb_par *par = info->par;
+ unsigned long flags;
+ u32 x2, y2;
+
+ if (!width || !height || x >= par->width || y >= par->height)
+ return;
+
+ x2 = x + min(width, par->width - x);
+ y2 = y + min(height, par->height - y);
+
+ spin_lock_irqsave(&par->damage_lock, flags);
+ if (par->damage_pending) {
+ par->damage_x1 = min(par->damage_x1, x);
+ par->damage_y1 = min(par->damage_y1, y);
+ par->damage_x2 = max(par->damage_x2, x2);
+ par->damage_y2 = max(par->damage_y2, y2);
+ } else {
+ par->damage_x1 = x;
+ par->damage_y1 = y;
+ par->damage_x2 = x2;
+ par->damage_y2 = y2;
+ par->damage_pending = true;
+ }
+ spin_unlock_irqrestore(&par->damage_lock, flags);
+
+ /* Advance an already-pending mmap update as well. */
+ mod_delayed_work(system_wq, &info->deferred_work, 0);
+}
+
static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len)
{
struct ssd1307fb_par *par = info->par;
- ssd1307fb_update_display(par);
+ ssd1307fb_schedule_damage(info, 0, 0, par->width, par->height);
}
static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y,
u32 width, u32 height)
{
- struct ssd1307fb_par *par = info->par;
-
- ssd1307fb_update_rect(par, x, y, width, height);
+ ssd1307fb_schedule_damage(info, x, y, width, height);
}
FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb,
@@ -329,7 +367,30 @@ static const struct fb_ops ssd1307fb_ops = {
static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
{
- ssd1307fb_update_display(info->par);
+ struct ssd1307fb_par *par = info->par;
+ unsigned long flags;
+ u32 x, y, width, height;
+
+ spin_lock_irqsave(&par->damage_lock, flags);
+ if (!list_empty(pagereflist)) {
+ x = 0;
+ y = 0;
+ width = par->width;
+ height = par->height;
+ par->damage_pending = false;
+ } else if (par->damage_pending) {
+ x = par->damage_x1;
+ y = par->damage_y1;
+ width = par->damage_x2 - par->damage_x1;
+ height = par->damage_y2 - par->damage_y1;
+ par->damage_pending = false;
+ } else {
+ spin_unlock_irqrestore(&par->damage_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&par->damage_lock, flags);
+
+ ssd1307fb_update_rect(par, x, y, width, height);
}
static int ssd1307fb_init(struct ssd1307fb_par *par)
@@ -609,6 +670,7 @@ static int ssd1307fb_probe(struct i2c_client *client)
par = info->par;
par->info = info;
par->client = client;
+ spin_lock_init(&par->damage_lock);
par->device_info = device_get_match_data(dev);
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index 08721da00385..5668991beff3 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -1384,7 +1384,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
return err;
- err = pci_enable_device(pdev);
+ err = pcim_enable_device(pdev);
if (err) {
printk(KERN_ERR "tdfxfb: Can't enable pdev: %d\n", err);
return err;
diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c
index bc40beacfba6..aa471f4ff2bb 100644
--- a/drivers/video/fbdev/uvesafb.c
+++ b/drivers/video/fbdev/uvesafb.c
@@ -1910,6 +1910,8 @@ static int uvesafb_init(void)
err = 0;
}
}
+ if (err)
+ cn_del_callback(&uvesafb_cn_id);
return err;
}
diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c
index c3765ad6eedf..f5bd82f7eb2c 100644
--- a/drivers/video/sticore.c
+++ b/drivers/video/sticore.c
@@ -325,7 +325,7 @@ static void sti_rom_copy(unsigned long base, unsigned long count, void *dest)
-static char default_sti_path[21] __read_mostly;
+static char default_sti_path[32] __read_mostly;
#ifndef MODULE
static int __init sti_setup(char *str)
@@ -1143,6 +1143,26 @@ static void sti_init_roms(void)
pr_info("STI GSC/PCI core graphics driver "
STI_DRIVERVERSION "\n");
+ /*
+ * Find default console by hardware path which is either stored in
+ * console entry in stable storage or alternatively from console path
+ * in PAGE0 used by BCH and PDC.
+ */
+ if (!default_sti_path[0]) {
+ struct pdc_module_path conspath;
+ struct device *dev = NULL;
+
+ if (pdc_stable_read(0x60, &conspath, sizeof(conspath)) == PDC_OK)
+ dev = hwpath_to_device(&conspath.path);
+ if (!dev)
+ dev = hwpath_to_device(&PAGE0->mem_cons.dp.path);
+ if (dev && dev_is_pci(dev))
+ print_pci_hwpath(to_pci_dev(dev), default_sti_path);
+ else if (dev && !dev_is_pci(dev))
+ print_pa_hwpath(to_parisc_device(dev), default_sti_path);
+ pr_debug("default graphic card: %s\n", default_sti_path);
+ }
+
/* Register drivers for native & PCI cards */
register_parisc_driver(&pa_sti_driver);
WARN_ON(pci_register_driver(&pci_sti_driver));
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 6cb7ccb21b71..b47db0e44eb5 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -7,6 +7,7 @@
*/
#include <linux/virtio.h>
+#include <uapi/linux/virtio_ring.h>
#include <linux/virtio_balloon.h>
#include <linux/swap.h>
#include <linux/workqueue.h>
@@ -1095,6 +1096,11 @@ static int virtballoon_validate(struct virtio_device *vdev)
else if (!virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON))
__virtio_clear_bit(vdev, VIRTIO_BALLOON_F_REPORTING);
+ /*
+ * Disable indirect descriptors to avoid memory allocation in
+ * virtqueue_add during page reporting.
+ */
+ __virtio_clear_bit(vdev, VIRTIO_RING_F_INDIRECT_DESC);
__virtio_clear_bit(vdev, VIRTIO_F_ACCESS_PLATFORM);
return 0;
}
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c
index b2d76c1784bd..da801859c7ec 100644
--- a/drivers/w1/masters/ds2482.c
+++ b/drivers/w1/masters/ds2482.c
@@ -309,6 +309,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit)
mutex_unlock(&pdev->access_lock);
+ /* On bus error, decode to 3 (no device responded) to abort the search */
+ if (status < 0)
+ status = 3 << 5;
+
/* Decode the status */
return (status >> 5);
}
diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c
index 52261b54d842..c0baf2d6cc09 100644
--- a/drivers/w1/slaves/w1_ds28e17.c
+++ b/drivers/w1/slaves/w1_ds28e17.c
@@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
* another simple read in that case.
*/
if (msgs[i+1].flags & I2C_M_RECV_LEN) {
+ if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ i = -EPROTO;
+ goto error;
+ }
result = w1_f19_i2c_read(sl, msgs[i+1].addr,
&(msgs[i+1].buf[1]), msgs[i+1].buf[0]);
if (result < 0) {
@@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
* another simple read in that case.
*/
if (msgs[i].flags & I2C_M_RECV_LEN) {
+ if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ i = -EPROTO;
+ goto error;
+ }
result = w1_f19_i2c_read(sl,
msgs[i].addr,
&(msgs[i].buf[1]),
diff --git a/drivers/watchdog/pseries-wdt.c b/drivers/watchdog/pseries-wdt.c
index 7f53b5293409..bad4c63b8310 100644
--- a/drivers/watchdog/pseries-wdt.c
+++ b/drivers/watchdog/pseries-wdt.c
@@ -13,61 +13,10 @@
#include <linux/platform_device.h>
#include <linux/time64.h>
#include <linux/watchdog.h>
+#include <asm/papr-watchdog.h>
#define DRV_NAME "pseries-wdt"
-/*
- * H_WATCHDOG Input
- *
- * R4: "flags":
- *
- * Bits 48-55: "operation"
- */
-#define PSERIES_WDTF_OP_START 0x100UL /* start timer */
-#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */
-#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */
-
-/*
- * Bits 56-63: "timeoutAction" (for "Start Watchdog" only)
- */
-#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */
-#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */
-#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */
-
-/*
- * H_WATCHDOG Output
- *
- * R3: Return code
- *
- * H_SUCCESS The operation completed.
- *
- * H_BUSY The hypervisor is too busy; retry the operation.
- *
- * H_PARAMETER The given "flags" are somehow invalid. Either the
- * "operation" or "timeoutAction" is invalid, or a
- * reserved bit is set.
- *
- * H_P2 The given "watchdogNumber" is zero or exceeds the
- * supported maximum value.
- *
- * H_P3 The given "timeoutInMs" is below the supported
- * minimum value.
- *
- * H_NOOP The given "watchdogNumber" is already stopped.
- *
- * H_HARDWARE The operation failed for ineffable reasons.
- *
- * H_FUNCTION The H_WATCHDOG hypercall is not supported by this
- * hypervisor.
- *
- * R4:
- *
- * - For the "Query Watchdog Capabilities" operation, a 64-bit
- * structure:
- */
-#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff)
-#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff)
-
static const unsigned long pseries_wdt_action[] = {
[0] = PSERIES_WDTF_ACTION_HARD_POWEROFF,
[1] = PSERIES_WDTF_ACTION_HARD_RESTART,
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index f84c87ee2839..6b1cc94efe2c 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -952,19 +952,27 @@ int xs_init(void)
int err;
struct task_struct *task;
- register_reboot_notifier(&xs_reboot_nb);
+ err = register_reboot_notifier(&xs_reboot_nb);
+ if (err)
+ return err;
/* Initialize the shared memory rings to talk to xenstored */
err = xb_init_comms();
if (err)
- return err;
+ goto err_unregister_reboot_notifier;
task = kthread_run(xenwatch_thread, NULL, "xenwatch");
- if (IS_ERR(task))
- return PTR_ERR(task);
+ if (IS_ERR(task)) {
+ err = PTR_ERR(task);
+ goto err_unregister_reboot_notifier;
+ }
/* shutdown watches for kexec boot */
xs_reset_watches();
return 0;
+
+err_unregister_reboot_notifier:
+ unregister_reboot_notifier(&xs_reboot_nb);
+ return err;
}
diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 744f4f4d4c68..ddb651e0bb05 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -732,14 +732,18 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
goto out;
folio_lock(folio);
- /* If it's not uptodate after we have the lock, we got a read error. */
- if (!folio_test_uptodate(folio)) {
+ /* Folio was truncated from mapping. */
+ if (!folio->mapping) {
folio_unlock(folio);
folio_put(folio);
- return ERR_PTR(-EIO);
+ goto again;
}
- folio_unlock(folio);
- goto out;
+ /* Another reader may have filled the folio while we waited. */
+ if (folio_test_uptodate(folio)) {
+ folio_unlock(folio);
+ goto out;
+ }
+ goto read_folio;
}
folio = filemap_alloc_folio(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS),
@@ -756,6 +760,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
return ERR_PTR(ret);
}
+read_folio:
/*
* Merkle item keys are indexed from byte 0 in the merkle tree.
* They have the form:
@@ -765,6 +770,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off,
folio_address(folio), PAGE_SIZE, &folio->page);
if (ret < 0) {
+ folio_unlock(folio);
folio_put(folio);
return ERR_PTR(ret);
}
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index dd667358e1fa..4688ae99c8ef 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -2590,9 +2590,14 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
}
}
- list_for_each_entry(cf, &ci->i_cap_flush_list, i_list) {
- if (cf->tid < first_tid)
+ cf = list_first_entry(&ci->i_cap_flush_list, struct ceph_cap_flush, i_list);
+ while (&cf->i_list != &ci->i_cap_flush_list) {
+ struct ceph_cap_flush *next;
+
+ if (cf->tid < first_tid) {
+ cf = list_next_entry(cf, i_list);
continue;
+ }
cap = ci->i_auth_cap;
if (!(cap && cap->session == session)) {
@@ -2602,6 +2607,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
}
first_tid = cf->tid + 1;
+ next = list_next_entry(cf, i_list);
if (!cf->is_capsnap) {
struct cap_msg_args arg;
@@ -2642,6 +2648,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
}
spin_lock(&ci->i_ceph_lock);
+ cf = next;
}
}
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index d692ebfddedb..42b066729d97 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -214,35 +214,32 @@ static struct inode *parse_longname(const struct inode *parent,
struct ceph_client *cl = ceph_inode_to_client(parent);
struct inode *dir = NULL;
struct ceph_vino vino = { .snap = CEPH_NOSNAP };
- char *inode_number;
- char *name_end;
- int orig_len = *name_len;
+ char *name_end, *inode_number;
int ret = -EIO;
-
- /* Skip initial '_' */
- name++;
- name_end = strrchr(name, '_');
+ /* Snapshot name must start with an underscore */
+ if (*name_len <= 0 || name[0] != '_')
+ return ERR_PTR(-EIO);
+ /* Skip initial '_' and NUL-terminate */
+ char *str __free(kfree) = kmemdup_nul(name + 1, *name_len - 1, GFP_KERNEL);
+ if (!str)
+ return ERR_PTR(-ENOMEM);
+ name_end = strrchr(str, '_');
if (!name_end) {
- doutc(cl, "failed to parse long snapshot name: %s\n", name);
+ doutc(cl, "failed to parse long snapshot name: %s\n", str);
return ERR_PTR(-EIO);
}
- *name_len = (name_end - name);
+ *name_len = (name_end - str);
if (*name_len <= 0) {
pr_err_client(cl, "failed to parse long snapshot name\n");
return ERR_PTR(-EIO);
}
/* Get the inode number */
- inode_number = kmemdup_nul(name_end + 1,
- orig_len - *name_len - 2,
- GFP_KERNEL);
- if (!inode_number)
- return ERR_PTR(-ENOMEM);
+ inode_number = name_end + 1;
ret = kstrtou64(inode_number, 10, &vino.ino);
if (ret) {
- doutc(cl, "failed to parse inode number: %s\n", name);
- dir = ERR_PTR(ret);
- goto out;
+ doutc(cl, "failed to parse inode number: %s\n", str);
+ return ERR_PTR(ret);
}
/* And finally the inode */
@@ -253,9 +250,6 @@ static struct inode *parse_longname(const struct inode *parent,
if (IS_ERR(dir))
doutc(cl, "can't find inode %s (%s)\n", inode_number, name);
}
-
-out:
- kfree(inode_number);
return dir;
}
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index 726af69d4d62..24a3d82de2fe 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -439,6 +439,16 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb,
return dentry;
}
+static int ceph_export_copy_name(char *name, const char *src, u32 len)
+{
+ if (len > NAME_MAX)
+ return -ENAMETOOLONG;
+
+ memcpy(name, src, len);
+ name[len] = '\0';
+ return 0;
+}
+
static int __get_snap_name(struct dentry *parent, char *name,
struct dentry *child)
{
@@ -504,9 +514,8 @@ static int __get_snap_name(struct dentry *parent, char *name,
BUG_ON(!rde->inode.in);
if (ceph_snap(inode) ==
le64_to_cpu(rde->inode.in->snapid)) {
- memcpy(name, rde->name, rde->name_len);
- name[rde->name_len] = '\0';
- err = 0;
+ err = ceph_export_copy_name(name, rde->name,
+ rde->name_len);
goto out;
}
}
@@ -571,8 +580,8 @@ static int ceph_get_name(struct dentry *parent, char *name,
rinfo = &req->r_reply_info;
if (!IS_ENCRYPTED(dir)) {
- memcpy(name, rinfo->dname, rinfo->dname_len);
- name[rinfo->dname_len] = 0;
+ err = ceph_export_copy_name(name, rinfo->dname,
+ rinfo->dname_len);
} else {
struct fscrypt_str oname = FSTR_INIT(NULL, 0);
struct ceph_fname fname = { .dir = dir,
@@ -586,10 +595,9 @@ static int ceph_get_name(struct dentry *parent, char *name,
goto out;
err = ceph_fname_to_usr(&fname, NULL, &oname, NULL);
- if (!err) {
- memcpy(name, oname.name, oname.len);
- name[oname.len] = 0;
- }
+ if (!err)
+ err = ceph_export_copy_name(name, oname.name,
+ oname.len);
ceph_fname_free_buffer(dir, &oname);
}
out:
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index cecaabd7388c..15d88d0a49ae 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -2343,6 +2343,54 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (err < 0)
goto out;
+ /*
+ * For O_APPEND writes we may have waited for Fwx exclusive caps
+ * while the previous Fwx holder (another client) extended the
+ * file. i_size has been updated via the cap grant message from
+ * the MDS, but ki_pos is still the old EOF. Re-read i_size here
+ * (no extra MDS round-trip needed) and adjust ki_pos to the true
+ * EOF. Since we hold Fwx, no other client can change the file.
+ */
+ if (iocb->ki_flags & IOCB_APPEND) {
+ loff_t cur_eof = i_size_read(inode);
+
+ if (cur_eof != pos) {
+ doutc(cl,
+ "%p %llx.%llx O_APPEND: pos adjusted %lld -> %lld\n",
+ inode, ceph_vinop(inode), pos, cur_eof);
+ iocb->ki_pos = cur_eof;
+ pos = cur_eof;
+ if (pos >= limit) {
+ err = -EFBIG;
+ goto out_caps;
+ }
+ iov_iter_truncate(from, limit - pos);
+ count = iov_iter_count(from);
+
+ /*
+ * ceph_get_caps() validated the old endoff
+ * against i_max_size; adjusting ki_pos forward
+ * may have shifted the write range beyond the
+ * granted max_size. Re-check and truncate if
+ * necessary.
+ */
+ spin_lock(&ci->i_ceph_lock);
+ if (pos + count > (loff_t)ci->i_max_size) {
+ loff_t max_size = ci->i_max_size;
+
+ spin_unlock(&ci->i_ceph_lock);
+ if (pos >= max_size) {
+ err = -EFBIG;
+ goto out_caps;
+ }
+ iov_iter_truncate(from, max_size - pos);
+ count = iov_iter_count(from);
+ } else {
+ spin_unlock(&ci->i_ceph_lock);
+ }
+ }
+ }
+
err = file_update_time(file);
if (err)
goto out_caps;
diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index 03634d721c52..7a90c8f8a470 100644
--- a/fs/ceph/mdsmap.c
+++ b/fs/ceph/mdsmap.c
@@ -3,6 +3,7 @@
#include <linux/bug.h>
#include <linux/err.h>
+#include <linux/overflow.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -126,6 +127,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
u8 mdsmap_v;
u16 mdsmap_ev;
u32 target;
+ size_t export_targets_len;
m = kzalloc(sizeof(*m), GFP_NOFS);
if (!m)
@@ -224,8 +226,11 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
*p += namelen;
if (info_v >= 2) {
ceph_decode_32_safe(p, end, num_export_targets, bad);
+ export_targets_len = size_mul(num_export_targets,
+ sizeof(u32));
+ ceph_decode_need(p, end, export_targets_len, bad);
pexport_targets = *p;
- *p += num_export_targets * sizeof(u32);
+ *p += export_targets_len;
} else {
num_export_targets = 0;
}
@@ -264,6 +269,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
goto nomem;
for (j = 0; j < num_export_targets; j++) {
target = ceph_decode_32(&pexport_targets);
+ if (target >= CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }
info->export_targets[j] = target;
}
} else {
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index a2f948d4aa6c..17da8be8423f 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -850,6 +850,7 @@ static int __build_xattrs(struct inode *inode)
name = p;
p += len;
ceph_decode_32_safe(&p, end, len, bad);
+ ceph_decode_need(&p, end, len, bad);
val = p;
p += len;
@@ -1256,6 +1257,22 @@ int __ceph_setxattr(struct inode *inode, const char *name,
ceph_vinop(inode), name, ceph_cap_string(issued));
__build_xattrs(inode);
+ /*
+ * __build_xattrs() may have released and reacquired i_ceph_lock,
+ * during which handle_cap_grant() could have replaced i_xattrs.blob
+ * with a newer MDS-provided blob and bumped i_xattrs.version. If that
+ * caused __build_xattrs() to rebuild the rb-tree from the new blob,
+ * count/names_size/vals_size may now be larger than when
+ * required_blob_size was computed above. Recompute it here so the
+ * prealloc_blob size check below reflects the current tree state.
+ */
+ required_blob_size = __get_required_blob_size(ci, name_len, val_len);
+ if (required_blob_size > mdsc->mdsmap->m_max_xattr_size) {
+ doutc(cl, "sync (size too large): %d > %llu\n",
+ required_blob_size, mdsc->mdsmap->m_max_xattr_size);
+ goto do_sync;
+ }
+
if (!ci->i_xattrs.prealloc_blob ||
required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) {
struct ceph_buffer *blob;
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 2fe0f3af1a08..8dd39209d27c 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1282,7 +1282,7 @@ static int ecryptfs_read_headers_virt(char *page_virt,
} else
set_default_header_data(crypt_stat);
rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset),
- ecryptfs_dentry);
+ PAGE_SIZE - offset, ecryptfs_dentry);
out:
return rc;
}
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index f2ed0c0266cb..6a16b1bb7554 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -590,7 +590,8 @@ int ecryptfs_generate_key_packet_set(char *dest_base,
size_t *len, size_t max);
int
ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
- unsigned char *src, struct dentry *ecryptfs_dentry);
+ unsigned char *src, size_t src_size,
+ struct dentry *ecryptfs_dentry);
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
ssize_t
ecryptfs_getxattr_lower(struct dentry *lower_dentry, struct inode *lower_inode,
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 7f9f68c00ef6..970e37510394 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -937,6 +937,12 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
"rc = [%d]\n", __func__, rc);
goto out;
}
+ if (s->parsed_tag_70_packet_size < (ECRYPTFS_SIG_SIZE + 2)) {
+ ecryptfs_printk(KERN_WARNING, "Invalid packet size [%zd]\n",
+ s->parsed_tag_70_packet_size);
+ rc = -EINVAL;
+ goto out;
+ }
s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
- ECRYPTFS_SIG_SIZE - 1);
if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
@@ -1424,10 +1430,20 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
}
(*new_auth_tok)->session_key.encrypted_key_size =
(body_size - (ECRYPTFS_SALT_SIZE + 5));
+ /*
+ * Although encrypted_key_size is copied into the
+ * encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES] buffer here,
+ * it later bounds operations on a smaller buffer:
+ * decrypt_passphrase_encrypted_session_key() sets decrypted_key_size =
+ * encrypted_key_size and decrypts into
+ * decrypted_key[ECRYPTFS_MAX_KEY_BYTES], then memcpy's into
+ * crypt_stat->key[ECRYPTFS_MAX_KEY_BYTES]. Limit to
+ * ECRYPTFS_MAX_KEY_BYTES to protect those smaller buffers.
+ */
if ((*new_auth_tok)->session_key.encrypted_key_size
- > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+ > ECRYPTFS_MAX_KEY_BYTES) {
printk(KERN_WARNING "Tag 3 packet contains key larger "
- "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES\n");
+ "than ECRYPTFS_MAX_KEY_BYTES\n");
rc = -EINVAL;
goto out_free;
}
@@ -1576,7 +1592,7 @@ parse_tag_11_packet(unsigned char *data, unsigned char *contents,
}
(*packet_size) += length_size;
(*tag_11_contents_size) = (body_size - 14);
- if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) {
+ if (unlikely((*packet_size) + body_size > max_packet_size)) {
printk(KERN_ERR "Packet size exceeds max\n");
rc = -EINVAL;
goto out;
@@ -1743,6 +1759,7 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
* ecryptfs_parse_packet_set
* @crypt_stat: The cryptographic context
* @src: Virtual address of region of memory containing the packets
+ * @src_size: Size of the packet set buffer
* @ecryptfs_dentry: The eCryptfs dentry associated with the packet set
*
* Get crypt_stat to have the file's session key if the requisite key
@@ -1753,7 +1770,7 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
* conditions.
*/
int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
- unsigned char *src,
+ unsigned char *src, size_t src_size,
struct dentry *ecryptfs_dentry)
{
size_t i = 0;
@@ -1777,7 +1794,11 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
* added the our &auth_tok_list */
next_packet_is_auth_tok_packet = 1;
while (next_packet_is_auth_tok_packet) {
- size_t max_packet_size = ((PAGE_SIZE - 8) - i);
+ size_t max_packet_size;
+
+ if (i >= src_size)
+ break;
+ max_packet_size = src_size - i;
switch (src[i]) {
case ECRYPTFS_TAG_3_PACKET_TYPE:
@@ -1792,12 +1813,16 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
rc = parse_tag_11_packet((unsigned char *)&src[i],
sig_tmp_space,
ECRYPTFS_SIG_SIZE,
&tag_11_contents_size,
&tag_11_packet_size,
- max_packet_size);
+ src_size - i);
if (rc) {
ecryptfs_printk(KERN_ERR, "No valid "
"(ecryptfs-specific) literal "
@@ -1809,6 +1834,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += tag_11_packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) {
ecryptfs_printk(KERN_ERR, "Expected "
"signature of size [%d]; "
@@ -1836,6 +1865,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
break;
case ECRYPTFS_TAG_11_PACKET_TYPE:
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index 6318f3500e5c..75c35b559b37 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -165,6 +165,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
mutex_unlock(&daemon->mux);
goto out;
}
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
list_for_each_entry_safe(msg_ctx, msg_ctx_tmp,
&daemon->msg_ctx_out_queue, daemon_out_list) {
list_del(&msg_ctx->daemon_out_list);
@@ -173,6 +174,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
"the out queue of a dying daemon\n", __func__);
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
}
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
hlist_del(&daemon->euid_chain);
mutex_unlock(&daemon->mux);
kfree_sensitive(daemon);
@@ -283,9 +285,16 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
rc = ecryptfs_send_miscdev(data, data_len, *msg_ctx, msg_type, 0,
daemon);
- if (rc)
+ if (rc) {
printk(KERN_ERR "%s: Error attempting to send message to "
"userspace daemon; rc = [%d]\n", __func__, rc);
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ mutex_lock(&(*msg_ctx)->mux);
+ ecryptfs_msg_ctx_alloc_to_free(*msg_ctx);
+ mutex_unlock(&(*msg_ctx)->mux);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ *msg_ctx = NULL;
+ }
out:
return rc;
}
diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c
index 4e62c3cef70f..23baa55d390f 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -357,7 +357,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
u32 seq;
size_t packet_size, packet_size_length;
char *data;
- unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
+ unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE] = { };
ssize_t rc;
if (count == 0) {
@@ -373,7 +373,8 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
}
if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET],
- sizeof(packet_size_peek))) {
+ min_t(size_t, count - PKT_LEN_OFFSET,
+ sizeof(packet_size_peek)))) {
printk(KERN_WARNING "%s: Error while inspecting packet size\n",
__func__);
return -EFAULT;
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index 0b1c878317ab..9ceb006f6ad7 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -153,6 +153,13 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)
if (mount_crypt_stat->global_default_cipher_key_size)
seq_printf(m, ",ecryptfs_key_bytes=%zd",
mount_crypt_stat->global_default_cipher_key_size);
+ if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
+ seq_printf(m, ",ecryptfs_fn_cipher=%s",
+ mount_crypt_stat->global_default_fn_cipher_name);
+ if (mount_crypt_stat->global_default_fn_cipher_key_bytes)
+ seq_printf(m, ",ecryptfs_fn_key_bytes=%zd",
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ }
if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)
seq_printf(m, ",ecryptfs_passthrough");
if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 34438981ddd8..5cc2ee382802 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -36,12 +36,30 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
/* Some UEFI firmware does not implement QueryVariableInfo() */
storage_space = remaining_space = 0;
if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) {
- status = efivar_query_variable_info(attr, &storage_space,
- &remaining_space,
- &max_variable_size);
- if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
- pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n",
- status);
+ static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5);
+ static u64 storage, remaining;
+ static DEFINE_SPINLOCK(lock);
+
+ if (!__ratelimit(&_rs)) {
+ ratelimit_set_flags(&_rs, RATELIMIT_MSG_ON_RELEASE);
+
+ spin_lock(&lock);
+ storage_space = storage;
+ remaining_space = remaining;
+ spin_unlock(&lock);
+ } else {
+ status = efivar_query_variable_info(attr, &storage_space,
+ &remaining_space,
+ &max_variable_size);
+ if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
+ pr_warn("query_variable_info() failed: 0x%lx\n",
+ status);
+
+ spin_lock(&lock);
+ storage = storage_space;
+ remaining = remaining_space;
+ spin_unlock(&lock);
+ }
}
/*
diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
index baabc37c71b3..bedb31517c44 100644
--- a/fs/erofs/Kconfig
+++ b/fs/erofs/Kconfig
@@ -101,11 +101,13 @@ config EROFS_FS_ZIP_LZMA
If unsure, say N.
config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS
- int "EROFS LZMA default maximum decompression streams"
+ int "EROFS LZMA default maximum decompression streams" if SMP
depends on EROFS_FS_ZIP_LZMA
range 1 NR_CPUS if SMP
range 1 1 if !SMP
- default 16
+ default 16 if SMP && NR_CPUS >= 16
+ default NR_CPUS if SMP
+ default 1
help
By default EROFS allocates one LZMA decompression stream per CPU.
Each stream can hold a dictionary of up to 8 MiB taken from the
@@ -113,7 +115,7 @@ config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS
of memory. This caps the default; the lzma_streams module parameter
still overrides it.
- If unsure, keep the default of 16.
+ If unsure, keep the suggested default (16 or lower).
config EROFS_FS_ZIP_DEFLATE
bool "EROFS DEFLATE compressed data support"
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 0d387a5b5a67..35670976250c 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -292,6 +292,13 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
return -EOPNOTSUPP;
inode_lock(inode);
+
+ /*
+ * Prevent concurrent direct I/O and buffered I/O to the same file
+ * range. Wait for in-flight DIO to finish before dirtying pages.
+ */
+ inode_dio_wait(inode);
+
ret = ext4_write_checks(iocb, from);
if (ret <= 0)
goto out;
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index a4206a20feaa..185bdc88c189 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1551,6 +1551,8 @@ int ext4_read_inline_dir(struct file *file,
/* for other entry, the real offset in
* the buf has to be tuned accordingly.
*/
+ if (i + ext4_dir_rec_len(1, NULL) > extra_size)
+ break;
de = (struct ext4_dir_entry_2 *)
(dir_buf + i - extra_offset);
/* It's too expensive to do a full
@@ -1585,10 +1587,17 @@ int ext4_read_inline_dir(struct file *file,
continue;
}
+ /*
+ * de lives at dir_buf + ctx->pos - extra_offset, within the
+ * kmalloc(inline_size) buffer. Make sure its header fits before
+ * ext4_check_dir_entry() dereferences de->rec_len.
+ */
+ if (ctx->pos + ext4_dir_rec_len(1, NULL) > extra_size)
+ goto out;
de = (struct ext4_dir_entry_2 *)
(dir_buf + ctx->pos - extra_offset);
if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf,
- extra_size, ctx->pos))
+ inline_size, ctx->pos))
goto out;
if (le32_to_cpu(de->inode)) {
if (!dir_emit(ctx, de->name, de->name_len,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 6a0afec3542b..dea6d27e5028 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2471,13 +2471,25 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
* page is already under writeback and we are not doing
* a data integrity writeback, skip the page
*/
- if (!folio_test_dirty(folio) ||
- (folio_test_writeback(folio) &&
- (mpd->wbc->sync_mode == WB_SYNC_NONE)) ||
+ if ((folio_test_writeback(folio) &&
+ mpd->wbc->sync_mode == WB_SYNC_NONE) ||
unlikely(folio->mapping != mapping)) {
folio_unlock(folio);
continue;
}
+ /*
+ * If the folio is clean, skip writing it back.
+ * Cycle the folio through the writeback state
+ * though, to clear stale xarray tags.
+ */
+ if (!folio_test_dirty(folio)) {
+ if (!folio_test_writeback(folio)) {
+ __folio_start_writeback(folio, false);
+ folio_end_writeback(folio);
+ }
+ folio_unlock(folio);
+ continue;
+ }
folio_wait_writeback(folio);
BUG_ON(folio_test_writeback(folio));
@@ -6015,6 +6027,16 @@ static int ext4_try_to_expand_extra_isize(struct inode *inode,
if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND))
return -EOVERFLOW;
+ /*
+ * Skip expansion during mount (!SB_ACTIVE). Expanding extra isize
+ * may move xattrs to external blocks and release ea_inodes via iput.
+ * When !SB_ACTIVE, iput triggers write_inode_now() which acquires
+ * s_writepages_rwsem, causing a deadlock with the caller's active
+ * jbd2 handle (lock order: s_writepages_rwsem -> jbd2_handle).
+ */
+ if (unlikely(!(inode->i_sb->s_flags & SB_ACTIVE)))
+ return -EBUSY;
+
/*
* In nojournal mode, we can immediately attempt to expand
* the inode. When journaled, we first need to obtain extra
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index 1b0dfd963d3f..84e3b703ab46 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -458,6 +458,7 @@ int ext4_ext_migrate(struct inode *inode)
if (IS_ERR(tmp_inode)) {
retval = PTR_ERR(tmp_inode);
ext4_journal_stop(handle);
+ tmp_inode = NULL;
goto out_unlock;
}
/*
@@ -585,9 +586,9 @@ int ext4_ext_migrate(struct inode *inode)
ext4_journal_stop(handle);
out_tmp_inode:
unlock_new_inode(tmp_inode);
- iput(tmp_inode);
out_unlock:
ext4_writepages_up_write(inode->i_sb, alloc_ctx);
+ iput(tmp_inode);
return retval;
}
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2e4575becd4f..1e72aa6f207e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1542,6 +1542,8 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
/* this code is executed quadratically often */
/* do minimal checking `by hand' */
if (de->name + de->name_len <= dlimit &&
+ (!ext4_hash_in_dirent(dir) ||
+ (char *)de + ext4_dir_rec_len(de->name_len, dir) <= dlimit) &&
ext4_match(dir, fname, de)) {
/* found a match - just to be sure, do
* a full check */
diff --git a/fs/ext4/orphan.c b/fs/ext4/orphan.c
index e5a6cb533242..270a50f67960 100644
--- a/fs/ext4/orphan.c
+++ b/fs/ext4/orphan.c
@@ -388,7 +388,7 @@ void ext4_orphan_cleanup(struct super_block *sb, struct ext4_super_block *es)
struct ext4_orphan_info *oi = &EXT4_SB(sb)->s_orphan_info;
int inodes_per_ob = ext4_inodes_per_orphan_block(sb);
- if (!es->s_last_orphan && !oi->of_blocks) {
+ if (!es->s_last_orphan && ext4_orphan_file_empty(sb)) {
ext4_debug("no orphan inodes to clean up\n");
return;
}
@@ -573,6 +573,7 @@ int ext4_init_orphan_info(struct super_block *sb)
int i, j;
int ret;
int free;
+ int loaded = 0;
__le32 *bdata;
int inodes_per_ob = ext4_inodes_per_orphan_block(sb);
struct ext4_orphan_block_tail *ot;
@@ -616,6 +617,7 @@ int ext4_init_orphan_info(struct super_block *sb)
ret = -EIO;
goto out_free;
}
+ loaded++;
ot = ext4_orphan_block_tail(sb, oi->of_binfo[i].ob_bh);
if (le32_to_cpu(ot->ob_magic) != EXT4_ORPHAN_BLOCK_MAGIC) {
ext4_error(sb, "orphan file block %d: bad magic", i);
@@ -638,8 +640,10 @@ int ext4_init_orphan_info(struct super_block *sb)
iput(inode);
return 0;
out_free:
- for (i--; i >= 0; i--)
- brelse(oi->of_binfo[i].ob_bh);
+ while (loaded > 0) {
+ loaded--;
+ brelse(oi->of_binfo[loaded].ob_bh);
+ }
kvfree(oi->of_binfo);
out_put:
iput(inode);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index d321a42f97c8..7125117b6322 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -36,23 +36,69 @@
#include <trace/events/f2fs.h>
#include <uapi/linux/f2fs.h>
-static void f2fs_zero_post_eof_page(struct inode *inode,
- loff_t new_size, bool lock)
+static int fill_zero(struct inode *inode, pgoff_t index,
+ loff_t start, loff_t len);
+
+static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
{
loff_t old_size = i_size_read(inode);
+ unsigned int offset, len;
+ pgoff_t index;
+ int err;
+
+ offset = old_size & (PAGE_SIZE - 1);
+
+ if (!offset)
+ return 0;
+
+ len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
+ index = old_size >> PAGE_SHIFT;
+
+ if (f2fs_has_inline_data(inode)) {
+ /* data post eof should be always zero */
+ if (new_size <= MAX_INLINE_DATA(inode))
+ return 0;
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
+ }
+
+ err = fill_zero(inode, index, offset, len);
+ if (err)
+ return err;
+ return filemap_write_and_wait_range(inode->i_mapping,
+ old_size, old_size + len - 1);
+}
+
+static int f2fs_zero_post_eof_page(struct inode *inode,
+ loff_t new_size, bool lock, bool writeback)
+{
+ loff_t old_size = i_size_read(inode);
+ bool strict =
+ F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
if (old_size >= new_size)
- return;
+ return 0;
- if (mapping_empty(inode->i_mapping))
- return;
+ if (!strict && mapping_empty(inode->i_mapping))
+ return 0;
if (lock)
filemap_invalidate_lock(inode->i_mapping);
/* zero or drop pages only in range of [old_size, new_size] */
- truncate_inode_pages_range(inode->i_mapping, old_size, new_size);
+ truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
if (lock)
filemap_invalidate_unlock(inode->i_mapping);
+
+ if (!writeback || !strict)
+ return 0;
+ /*
+ * In fsync_mode=strict, when we expand an unaligned EOF size, we
+ * should zero post EOF data and writeback the data immediately,
+ * so that it can avoid exposing stale data after metadata flush
+ * and POR.
+ */
+ return do_zero_post_eof_page(inode, new_size);
}
static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
@@ -123,7 +169,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
- f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true);
+ err = f2fs_zero_post_eof_page(inode,
+ (folio->index + 1) << PAGE_SHIFT, true, false);
+ if (err)
+ goto out_pagefault;
file_update_time(vmf->vma->vm_file);
filemap_invalidate_lock_shared(inode->i_mapping);
@@ -180,7 +229,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
out_sem:
filemap_invalidate_unlock_shared(inode->i_mapping);
-
+out_pagefault:
sb_end_pagefault(inode->i_sb);
out:
ret = vmf_fs_error(err);
@@ -1085,8 +1134,12 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
- if (attr->ia_size > old_size)
- f2fs_zero_post_eof_page(inode, attr->ia_size, false);
+ if (attr->ia_size > old_size) {
+ err = f2fs_zero_post_eof_page(inode,
+ attr->ia_size, false, true);
+ if (err)
+ goto err_out;
+ }
truncate_setsize(inode, attr->ia_size);
if (attr->ia_size <= old_size)
@@ -1095,6 +1148,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
* do not trim all blocks after i_size if target size is
* larger than i_size.
*/
+err_out:
filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
if (err)
@@ -1205,7 +1259,9 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
if (ret)
return ret;
- f2fs_zero_post_eof_page(inode, offset + len, true);
+ ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+ if (ret)
+ return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1491,7 +1547,9 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
- f2fs_zero_post_eof_page(inode, offset + len, false);
+ ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+ if (ret)
+ goto out_unlock;
f2fs_lock_op(sbi);
f2fs_drop_extent_tree(inode);
@@ -1499,6 +1557,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
f2fs_unlock_op(sbi);
+out_unlock:
filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
return ret;
@@ -1531,11 +1590,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
/* write out all moved pages, if possible */
filemap_invalidate_lock(inode->i_mapping);
- filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+ if (ret)
+ goto out_unlock;
truncate_pagecache(inode, offset);
new_size = i_size_read(inode) - len;
ret = f2fs_truncate_blocks(inode, new_size, true);
+out_unlock:
filemap_invalidate_unlock(inode->i_mapping);
if (!ret)
f2fs_i_size_write(inode, new_size);
@@ -1618,7 +1680,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
if (ret)
return ret;
- f2fs_zero_post_eof_page(inode, offset + len, true);
+ ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+ if (ret)
+ return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1752,7 +1816,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(mapping);
- f2fs_zero_post_eof_page(inode, offset + len, false);
+ ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+ if (ret)
+ goto out_unlock;
truncate_pagecache(inode, offset);
while (!ret && idx > pg_start) {
@@ -1768,6 +1834,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
idx + delta, nr, false);
f2fs_unlock_op(sbi);
}
+out_unlock:
filemap_invalidate_unlock(mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
@@ -1808,7 +1875,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
if (err)
return err;
- f2fs_zero_post_eof_page(inode, offset + len, true);
+ err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
+ if (err)
+ return err;
f2fs_balance_fs(sbi, true);
@@ -1829,8 +1898,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
block_t sec_len;
if (map.m_lblk % sec_blks) {
- map.m_lblk = rounddown(map.m_lblk, sec_blks);
- map.m_len = pg_end - map.m_lblk;
+ pg_start = rounddown(map.m_lblk, sec_blks);
+ map.m_lblk = pg_start;
+ map.m_len = pg_end - pg_start;
if (off_end)
map.m_len++;
}
@@ -3018,8 +3088,6 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
if (src == dst) {
if (pos_in == pos_out)
return 0;
- if (pos_out > pos_in && pos_out < pos_in + len)
- return -EINVAL;
}
inode_lock(src);
@@ -3045,6 +3113,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
goto out_unlock;
if (len == 0)
olen = len = src->i_size - pos_in;
+ if (src == dst && pos_out > pos_in && pos_out < pos_in + len)
+ goto out_unlock;
if (pos_in + len == src->i_size)
len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in;
if (len == 0) {
@@ -4779,8 +4849,10 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
if (err)
return err;
- f2fs_zero_post_eof_page(inode,
- iocb->ki_pos + iov_iter_count(from), true);
+ err = f2fs_zero_post_eof_page(inode,
+ iocb->ki_pos + iov_iter_count(from), true, true);
+ if (err)
+ return err;
return count;
}
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index e55d0ee3e294..ee74b75b278e 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -673,15 +673,16 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir,
* performance regression.
*/
if (!err) {
- filemap_write_and_wait_range(inode->i_mapping, 0,
- disk_link.len - 1);
+ err = filemap_write_and_wait_range(inode->i_mapping, 0,
+ disk_link.len - 1);
- if (IS_DIRSYNC(dir))
+ if (!err && IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
- } else {
- f2fs_unlink(dir, dentry);
}
+ if (err)
+ f2fs_unlink(dir, dentry);
+
f2fs_balance_fs(sbi, true);
goto out_free_encrypted_link;
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index cb9f58db5253..5b2b114af8d2 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -118,7 +118,7 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
unsigned char old_advise = F2FS_I(inode)->i_advise;
unsigned char new_advise;
- if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+ if (!inode_owner_or_capable(idmap, inode))
return -EPERM;
if (value == NULL)
return -EINVAL;
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index f2304a1054aa..d17e6c9ea42a 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -133,7 +133,11 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
ret = fat_ent_read(inode, &fatent, last);
if (ret >= 0) {
int wait = inode_needs_sync(inode);
+ int old = ret;
+
ret = fat_ent_write(inode, &fatent, new_dclus, wait);
+ if (ret < 0)
+ fat_ent_write(inode, &fatent, old, wait);
fatent_brelse(&fatent);
}
if (ret < 0)
diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c
index 93c97bf45b06..ad4c677d0475 100644
--- a/fs/fat/nfs.c
+++ b/fs/fat/nfs.c
@@ -250,8 +250,10 @@ struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
MSDOS_I(dummy_grand_parent)->i_pos = -1;
}
- if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo))
+ if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo)) {
parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ }
brelse(parent_bh);
iput(dummy_grand_parent);
diff --git a/fs/fuse/control.c b/fs/fuse/control.c
index edb2b867118c..2944f823f762 100644
--- a/fs/fuse/control.c
+++ b/fs/fuse/control.c
@@ -235,7 +235,7 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
inode->i_mode = mode;
inode->i_uid = fc->user_id;
inode->i_gid = fc->group_id;
- inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
+ simple_inode_init_ts(inode);
/* setting ->i_op to NULL is not allowed */
if (iop)
inode->i_op = iop;
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index e4d809e2d68d..5fb9fc6b5173 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -643,6 +643,11 @@ static void __exit cuse_exit(void)
{
misc_deregister(&cuse_miscdev);
class_destroy(cuse_class);
+ /*
+ * Wait for pending call_rcu() callbacks that call back into
+ * this module via fc->release (cuse_fc_release).
+ */
+ rcu_barrier();
}
module_init(cuse_init);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 252b6697960f..bfc07ffc6eb7 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -974,17 +974,41 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
struct fuse_req *req = cs->req;
struct fuse_args_pages *ap = container_of(req->args, typeof(*ap), args);
+ if (ap->uses_folios) {
+ for (i = 0; i < ap->num_folios && (nbytes || zeroing); i++) {
+ int err;
+ unsigned int offset = ap->folio_descs[i].offset;
+ unsigned int count = min(nbytes, ap->folio_descs[i].length);
+ struct page *orig, *pagep;
- for (i = 0; i < ap->num_pages && (nbytes || zeroing); i++) {
- int err;
- unsigned int offset = ap->descs[i].offset;
- unsigned int count = min(nbytes, ap->descs[i].length);
+ orig = pagep = &ap->folios[i]->page;
- err = fuse_copy_page(cs, &ap->pages[i], offset, count, zeroing);
- if (err)
- return err;
+ err = fuse_copy_page(cs, &pagep, offset, count, zeroing);
+ if (err)
+ return err;
+
+ nbytes -= count;
+
+ /*
+ * fuse_copy_page may have moved a page from a pipe
+ * instead of copying into our given page, so update
+ * the folios if it was replaced.
+ */
+ if (pagep != orig)
+ ap->folios[i] = page_folio(pagep);
+ }
+ } else {
+ for (i = 0; i < ap->num_pages && (nbytes || zeroing); i++) {
+ int err;
+ unsigned int offset = ap->descs[i].offset;
+ unsigned int count = min(nbytes, ap->descs[i].length);
- nbytes -= count;
+ err = fuse_copy_page(cs, &ap->pages[i], offset, count, zeroing);
+ if (err)
+ return err;
+
+ nbytes -= count;
+ }
}
return 0;
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 4a7991d53c2b..2b353ff96023 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -366,7 +366,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
struct fuse_mount *fm = get_fuse_mount_super(sb);
FUSE_ARGS(args);
struct fuse_forget_link *forget;
- u64 attr_version;
+ u64 attr_version, evict_ctr;
int err;
*inode = NULL;
@@ -381,6 +381,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
goto out;
attr_version = fuse_get_attr_version(fm->fc);
+ evict_ctr = fuse_get_evict_ctr(fm->fc);
fuse_lookup_init(fm->fc, &args, nodeid, name, outarg);
err = fuse_simple_request(fm, &args);
@@ -398,7 +399,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
&outarg->attr, ATTR_TIMEOUT(outarg),
- attr_version);
+ attr_version, evict_ctr);
err = -ENOMEM;
if (!*inode) {
fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1);
@@ -684,7 +685,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
ff->nodeid = outentry.nodeid;
ff->open_flags = outopen.open_flags;
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
- &outentry.attr, ATTR_TIMEOUT(&outentry), 0);
+ &outentry.attr, ATTR_TIMEOUT(&outentry), 0, 0);
if (!inode) {
flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
fuse_sync_release(NULL, ff, flags);
@@ -812,7 +813,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
goto out_put_forget_req;
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
- &outarg.attr, ATTR_TIMEOUT(&outarg), 0);
+ &outarg.attr, ATTR_TIMEOUT(&outarg), 0, 0);
if (!inode) {
fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1);
return -ENOMEM;
@@ -1819,12 +1820,12 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
memset(&outarg, 0, sizeof(outarg));
inarg.valid = FATTR_MTIME;
- inarg.mtime = inode->i_mtime.tv_sec;
- inarg.mtimensec = inode->i_mtime.tv_nsec;
+ inarg.mtime = inode_get_mtime_sec(inode);
+ inarg.mtimensec = inode_get_mtime_nsec(inode);
if (fm->fc->minor >= 23) {
inarg.valid |= FATTR_CTIME;
- inarg.ctime = inode_get_ctime(inode).tv_sec;
- inarg.ctimensec = inode_get_ctime(inode).tv_nsec;
+ inarg.ctime = inode_get_ctime_sec(inode);
+ inarg.ctimensec = inode_get_ctime_nsec(inode);
}
if (ff) {
inarg.valid |= FATTR_FH;
@@ -1964,7 +1965,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
/* the kernel maintains i_mtime locally */
if (trust_local_cmtime) {
if (attr->ia_valid & ATTR_MTIME)
- inode->i_mtime = attr->ia_mtime;
+ inode_set_mtime_to_ts(inode, attr->ia_mtime);
if (attr->ia_valid & ATTR_CTIME)
inode_set_ctime_to_ts(inode, attr->ia_ctime);
/* FIXME: clear I_DIRTY_SYNC? */
@@ -1980,7 +1981,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
fuse_change_attributes_common(inode, &outarg.attr, NULL,
ATTR_TIMEOUT(&outarg),
- fuse_get_cache_mask(inode));
+ fuse_get_cache_mask(inode), 0);
oldsize = inode->i_size;
/* see the comment in fuse_change_attributes() */
if (!is_wb || is_truncate)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index aa12ff6de706..8751217aaafa 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -266,6 +266,12 @@ struct fuse_page_desc {
unsigned int offset;
};
+/** FUSE folio descriptor */
+struct fuse_folio_desc {
+ unsigned int length;
+ unsigned int offset;
+};
+
struct fuse_args {
uint64_t nodeid;
uint32_t opcode;
@@ -290,9 +296,19 @@ struct fuse_args {
struct fuse_args_pages {
struct fuse_args args;
- struct page **pages;
- struct fuse_page_desc *descs;
- unsigned int num_pages;
+ union {
+ struct {
+ struct page **pages;
+ struct fuse_page_desc *descs;
+ unsigned int num_pages;
+ };
+ struct {
+ struct folio **folios;
+ struct fuse_folio_desc *folio_descs;
+ unsigned int num_folios;
+ };
+ };
+ bool uses_folios;
};
#define FUSE_ARGS(args) struct fuse_args args = {}
@@ -842,6 +858,9 @@ struct fuse_conn {
/** Version counter for attribute changes */
atomic64_t attr_version;
+ /** Version counter for evict inode */
+ atomic64_t evict_ctr;
+
/** Called on final put */
void (*release)(struct fuse_conn *);
@@ -931,6 +950,11 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc)
return atomic64_read(&fc->attr_version);
}
+static inline u64 fuse_get_evict_ctr(struct fuse_conn *fc)
+{
+ return atomic64_read(&fc->evict_ctr);
+}
+
static inline bool fuse_stale_inode(const struct inode *inode, int generation,
struct fuse_attr *attr)
{
@@ -990,7 +1014,8 @@ extern const struct dentry_operations fuse_root_dentry_operations;
*/
struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
int generation, struct fuse_attr *attr,
- u64 attr_valid, u64 attr_version);
+ u64 attr_valid, u64 attr_version,
+ u64 evict_ctr);
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
struct fuse_entry_out *outarg, struct inode **inode);
@@ -1089,7 +1114,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
- u64 attr_valid, u32 cache_mask);
+ u64 attr_valid, u32 cache_mask,
+ u64 evict_ctr);
u32 fuse_get_cache_mask(struct inode *inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 96050028d91b..990411ada747 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -164,6 +164,14 @@ static void fuse_evict_inode(struct inode *inode)
fuse_cleanup_submount_lookup(fc, fi->submount_lookup);
fi->submount_lookup = NULL;
}
+ /*
+ * Evict of non-deleted inode may race with outstanding
+ * LOOKUP/READDIRPLUS requests and result in inconsistency when
+ * the request finishes. Deal with that here by bumping a
+ * counter that can be compared to the starting value.
+ */
+ if (inode->i_nlink > 0)
+ atomic64_inc(&fc->evict_ctr);
}
if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
WARN_ON(!list_empty(&fi->write_files));
@@ -196,17 +204,30 @@ static ino_t fuse_squash_ino(u64 ino64)
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
- u64 attr_valid, u32 cache_mask)
+ u64 attr_valid, u32 cache_mask,
+ u64 evict_ctr)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
lockdep_assert_held(&fi->lock);
+ /*
+ * Clear basic stats from invalid mask.
+ *
+ * Don't do this if this is coming from a fuse_iget() call and there
+ * might have been a racing evict which would've invalidated the result
+ * if the attr_version would've been preserved.
+ *
+ * !evict_ctr -> this is create
+ * fi->attr_version != 0 -> this is not a new inode
+ * evict_ctr == fuse_get_evict_ctr() -> no evicts while during request
+ */
+ if (!evict_ctr || fi->attr_version || evict_ctr == fuse_get_evict_ctr(fc))
+ set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0);
+
fi->attr_version = atomic64_inc_return(&fc->attr_version);
fi->i_time = attr_valid;
- /* Clear basic stats from invalid mask */
- set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0);
inode->i_ino = fuse_squash_ino(attr->ino);
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
@@ -220,12 +241,10 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
attr->mtimensec = min_t(u32, attr->mtimensec, NSEC_PER_SEC - 1);
attr->ctimensec = min_t(u32, attr->ctimensec, NSEC_PER_SEC - 1);
- inode->i_atime.tv_sec = attr->atime;
- inode->i_atime.tv_nsec = attr->atimensec;
+ inode_set_atime(inode, attr->atime, attr->atimensec);
/* mtime from server may be stale due to local buffered write */
if (!(cache_mask & STATX_MTIME)) {
- inode->i_mtime.tv_sec = attr->mtime;
- inode->i_mtime.tv_nsec = attr->mtimensec;
+ inode_set_mtime(inode, attr->mtime, attr->mtimensec);
}
if (!(cache_mask & STATX_CTIME)) {
inode_set_ctime(inode, attr->ctime, attr->ctimensec);
@@ -287,9 +306,9 @@ u32 fuse_get_cache_mask(struct inode *inode)
return STATX_MTIME | STATX_CTIME | STATX_SIZE;
}
-void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
- struct fuse_statx *sx,
- u64 attr_valid, u64 attr_version)
+static void fuse_change_attributes_i(struct inode *inode, struct fuse_attr *attr,
+ struct fuse_statx *sx, u64 attr_valid,
+ u64 attr_version, u64 evict_ctr)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
@@ -308,12 +327,12 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
attr->size = i_size_read(inode);
if (cache_mask & STATX_MTIME) {
- attr->mtime = inode->i_mtime.tv_sec;
- attr->mtimensec = inode->i_mtime.tv_nsec;
+ attr->mtime = inode_get_mtime_sec(inode);
+ attr->mtimensec = inode_get_mtime_nsec(inode);
}
if (cache_mask & STATX_CTIME) {
- attr->ctime = inode_get_ctime(inode).tv_sec;
- attr->ctimensec = inode_get_ctime(inode).tv_nsec;
+ attr->ctime = inode_get_ctime_sec(inode);
+ attr->ctimensec = inode_get_ctime_nsec(inode);
}
if ((attr_version != 0 && fi->attr_version > attr_version) ||
@@ -322,8 +341,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
return;
}
- old_mtime = inode->i_mtime;
- fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask);
+ old_mtime = inode_get_mtime(inode);
+ fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask,
+ evict_ctr);
oldsize = inode->i_size;
/*
@@ -364,6 +384,13 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
fuse_dax_dontcache(inode, attr->flags);
}
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+ struct fuse_statx *sx, u64 attr_valid,
+ u64 attr_version)
+{
+ fuse_change_attributes_i(inode, attr, sx, attr_valid, attr_version, 0);
+}
+
static void fuse_init_submount_lookup(struct fuse_submount_lookup *sl,
u64 nodeid)
{
@@ -376,8 +403,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
{
inode->i_mode = attr->mode & S_IFMT;
inode->i_size = attr->size;
- inode->i_mtime.tv_sec = attr->mtime;
- inode->i_mtime.tv_nsec = attr->mtimensec;
+ inode_set_mtime(inode, attr->mtime, attr->mtimensec);
inode_set_ctime(inode, attr->ctime, attr->ctimensec);
if (S_ISREG(inode->i_mode)) {
fuse_init_common(inode);
@@ -419,7 +445,8 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)
struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
int generation, struct fuse_attr *attr,
- u64 attr_valid, u64 attr_version)
+ u64 attr_valid, u64 attr_version,
+ u64 evict_ctr)
{
struct inode *inode;
struct fuse_inode *fi;
@@ -480,8 +507,8 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
fi->nlookup++;
spin_unlock(&fi->lock);
done:
- fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version);
-
+ fuse_change_attributes_i(inode, attr, NULL, attr_valid, attr_version,
+ evict_ctr);
return inode;
}
@@ -937,6 +964,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
fc->initialized = 0;
fc->connected = 1;
atomic64_set(&fc->attr_version, 1);
+ atomic64_set(&fc->evict_ctr, 1);
get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
fc->pid_ns = get_pid_ns(task_active_pid_ns(current));
fc->user_ns = get_user_ns(user_ns);
@@ -993,7 +1021,7 @@ static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
attr.mode = mode;
attr.ino = FUSE_ROOT_ID;
attr.nlink = 1;
- return fuse_iget(sb, 1, 0, &attr, 0, 0);
+ return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0);
}
struct fuse_inode_handle {
@@ -1498,17 +1526,19 @@ EXPORT_SYMBOL_GPL(fuse_dev_free);
static void fuse_fill_attr_from_inode(struct fuse_attr *attr,
const struct fuse_inode *fi)
{
+ struct timespec64 atime = inode_get_atime(&fi->inode);
+ struct timespec64 mtime = inode_get_mtime(&fi->inode);
struct timespec64 ctime = inode_get_ctime(&fi->inode);
*attr = (struct fuse_attr){
.ino = fi->inode.i_ino,
.size = fi->inode.i_size,
.blocks = fi->inode.i_blocks,
- .atime = fi->inode.i_atime.tv_sec,
- .mtime = fi->inode.i_mtime.tv_sec,
+ .atime = atime.tv_sec,
+ .mtime = mtime.tv_sec,
.ctime = ctime.tv_sec,
- .atimensec = fi->inode.i_atime.tv_nsec,
- .mtimensec = fi->inode.i_mtime.tv_nsec,
+ .atimensec = atime.tv_nsec,
+ .mtimensec = mtime.tv_nsec,
.ctimensec = ctime.tv_nsec,
.mode = fi->inode.i_mode,
.nlink = fi->inode.i_nlink,
@@ -1558,7 +1588,10 @@ static int fuse_fill_super_submount(struct super_block *sb,
return -ENOMEM;
fuse_fill_attr_from_inode(&root_attr, parent_fi);
- root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0);
+ root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0,
+ fuse_get_evict_ctr(fm->fc));
+ if (!root)
+ return -ENOMEM;
/*
* This inode is just a duplicate, so it is not looked up and
* its nlookup should not be incremented. fuse_iget() does
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index eb715a502613..2dad402e049a 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -153,7 +153,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
static int fuse_direntplus_link(struct file *file,
struct fuse_direntplus *direntplus,
- u64 attr_version)
+ u64 attr_version, u64 evict_ctr)
{
struct fuse_entry_out *o = &direntplus->entry_out;
struct fuse_dirent *dirent = &direntplus->dirent;
@@ -237,7 +237,7 @@ static int fuse_direntplus_link(struct file *file,
} else {
inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
&o->attr, ATTR_TIMEOUT(o),
- attr_version);
+ attr_version, evict_ctr);
if (!inode)
inode = ERR_PTR(-ENOMEM);
@@ -288,7 +288,8 @@ static void fuse_force_forget(struct file *file, u64 nodeid)
}
static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
- struct dir_context *ctx, u64 attr_version)
+ struct dir_context *ctx, u64 attr_version,
+ u64 evict_ctr)
{
struct fuse_direntplus *direntplus;
struct fuse_dirent *dirent;
@@ -323,7 +324,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
buf += reclen;
nbytes -= reclen;
- ret = fuse_direntplus_link(file, direntplus, attr_version);
+ ret = fuse_direntplus_link(file, direntplus, attr_version, evict_ctr);
if (ret)
fuse_force_forget(file, direntplus->entry_out.nodeid);
}
@@ -335,26 +336,28 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
{
int plus;
ssize_t res;
- struct page *page;
+ struct folio *folio;
struct inode *inode = file_inode(file);
struct fuse_mount *fm = get_fuse_mount(inode);
struct fuse_io_args ia = {};
struct fuse_args_pages *ap = &ia.ap;
- struct fuse_page_desc desc = { .length = PAGE_SIZE };
- u64 attr_version = 0;
+ struct fuse_folio_desc desc = { .length = PAGE_SIZE };
+ u64 attr_version = 0, evict_ctr = 0;
bool locked;
- page = alloc_page(GFP_KERNEL);
- if (!page)
+ folio = folio_alloc(GFP_KERNEL, 0);
+ if (!folio)
return -ENOMEM;
plus = fuse_use_readdirplus(inode, ctx);
ap->args.out_pages = true;
- ap->num_pages = 1;
- ap->pages = &page;
- ap->descs = &desc;
+ ap->uses_folios = true;
+ ap->num_folios = 1;
+ ap->folios = &folio;
+ ap->folio_descs = &desc;
if (plus) {
attr_version = fuse_get_attr_version(fm->fc);
+ evict_ctr = fuse_get_evict_ctr(fm->fc);
fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
FUSE_READDIRPLUS);
} else {
@@ -371,15 +374,16 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
if (ff->open_flags & FOPEN_CACHE_DIR)
fuse_readdir_cache_end(file, ctx->pos);
} else if (plus) {
- res = parse_dirplusfile(page_address(page), res,
- file, ctx, attr_version);
+ res = parse_dirplusfile(folio_address(folio), res,
+ file, ctx, attr_version,
+ evict_ctr);
} else {
- res = parse_dirfile(page_address(page), res, file,
+ res = parse_dirfile(folio_address(folio), res, file,
ctx);
}
}
- __free_page(page);
+ folio_put(folio);
fuse_invalidate_atime(inode);
return res;
}
@@ -480,7 +484,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
if (!fi->rdc.cached) {
/* Starting cache? Set cache mtime. */
if (!ctx->pos && !fi->rdc.size) {
- fi->rdc.mtime = inode->i_mtime;
+ fi->rdc.mtime = inode_get_mtime(inode);
fi->rdc.iversion = inode_query_iversion(inode);
}
spin_unlock(&fi->rdc.lock);
@@ -492,8 +496,10 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
* changed, and reset the cache if so.
*/
if (!ctx->pos) {
+ struct timespec64 mtime = inode_get_mtime(inode);
+
if (inode_peek_iversion(inode) != fi->rdc.iversion ||
- !timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
+ !timespec64_equal(&fi->rdc.mtime, &mtime)) {
fuse_rdc_reset(inode);
goto retry_locked;
}
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 34e19f6c3468..32c91289c196 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -204,7 +204,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
return err;
type = be16_to_cpu(tmp.type);
- if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
+ if (!is_hfs_thread_record_type(type)) {
pr_err("found bad thread record in catalog\n");
return -EIO;
}
@@ -350,23 +350,22 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
goto out;
if (!str) {
- int len;
+ hfsplus_cat_entry entry = {0};
hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
- err = hfs_brec_find(&fd, hfs_find_rec_by_key);
+ err = hfsplus_brec_read_cat(&fd, &entry);
if (err)
goto out;
- off = fd.entryoffset +
- offsetof(struct hfsplus_cat_thread, nodeName);
- fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
- hfs_bnode_read(fd.bnode,
- &fd.search_key->cat.name.length, off, 2);
- len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
- hfs_bnode_read(fd.bnode,
- &fd.search_key->cat.name.unicode,
- off + 2, len);
- fd.search_key->key_len = cpu_to_be16(6 + len);
+ type = be16_to_cpu(entry.type);
+ if (!is_hfs_thread_record_type(type)) {
+ pr_err("found bad thread record in catalog\n");
+ err = -EIO;
+ goto out;
+ }
+
+ hfsplus_cat_build_key_uni(fd.search_key, dir->i_ino,
+ &entry.thread.nodeName);
} else {
err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
if (unlikely(err))
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 0e45973be3df..f92ccc7a8a82 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -542,6 +542,12 @@ static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *threa
be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr);
}
+static inline
+bool is_hfs_thread_record_type(u16 type)
+{
+ return type == HFSPLUS_FOLDER_THREAD || type == HFSPLUS_FILE_THREAD;
+}
+
int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry);
/*
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 914e850b9cbb..9bc9f04e4b7a 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1553,7 +1553,8 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
goto out_free;
return 0;
out_free:
- kfree(sbinfo->spool);
+ if (sbinfo->spool)
+ hugepage_put_subpool(sbinfo->spool);
kfree(sbinfo);
return -ENOMEM;
}
diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c
index c4da3f634b92..fa3daeebad19 100644
--- a/fs/isofs/compress.c
+++ b/fs/isofs/compress.c
@@ -65,12 +65,14 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start,
/* Empty block? */
if (block_size == 0) {
for ( i = 0 ; i < pcount ; i++ ) {
+ unsigned int off = i ? 0 : poffset;
+
if (!pages[i])
continue;
- memzero_page(pages[i], 0, PAGE_SIZE);
+ memzero_page(pages[i], off, PAGE_SIZE - off);
SetPageUptodate(pages[i]);
}
- return ((loff_t)pcount) << PAGE_SHIFT;
+ return (((loff_t)pcount) << PAGE_SHIFT) - poffset;
}
/* Because zlib is not thread-safe, do all the I/O at the top. */
@@ -291,6 +293,7 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount,
memzero_page(*pages, poffset, PAGE_SIZE - poffset);
SetPageUptodate(*pages);
}
+ brelse(bh);
return 0;
}
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index e64e0110f66b..9e2a67bfd070 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -286,12 +286,10 @@ nlm_file_inuse(struct nlm_file *file)
return 0;
}
-static void nlm_close_files(struct nlm_file *file)
+static void nlm_file_release(struct nlm_file *file)
{
- if (file->f_file[O_RDONLY])
- nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
- if (file->f_file[O_WRONLY])
- nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
+ if (!nlm_file_inuse(file))
+ nlm_delete_file(file);
}
/*
@@ -301,32 +299,41 @@ static int
nlm_traverse_files(void *data, nlm_host_match_fn_t match,
int (*is_failover_file)(void *data, struct nlm_file *file))
{
- struct hlist_node *next;
- struct nlm_file *file;
+ struct nlm_file *file, *next;
int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
- hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
- if (is_failover_file && !is_failover_file(data, file))
- continue;
+ file = hlist_entry_safe(nlm_files[i].first,
+ struct nlm_file, f_list);
+ if (file)
file->f_count++;
- mutex_unlock(&nlm_file_mutex);
-
- /* Traverse locks, blocks and shares of this file
- * and update file->f_locks count */
- if (nlm_inspect_file(data, file, match))
- ret = 1;
+ while (file) {
+ /*
+ * Pin the next neighbour before we drop the mutex
+ * for nlm_inspect_file(); a concurrent
+ * nlm_release_file() under the same mutex would
+ * otherwise be free to unlink and kfree it during
+ * the unlock window, leaving us to dereference a
+ * freed slab when we walked to next afterwards.
+ */
+ next = hlist_entry_safe(file->f_list.next,
+ struct nlm_file, f_list);
+ if (next)
+ next->f_count++;
+
+ if (!is_failover_file || is_failover_file(data, file)) {
+ mutex_unlock(&nlm_file_mutex);
+
+ if (nlm_inspect_file(data, file, match))
+ ret = 1;
+
+ mutex_lock(&nlm_file_mutex);
+ }
- mutex_lock(&nlm_file_mutex);
file->f_count--;
- /* No more references to this file. Let go of it. */
- if (list_empty(&file->f_blocks) && !file->f_locks
- && !file->f_shares && !file->f_count) {
- hlist_del(&file->f_list);
- nlm_close_files(file);
- kfree(file);
- }
+ nlm_file_release(file);
+ file = next;
}
}
mutex_unlock(&nlm_file_mutex);
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 4350388d40cd..ba4d6301696c 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -208,9 +208,21 @@ static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
- if (clp->cl_cb_ident)
+ if (clp->cl_cb_ident) {
idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
+ clp->cl_cb_ident = 0;
+ }
+}
+
+void nfs_cb_idr_remove(struct nfs_client *clp)
+{
+ struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+
+ spin_lock(&nn->nfs_client_lock);
+ nfs_cb_idr_remove_locked(clp);
+ spin_unlock(&nn->nfs_client_lock);
}
+EXPORT_SYMBOL_GPL(nfs_cb_idr_remove);
static void pnfs_init_server(struct nfs_server *server)
{
@@ -1232,6 +1244,10 @@ void nfs_clients_init(struct net *net)
INIT_LIST_HEAD(&nn->nfs_volume_list);
#if IS_ENABLED(CONFIG_NFS_V4)
idr_init(&nn->cb_ident_idr);
+#endif
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+ INIT_LIST_HEAD(&nn->nfs4_data_server_cache);
+ spin_lock_init(&nn->nfs4_data_server_lock);
#endif
spin_lock_init(&nn->nfs_client_lock);
nn->boot_time = ktime_get_real();
@@ -1249,6 +1265,9 @@ void nfs_clients_exit(struct net *net)
nfs_cleanup_cb_ident_idr(net);
WARN_ON_ONCE(!list_empty(&nn->nfs_client_list));
WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list));
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+ WARN_ON_ONCE(!list_empty(&nn->nfs4_data_server_cache));
+#endif
}
#ifdef CONFIG_PROC_FS
diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c
index d5f1fbfd9a0c..e90e1cf2f0aa 100644
--- a/fs/nfs/filelayout/filelayoutdev.c
+++ b/fs/nfs/filelayout/filelayoutdev.c
@@ -169,7 +169,8 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
goto out_err_free_deviceid;
}
- dsaddr->ds_list[i] = nfs4_pnfs_ds_add(net, &dsaddrs, gfp_flags);
+ dsaddr->ds_list[i] = nfs4_pnfs_ds_add(net, &dsaddrs, 4,
+ gfp_flags);
if (!dsaddr->ds_list[i])
goto out_err_drain_dsaddrs;
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index ed18e9e87c25..e764e5a25f43 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -149,7 +149,8 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
new_ds->ds_versions = ds_versions;
new_ds->ds_versions_cnt = version_count;
- new_ds->ds = nfs4_pnfs_ds_add(net, &dsaddrs, gfp_flags);
+ new_ds->ds = nfs4_pnfs_ds_add(net, &dsaddrs, ds_versions[0].version,
+ gfp_flags);
if (!new_ds->ds)
goto out_err_drain_dsaddrs;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 0d7facfdafb9..08326986650a 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1376,9 +1376,7 @@ int nfs_clear_invalid_mapping(struct address_space *mapping)
ret = nfs_invalidate_mapping(inode, mapping);
trace_nfs_invalidate_mapping_exit(inode, ret);
- clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
- smp_mb__after_atomic();
- wake_up_bit(bitlock, NFS_INO_INVALIDATING);
+ clear_and_wake_up_bit(NFS_INO_INVALIDATING, bitlock);
out:
return ret;
}
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 1ab81d639d5c..2290433451ce 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -218,6 +218,7 @@ void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *);
extern void nfs_put_client(struct nfs_client *);
extern void nfs_free_client(struct nfs_client *);
+void nfs_cb_idr_remove(struct nfs_client *clp);
extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
extern struct nfs_client *
nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h
index a68b21603ea9..6ba3ea39e928 100644
--- a/fs/nfs/netns.h
+++ b/fs/nfs/netns.h
@@ -31,7 +31,11 @@ struct nfs_net {
unsigned short nfs_callback_tcpport;
unsigned short nfs_callback_tcpport6;
int cb_users[NFS4_MAX_MINOR_VERSION + 1];
-#endif
+#endif /* CONFIG_NFS_V4 */
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+ struct list_head nfs4_data_server_cache;
+ spinlock_t nfs4_data_server_lock;
+#endif /* CONFIG_NFS_V4_1 */
struct nfs_netns_client *nfs_client;
spinlock_t nfs_client_lock;
ktime_t boot_time;
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 3f31d05e87ae..9311944a47f5 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -270,6 +270,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
return clp;
error:
+ nfs_cb_idr_remove(clp);
nfs_free_client(clp);
return ERR_PTR(err);
}
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index e4aa1d0ccfa2..84b6b2f99026 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1967,15 +1967,6 @@ static bool pnfs_is_first_layoutget(struct pnfs_layout_hdr *lo)
return test_bit(NFS_LAYOUT_FIRST_LAYOUTGET, &lo->plh_flags);
}
-static void pnfs_clear_first_layoutget(struct pnfs_layout_hdr *lo)
-{
- unsigned long *bitlock = &lo->plh_flags;
-
- clear_bit_unlock(NFS_LAYOUT_FIRST_LAYOUTGET, bitlock);
- smp_mb__after_atomic();
- wake_up_bit(bitlock, NFS_LAYOUT_FIRST_LAYOUTGET);
-}
-
static void _add_to_server_list(struct pnfs_layout_hdr *lo,
struct nfs_server *server)
{
@@ -2149,7 +2140,8 @@ pnfs_update_layout(struct inode *ino,
iomode, lo, lseg,
PNFS_UPDATE_LAYOUT_INVALID_OPEN);
nfs4_schedule_stateid_recovery(server, ctx->state);
- pnfs_clear_first_layoutget(lo);
+ clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET,
+ &lo->plh_flags);
pnfs_put_layout_hdr(lo);
goto lookup_again;
}
@@ -2216,7 +2208,8 @@ pnfs_update_layout(struct inode *ino,
}
if (lseg) {
if (first)
- pnfs_clear_first_layoutget(lo);
+ clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET,
+ &lo->plh_flags);
trace_pnfs_update_layout(ino, pos, count,
iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY);
pnfs_put_layout_hdr(lo);
@@ -2228,7 +2221,7 @@ pnfs_update_layout(struct inode *ino,
out_put_layout_hdr:
if (first)
- pnfs_clear_first_layoutget(lo);
+ clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET, &lo->plh_flags);
trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
PNFS_UPDATE_LAYOUT_EXIT);
pnfs_put_layout_hdr(lo);
@@ -2320,7 +2313,7 @@ static void _lgopen_prepare_attached(struct nfs4_opendata *data,
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, ¤t_stateid, &rng,
nfs_io_gfp_mask());
if (!lgp) {
- pnfs_clear_first_layoutget(lo);
+ clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET, &lo->plh_flags);
nfs_layoutget_end(lo);
pnfs_put_layout_hdr(lo);
return;
@@ -2424,7 +2417,8 @@ void nfs4_lgopen_release(struct nfs4_layoutget *lgp)
{
if (lgp != NULL) {
if (lgp->lo) {
- pnfs_clear_first_layoutget(lgp->lo);
+ clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET,
+ &lgp->lo->plh_flags);
nfs_layoutget_end(lgp->lo);
}
pnfs_layoutget_free(lgp);
@@ -2506,6 +2500,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
spin_unlock(&ino->i_lock);
lseg->pls_layout = lo;
NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg);
+ pnfs_free_lseg_list(&free_me);
return ERR_PTR(-EAGAIN);
}
@@ -3145,15 +3140,6 @@ pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
}
EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages);
-static void pnfs_clear_layoutcommitting(struct inode *inode)
-{
- unsigned long *bitlock = &NFS_I(inode)->flags;
-
- clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock);
- smp_mb__after_atomic();
- wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING);
-}
-
/*
* There can be multiple RW segments.
*/
@@ -3178,7 +3164,7 @@ static void pnfs_list_write_lseg_done(struct inode *inode, struct list_head *lis
pnfs_put_lseg(lseg);
}
- pnfs_clear_layoutcommitting(inode);
+ clear_and_wake_up_bit(NFS_INO_LAYOUTCOMMITTING, &NFS_I(inode)->flags);
}
void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg)
@@ -3318,7 +3304,7 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync)
spin_unlock(&inode->i_lock);
kfree(data);
clear_layoutcommitting:
- pnfs_clear_layoutcommitting(inode);
+ clear_and_wake_up_bit(NFS_INO_LAYOUTCOMMITTING, &NFS_I(inode)->flags);
goto out;
}
EXPORT_SYMBOL_GPL(pnfs_layoutcommit_inode);
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index a613466f6f22..f82defddcd82 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -62,6 +62,7 @@ struct nfs4_pnfs_ds {
const struct net *ds_net;
struct nfs_client *ds_clp;
refcount_t ds_count;
+ u32 ds_version; /* cache key, with ds_addrs */
unsigned long ds_state;
#define NFS4DS_CONNECTING 0 /* ds is establishing connection */
};
@@ -407,7 +408,7 @@ void pnfs_generic_write_commit_done(struct rpc_task *task, void *data);
void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds);
struct nfs4_pnfs_ds *nfs4_pnfs_ds_add(const struct net *net,
struct list_head *dsaddrs,
- gfp_t gfp_flags);
+ u32 version, gfp_t gfp_flags);
void nfs4_pnfs_v3_ds_connect_unload(void);
int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
struct nfs4_deviceid_node *devid, unsigned int timeo,
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index 4f343dd1e65b..01779ef092ec 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -16,6 +16,7 @@
#include "nfs4session.h"
#include "internal.h"
#include "pnfs.h"
+#include "netns.h"
#define NFSDBG_FACILITY NFSDBG_PNFS
@@ -551,14 +552,14 @@ EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);
/*
* Data server cache
*
- * Data servers can be mapped to different device ids.
- * nfs4_pnfs_ds reference counting
+ * Data servers can be mapped to different device ids, but should
+ * never be shared between net namespaces.
+ *
+ * nfs4_pnfs_ds reference counting:
* - set to 1 on allocation
* - incremented when a device id maps a data server already in the cache.
* - decremented when deviceid is removed from the cache.
*/
-static DEFINE_SPINLOCK(nfs4_ds_cache_lock);
-static LIST_HEAD(nfs4_data_server_cache);
/* Debug routines */
static void
@@ -648,15 +649,17 @@ _same_data_server_addrs_locked(const struct list_head *dsaddrs1,
}
/*
- * Lookup DS by addresses. nfs4_ds_cache_lock is held
+ * Lookup DS by addresses and NFS version. nfs4_ds_cache_lock is held
*/
static struct nfs4_pnfs_ds *
-_data_server_lookup_locked(const struct net *net, const struct list_head *dsaddrs)
+_data_server_lookup_locked(const struct nfs_net *nn,
+ const struct list_head *dsaddrs, u32 version)
{
struct nfs4_pnfs_ds *ds;
- list_for_each_entry(ds, &nfs4_data_server_cache, ds_node)
- if (ds->ds_net == net && _same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
+ list_for_each_entry(ds, &nn->nfs4_data_server_cache, ds_node)
+ if (ds->ds_version == version &&
+ _same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
return ds;
return NULL;
}
@@ -700,10 +703,11 @@ static void destroy_ds(struct nfs4_pnfs_ds *ds)
void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds)
{
- if (refcount_dec_and_lock(&ds->ds_count,
- &nfs4_ds_cache_lock)) {
+ struct nfs_net *nn = net_generic(ds->ds_net, nfs_net_id);
+
+ if (refcount_dec_and_lock(&ds->ds_count, &nn->nfs4_data_server_lock)) {
list_del_init(&ds->ds_node);
- spin_unlock(&nfs4_ds_cache_lock);
+ spin_unlock(&nn->nfs4_data_server_lock);
destroy_ds(ds);
}
}
@@ -763,8 +767,10 @@ nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags)
* uncached and return cached struct nfs4_pnfs_ds.
*/
struct nfs4_pnfs_ds *
-nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, gfp_t gfp_flags)
+nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, u32 version,
+ gfp_t gfp_flags)
{
+ struct nfs_net *nn = net_generic(net, nfs_net_id);
struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
char *remotestr;
@@ -780,8 +786,8 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, gfp_t gfp_fla
/* this is only used for debugging, so it's ok if its NULL */
remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
- spin_lock(&nfs4_ds_cache_lock);
- tmp_ds = _data_server_lookup_locked(net, dsaddrs);
+ spin_lock(&nn->nfs4_data_server_lock);
+ tmp_ds = _data_server_lookup_locked(nn, dsaddrs, version);
if (tmp_ds == NULL) {
INIT_LIST_HEAD(&ds->ds_addrs);
list_splice_init(dsaddrs, &ds->ds_addrs);
@@ -790,7 +796,8 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, gfp_t gfp_fla
INIT_LIST_HEAD(&ds->ds_node);
ds->ds_net = net;
ds->ds_clp = NULL;
- list_add(&ds->ds_node, &nfs4_data_server_cache);
+ ds->ds_version = version;
+ list_add(&ds->ds_node, &nn->nfs4_data_server_cache);
dprintk("%s add new data server %s\n", __func__,
ds->ds_remotestr);
} else {
@@ -802,7 +809,7 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, gfp_t gfp_fla
refcount_read(&tmp_ds->ds_count));
ds = tmp_ds;
}
- spin_unlock(&nfs4_ds_cache_lock);
+ spin_unlock(&nn->nfs4_data_server_lock);
out:
return ds;
}
diff --git a/fs/nfsd/flexfilelayoutxdr.c b/fs/nfsd/flexfilelayoutxdr.c
index 223a10f37898..c05d1d3e2021 100644
--- a/fs/nfsd/flexfilelayoutxdr.c
+++ b/fs/nfsd/flexfilelayoutxdr.c
@@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
struct ff_idmap uid;
struct ff_idmap gid;
- fh_len = 4 + fl->fh.size;
+ fh_len = 4 + xdr_align_size(fl->fh.size);
uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid));
gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid));
- /* 8 + len for recording the length, name, and padding */
- ds_len = 20 + sizeof(stateid_opaque_t) + 4 + fh_len +
- 8 + uid.len + 8 + gid.len;
+ /* data server entry: deviceid + efficiency + stateid + fh list +
+ * user + group + flags + stats_collect_hint
+ */
+ ds_len = 16 + 4 + 4 + sizeof(stateid_opaque_t) + 4 + fh_len +
+ 4 + xdr_align_size(uid.len) +
+ 4 + xdr_align_size(gid.len) +
+ 4 + 4;
+ /* mirror: ds_count + ds */
mirror_len = 4 + ds_len;
- /* The layout segment */
- len = 20 + mirror_len;
+ /* stripe_unit + mirror_count + mirror */
+ len = 12 + mirror_len;
p = xdr_reserve_space(xdr, sizeof(__be32) + len);
if (!p)
@@ -94,7 +99,8 @@ nfsd4_ff_encode_getdeviceinfo(struct xdr_stream *xdr,
}
/* len + padding for two strings */
- addr_len = 16 + da->netaddr.netid_len + da->netaddr.addr_len;
+ addr_len = 8 + xdr_align_size(da->netaddr.netid_len) +
+ xdr_align_size(da->netaddr.addr_len);
ver_len = 20;
len = 4 + ver_len + 4 + addr_len;
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index bdcfab9e3a32..da30e967ca83 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -115,14 +115,19 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
inode_lock(inode);
- error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
- argp->acl_access);
- if (error)
- goto out_drop_lock;
- error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
- argp->acl_default);
- if (error)
- goto out_drop_lock;
+ error = 0;
+ if (argp->mask & NFS_ACL) {
+ error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
+ ACL_TYPE_ACCESS, argp->acl_access);
+ if (error)
+ goto out_drop_lock;
+ }
+ if (argp->mask & NFS_DFACL) {
+ error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
+ ACL_TYPE_DEFAULT, argp->acl_default);
+ if (error)
+ goto out_drop_lock;
+ }
inode_unlock(inode);
@@ -248,22 +253,21 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
-
- if (dentry == NULL || d_really_is_negative(dentry))
- return true;
- inode = d_inode(dentry);
-
- if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
- return false;
- if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
- return false;
-
- if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
- resp->mask & NFS_ACL, 0))
- return false;
- if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
- resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
- return false;
+ switch (resp->status) {
+ case nfs_ok:
+ inode = d_inode(dentry);
+ if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
+ return false;
+ if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
+ return false;
+ if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
+ resp->mask & NFS_ACL, 0))
+ return false;
+ if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
+ resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
+ return false;
+ break;
+ }
return true;
}
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 4e9c0f2ef796..638c1635d03d 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -105,12 +105,17 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
inode_lock(inode);
- error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
- argp->acl_access);
- if (error)
- goto out_drop_lock;
- error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
- argp->acl_default);
+ error = 0;
+ if (argp->mask & NFS_ACL) {
+ error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
+ ACL_TYPE_ACCESS, argp->acl_access);
+ if (error)
+ goto out_drop_lock;
+ }
+ if (argp->mask & NFS_DFACL) {
+ error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry,
+ ACL_TYPE_DEFAULT, argp->acl_default);
+ }
out_drop_lock:
inode_unlock(inode);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 5767080362e8..c82d96089da5 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1639,6 +1639,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
/* See RFC 7862 p.67: */
if (bytes_total == 0)
bytes_total = ULLONG_MAX;
+ since = READ_ONCE(dst->f_wb_err);
do {
if (kthread_should_stop())
break;
@@ -1653,13 +1654,14 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
} while (bytes_total > 0 && nfsd4_copy_is_async(copy));
/* for a non-zero asynchronous copy do a commit of data */
if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) {
- since = READ_ONCE(dst->f_wb_err);
end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1;
status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0);
if (!status)
status = filemap_check_wb_err(dst->f_mapping, since);
if (!status)
set_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags);
+ else if (status != -EAGAIN && status != -ESTALE)
+ nfsd_reset_write_verifier(copy->cp_nn);
}
return bytes_copied;
}
@@ -1926,7 +1928,6 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_stid *stid;
struct nfs4_cpntf_state *cps;
- struct nfs4_client *clp = cstate->clp;
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&cn->cpn_src_stateid, RD_STATE, NULL,
@@ -1938,12 +1939,14 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
cn->cpn_nsec = 0;
status = nfserrno(-ENOMEM);
+ /*
+ * The returned cps is published and fully initialized, and carries an
+ * extra reference for us; drop it once we are done with it.
+ */
cps = nfs4_alloc_init_cpntf_state(nn, stid);
if (!cps)
goto out;
memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.cs_stid, sizeof(stateid_t));
- memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t));
- memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t));
/* For now, only return one server address in cpn_src, the
* address used by the client to connect to this server.
@@ -1952,10 +1955,11 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr,
&cn->cpn_src->u.nl4_addr);
WARN_ON_ONCE(status);
- if (status) {
- nfs4_put_cpntf_state(nn, cps);
- goto out;
- }
+ /*
+ * Drop our extra reference. The membership reference keeps the entry
+ * alive for a later inter-server READ, or until the laundromat reaps it.
+ */
+ nfs4_put_cpntf_state(nn, cps);
out:
nfs4_put_stid(stid);
return status;
@@ -2686,9 +2690,22 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
op->status = nfsd4_open_omfg(rqstp, cstate, op);
goto encode_op;
}
- if (!current_fh->fh_dentry &&
- !HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
- if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
+ if (!current_fh->fh_dentry) {
+ if (HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
+ /*
+ * FOREIGN fh from inter-SSC PUTFH: only
+ * SAVEFH may proceed with a NULL fh_dentry.
+ * Per RFC 7862 S15.2.3, validation of a
+ * foreign fh is deferred to the operation
+ * that consumes it, and NFS4ERR_STALE is
+ * returned at that point.
+ */
+ if (op->opnum != OP_SAVEFH &&
+ !(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
+ op->status = nfserr_stale;
+ goto encode_op;
+ }
+ } else if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_nofilehandle;
goto encode_op;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f21878a7f393..b3dadc6bfdf0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -981,7 +981,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla
* Create a unique stateid_t to represent each COPY.
*/
static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid,
- unsigned char cs_type)
+ unsigned char cs_type, struct nfs4_stid *p_stid)
{
int new_id;
@@ -991,19 +991,34 @@ static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid,
idr_preload(GFP_KERNEL);
spin_lock(&nn->s2s_cp_lock);
new_id = idr_alloc_cyclic(&nn->s2s_cp_stateids, stid, 0, 0, GFP_NOWAIT);
- stid->cs_stid.si_opaque.so_id = new_id;
- stid->cs_stid.si_generation = 1;
+ if (new_id >= 0) {
+ stid->cs_stid.si_opaque.so_id = new_id;
+ stid->cs_stid.si_generation = 1;
+ /*
+ * Set cs_type and link onto sc_cp_list under the same lock
+ * that installed the IDR entry, so a concurrent
+ * manage_cpntf_state() sees either no entry or a fully
+ * linked cp_list.
+ */
+ stid->cs_type = cs_type;
+ if (p_stid) {
+ struct nfs4_cpntf_state *cps =
+ container_of(stid, struct nfs4_cpntf_state,
+ cp_stateid);
+
+ list_add(&cps->cp_list, &p_stid->sc_cp_list);
+ }
+ }
spin_unlock(&nn->s2s_cp_lock);
idr_preload_end();
if (new_id < 0)
return 0;
- stid->cs_type = cs_type;
return 1;
}
int nfs4_init_copy_state(struct nfsd_net *nn, struct nfsd4_copy *copy)
{
- return nfs4_init_cp_state(nn, ©->cp_stateid, NFS4_COPY_STID);
+ return nfs4_init_cp_state(nn, ©->cp_stateid, NFS4_COPY_STID, NULL);
}
struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
@@ -1014,13 +1029,21 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL);
if (!cps)
return NULL;
+ /* So a stale list_del_init() before linking is a no-op. */
+ INIT_LIST_HEAD(&cps->cp_list);
cps->cpntf_time = ktime_get_boottime_seconds();
- refcount_set(&cps->cp_stateid.cs_count, 1);
- if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID))
+ /*
+ * Fully initialize the entry before nfs4_init_cp_state() publishes it,
+ * since a concurrent OFFLOAD_CANCEL could then free it. Take an extra
+ * reference for the caller (dropped with nfs4_put_cpntf_state()).
+ */
+ memcpy(&cps->cp_p_stateid, &p_stid->sc_stateid, sizeof(stateid_t));
+ memcpy(&cps->cp_p_clid, &p_stid->sc_client->cl_clientid,
+ sizeof(clientid_t));
+ refcount_set(&cps->cp_stateid.cs_count, 2);
+ if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID,
+ p_stid))
goto out_free;
- spin_lock(&nn->s2s_cp_lock);
- list_add(&cps->cp_list, &p_stid->sc_cp_list);
- spin_unlock(&nn->s2s_cp_lock);
return cps;
out_free:
kfree(cps);
@@ -1040,18 +1063,66 @@ void nfs4_free_copy_state(struct nfsd4_copy *copy)
spin_unlock(&nn->s2s_cp_lock);
}
+/*
+ * Drop the parent's reference on an already-unlinked cpntf entry. If a
+ * concurrent holder still owns a reference, its nfs4_put_cpntf_state() does
+ * the final free.
+ *
+ * nn->s2s_cp_lock must be held.
+ */
+static void put_cpntf_state_unlinked_locked(struct nfs4_cpntf_state *cps)
+{
+ WARN_ON_ONCE(cps->cp_stateid.cs_type != NFS4_COPYNOTIFY_STID);
+ WARN_ON_ONCE(!list_empty(&cps->cp_list));
+
+ if (refcount_dec_and_test(&cps->cp_stateid.cs_count))
+ kfree(cps);
+}
+
+/*
+ * Unhash from the IDR and sc_cp_list. Gated on list_empty() to avoid
+ * evicting a recycled so_id.
+ */
+static void nfsd4_unhash_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
+{
+ lockdep_assert_held(&nn->s2s_cp_lock);
+
+ if (!list_empty(&cps->cp_list)) {
+ list_del_init(&cps->cp_list);
+ idr_remove(&nn->s2s_cp_stateids, cps->cp_stateid.cs_stid.si_opaque.so_id);
+ }
+}
+
+/*
+ * Revoke a copy-notify stateid: unlink it from the IDR and sc_cp_list first
+ * so no new finder can discover it, then drop the membership reference. Every
+ * revoke path (cancel, laundromat, drain) must use this rather than
+ * _free_cpntf_state_locked(), which unlinks only at refcount zero and so could
+ * let a second revoke free the entry under a concurrent reader.
+ *
+ * nn->s2s_cp_lock must be held.
+ */
+static void revoke_cpntf_state_locked(struct nfsd_net *nn,
+ struct nfs4_cpntf_state *cps)
+{
+ nfsd4_unhash_cpntf_state(nn, cps);
+ put_cpntf_state_unlinked_locked(cps);
+}
+
static void nfs4_free_cpntf_statelist(struct net *net, struct nfs4_stid *stid)
{
- struct nfs4_cpntf_state *cps;
+ struct nfs4_cpntf_state *cps, *tmp;
struct nfsd_net *nn;
nn = net_generic(net, nfsd_net_id);
spin_lock(&nn->s2s_cp_lock);
- while (!list_empty(&stid->sc_cp_list)) {
- cps = list_first_entry(&stid->sc_cp_list,
- struct nfs4_cpntf_state, cp_list);
- _free_cpntf_state_locked(nn, cps);
- }
+ /*
+ * Revoke unlinks each entry before dropping the parent's reference, so
+ * the drain terminates in one pass per entry regardless of cs_count; a
+ * concurrent holder does the final kfree via nfs4_put_cpntf_state().
+ */
+ list_for_each_entry_safe(cps, tmp, &stid->sc_cp_list, cp_list)
+ revoke_cpntf_state_locked(nn, cps);
spin_unlock(&nn->s2s_cp_lock);
}
@@ -2264,14 +2335,24 @@ __destroy_client(struct nfs4_client *clp)
release_openowner(oo);
}
for (i = 0; i < OWNER_HASH_SIZE; i++) {
- struct nfs4_stateowner *so, *tmp;
+ struct nfs4_stateowner *so;
- list_for_each_entry_safe(so, tmp, &clp->cl_ownerstr_hashtbl[i],
- so_strhash) {
+ spin_lock(&clp->cl_lock);
+ while (!list_empty(&clp->cl_ownerstr_hashtbl[i])) {
+ so = list_first_entry(&clp->cl_ownerstr_hashtbl[i],
+ struct nfs4_stateowner, so_strhash);
/* Should be no openowners at this point */
WARN_ON_ONCE(so->so_is_open_owner);
+ nfs4_get_stateowner(so);
+ unhash_lockowner_locked(lockowner(so));
+ spin_unlock(&clp->cl_lock);
+
remove_blocked_locks(lockowner(so));
+ nfs4_put_stateowner(so);
+
+ spin_lock(&clp->cl_lock);
}
+ spin_unlock(&clp->cl_lock);
}
nfsd4_return_all_client_layouts(clp);
nfsd4_shutdown_copy(clp);
@@ -6059,30 +6140,36 @@ static void nfsd4_ssc_shutdown_umount(struct nfsd_net *nn)
static void nfsd4_ssc_expire_umount(struct nfsd_net *nn)
{
bool do_wakeup = false;
- struct nfsd4_ssc_umount_item *ni = NULL;
- struct nfsd4_ssc_umount_item *tmp;
+ struct nfsd4_ssc_umount_item *ni;
+restart:
spin_lock(&nn->nfsd_ssc_lock);
- list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
- if (time_after(jiffies, ni->nsui_expire)) {
- if (refcount_read(&ni->nsui_refcnt) > 1)
- continue;
+ list_for_each_entry(ni, &nn->nfsd_ssc_mount_list, nsui_list) {
+ if (!time_after(jiffies, ni->nsui_expire))
+ break;
+ if (refcount_read(&ni->nsui_refcnt) > 1)
+ continue;
- /* mark being unmount */
- ni->nsui_busy = true;
- spin_unlock(&nn->nfsd_ssc_lock);
- mntput(ni->nsui_vfsmount);
- spin_lock(&nn->nfsd_ssc_lock);
+ /* Prevent concurrent setup during unmount */
+ ni->nsui_busy = true;
+ spin_unlock(&nn->nfsd_ssc_lock);
+ mntput(ni->nsui_vfsmount);
+ spin_lock(&nn->nfsd_ssc_lock);
- /* waiters need to start from begin of list */
- list_del(&ni->nsui_list);
- kfree(ni);
+ /* Force concurrent scanners to restart */
+ list_del(&ni->nsui_list);
+ kfree(ni);
- /* wakeup ssc_connect waiters */
- do_wakeup = true;
- continue;
- }
- break;
+ /* wakeup ssc_connect waiters */
+ do_wakeup = true;
+ /*
+ * Concurrent nfsd4_ssc_cancel_dul() can free any item
+ * on the list under nfsd_ssc_lock while mntput() runs
+ * above. Restart from the head; the list is short and
+ * the expire worker is periodic, so this is cheap.
+ */
+ spin_unlock(&nn->nfsd_ssc_lock);
+ goto restart;
}
if (do_wakeup)
wake_up_all(&nn->nfsd_ssc_waitq);
@@ -6239,7 +6326,7 @@ nfs4_laundromat(struct nfsd_net *nn)
cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid);
if (cps->cp_stateid.cs_type == NFS4_COPYNOTIFY_STID &&
state_expired(<, cps->cpntf_time))
- _free_cpntf_state_locked(nn, cps);
+ revoke_cpntf_state_locked(nn, cps);
}
spin_unlock(&nn->s2s_cp_lock);
nfs4_get_client_reaplist(nn, &reaplist, <);
@@ -6620,16 +6707,14 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
out:
return status;
}
-static void
-_free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
+
+static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
{
WARN_ON_ONCE(cps->cp_stateid.cs_type != NFS4_COPYNOTIFY_STID);
- if (!refcount_dec_and_test(&cps->cp_stateid.cs_count))
- return;
- list_del(&cps->cp_list);
- idr_remove(&nn->s2s_cp_stateids,
- cps->cp_stateid.cs_stid.si_opaque.so_id);
- kfree(cps);
+ if (refcount_dec_and_test(&cps->cp_stateid.cs_count)) {
+ nfsd4_unhash_cpntf_state(nn, cps);
+ kfree(cps);
+ }
}
/*
* A READ from an inter server to server COPY will have a
@@ -6654,10 +6739,20 @@ __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st,
state = NULL;
goto unlock;
}
- if (!clp)
+ if (!clp) {
refcount_inc(&state->cp_stateid.cs_count);
- else
- _free_cpntf_state_locked(nn, state);
+ } else if (memcmp(&clp->cl_clientid, &state->cp_p_clid,
+ sizeof(clientid_t))) {
+ /*
+ * OFFLOAD_CANCEL: only the creating client may cancel.
+ * so_id is guessable, so without this check any client
+ * could free another's cpntf state.
+ */
+ state = NULL;
+ goto unlock;
+ } else {
+ revoke_cpntf_state_locked(nn, state);
+ }
}
unlock:
spin_unlock(&nn->s2s_cp_lock);
@@ -7178,6 +7273,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto put_stateid;
+ status = nfs4_check_fh(&cstate->current_fh, &dp->dl_stid);
+ if (status)
+ goto put_stateid;
+
trace_nfsd_deleg_return(stateid);
wake_up_var(d_inode(cstate->current_fh.fh_dentry));
destroy_delegation(dp);
@@ -7641,6 +7740,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_no_grace;
if (!locks_in_grace(net) && lock->lk_reclaim)
goto out;
+ if (lock->lk_reclaim &&
+ test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags))
+ goto out;
if (lock->lk_reclaim)
fl_flags |= FL_RECLAIM;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 52fe95d02fc4..9fe19a889ae6 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -801,6 +801,10 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
case NF4LNK:
if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0)
return nfserr_bad_xdr;
+ if (create->cr_datalen == 0)
+ return nfserr_inval;
+ if (create->cr_datalen > NFS4_MAXPATHLEN)
+ return nfserr_nametoolong;
p = xdr_inline_decode(argp->xdr, create->cr_datalen);
if (!p)
return nfserr_bad_xdr;
@@ -1918,6 +1922,7 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
{
struct nfs42_netaddr *naddr;
__be32 *p;
+ u32 str_len;
if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
return nfserr_bad_xdr;
@@ -1947,6 +1952,18 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
return nfserr_bad_xdr;
memcpy(naddr->addr, p, naddr->addr_len);
break;
+ case NL4_NAME:
+ case NL4_URL:
+ /*
+ * Well-formed XDR, but only NL4_NETADDR is supported. Consume
+ * the utf8str_cis to keep the stream aligned, then return
+ * NFS4ERR_NOTSUPP rather than the misleading NFS4ERR_BADXDR.
+ */
+ if (xdr_stream_decode_u32(argp->xdr, &str_len) < 0)
+ return nfserr_bad_xdr;
+ if (!xdr_inline_decode(argp->xdr, str_len))
+ return nfserr_bad_xdr;
+ return nfserr_notsupp;
default:
return nfserr_bad_xdr;
}
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index c52132ecb339..e26fcc571560 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -284,7 +284,7 @@ nfsd_prune_bucket_locked(struct nfsd_net *nn, struct nfsd_drc_bucket *b,
nfsd_cacherep_unlink_locked(nn, b, rp);
list_add(&rp->c_lru, dispose);
- if (max && ++freed > max)
+ if (max && ++freed >= max)
break;
}
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 283c1a60c846..c4af2ee271cb 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -70,10 +70,8 @@ nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
if (requested == 0) /* the caller doesn't care */
return nfs_ok;
if (mode == requested) {
- if (mode == S_IFDIR && !d_can_lookup(dentry)) {
- WARN_ON_ONCE(1);
+ if (mode == S_IFDIR && !d_can_lookup(dentry))
return nfserr_notdir;
- }
return nfs_ok;
}
/*
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 36370b957b63..aca0391b1544 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -55,6 +55,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
.na_iattr = iap,
};
struct svc_fh *fhp;
+ int hosterr;
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
SVCFH_fmt(&argp->fh),
@@ -90,6 +91,12 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
if (resp->status != nfs_ok)
goto out;
+ hosterr = fh_want_write(fhp);
+ if (hosterr) {
+ resp->status = nfserrno(hosterr);
+ goto out;
+ }
+
if (delta < 0)
delta = -delta;
if (delta < MAX_TOUCH_TIME_ERROR &&
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 623f522b1565..409f36cb21f2 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -858,7 +858,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
ret->mismatch.lovers = NFSD_ACL_NRVERS;
for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) {
- if (nfsd_support_acl_version(rqstp->rq_vers) &&
+ if (nfsd_support_acl_version(i) &&
nfsd_vers(nn, i, NFSD_TEST)) {
ret->mismatch.lovers = i;
break;
@@ -868,7 +868,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
return rpc_prog_unavail;
ret->mismatch.hivers = NFSD_ACL_MINVERS;
for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
- if (nfsd_support_acl_version(rqstp->rq_vers) &&
+ if (nfsd_support_acl_version(i) &&
nfsd_vers(nn, i, NFSD_TEST)) {
ret->mismatch.hivers = i;
break;
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 5777f40c7353..c7495a6db2c4 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -9,6 +9,16 @@
#include "xdr.h"
#include "auth.h"
+/*
+ * Sun convention: a sattr time-useconds field of one full second (an
+ * otherwise out-of-range value) means "set this time to the current
+ * server time." It's needed to make permissions checks for the "touch"
+ * program across NFSv2 mounts work correctly. See description of
+ * sattr in section 6.1 of "NFS Illustrated" by Brent Callaghan,
+ * Addison-Wesley, ISBN 0-201-32750-5
+ */
+#define NFS2_SATTR_SET_TO_SERVER_TIME (1000000)
+
/*
* Mapping of S_IF* types to NFS file types
*/
@@ -172,27 +182,29 @@ svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
+ /*
+ * Range test here to prevent the multiplication from
+ * wrapping to a valid (but incorrect) value on 32-bit
+ * platforms.
+ */
+ if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
+ return false;
iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
iap->ia_atime.tv_sec = tmp1;
iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
+ if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
+ iap->ia_valid &= ~ATTR_ATIME_SET;
}
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
+ if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
+ return false;
iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
iap->ia_mtime.tv_sec = tmp1;
iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
- /*
- * Passing the invalid value useconds=1000000 for mtime
- * is a Sun convention for "set both mtime and atime to
- * current server time". It's needed to make permissions
- * checks for the "touch" program across v2 mounts to
- * Solaris and Irix boxes work correctly. See description of
- * sattr in section 6.1 of "NFS Illustrated" by
- * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
- */
- if (tmp2 == 1000000)
+ if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 5dc798e89e97..9a5f364954b4 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -134,8 +134,10 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
follow_flags = LOOKUP_AUTOMOUNT;
err = follow_down(&path, follow_flags);
- if (err < 0)
+ if (err < 0) {
+ path_put(&path);
goto out;
+ }
if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
nfsd_mountpoint(dentry, exp) == 2) {
/* This is only a mountpoint in some other namespace */
diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c
index 7a8f166f2c8d..384bd19ce994 100644
--- a/fs/nilfs2/bmap.c
+++ b/fs/nilfs2/bmap.c
@@ -181,7 +181,7 @@ static int nilfs_bmap_do_delete(struct nilfs_bmap *bmap, __u64 key)
return ret;
}
- return bmap->b_ops->bop_delete(bmap, key);
+ return bmap->b_ops->bop_delete(bmap, key, false);
}
/**
diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h
index 608168a5cb88..e7f3edde7acb 100644
--- a/fs/nilfs2/bmap.h
+++ b/fs/nilfs2/bmap.h
@@ -50,7 +50,7 @@ struct nilfs_bmap_operations {
int (*bop_lookup_contig)(const struct nilfs_bmap *, __u64, __u64 *,
unsigned int);
int (*bop_insert)(struct nilfs_bmap *, __u64, __u64);
- int (*bop_delete)(struct nilfs_bmap *, __u64);
+ int (*bop_delete)(struct nilfs_bmap *bmap, __u64 key, bool deform);
void (*bop_clear)(struct nilfs_bmap *);
int (*bop_propagate)(struct nilfs_bmap *, struct buffer_head *);
diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c
index 3dda7c39089b..2573939dde26 100644
--- a/fs/nilfs2/btnode.c
+++ b/fs/nilfs2/btnode.c
@@ -199,23 +199,23 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
ctxt->newbh = NULL;
if (inode->i_blkbits == PAGE_SHIFT) {
- struct page *opage = obh->b_page;
- lock_page(opage);
+ struct folio *ofolio = obh->b_folio;
+ folio_lock(ofolio);
retry:
/* BUG_ON(oldkey != obh->b_folio->index); */
- if (unlikely(oldkey != opage->index))
- NILFS_PAGE_BUG(opage,
+ if (unlikely(oldkey != ofolio->index))
+ NILFS_FOLIO_BUG(ofolio,
"invalid oldkey %lld (newkey=%lld)",
(unsigned long long)oldkey,
(unsigned long long)newkey);
xa_lock_irq(&btnc->i_pages);
- err = __xa_insert(&btnc->i_pages, newkey, opage, GFP_NOFS);
+ err = __xa_insert(&btnc->i_pages, newkey, ofolio, GFP_NOFS);
xa_unlock_irq(&btnc->i_pages);
/*
- * Note: page->index will not change to newkey until
+ * Note: folio->index will not change to newkey until
* nilfs_btnode_commit_change_key() will be called.
- * To protect the page in intermediate state, the page lock
+ * To protect the folio in intermediate state, the folio lock
* is held.
*/
if (!err)
@@ -227,7 +227,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
if (!err)
goto retry;
/* fallback to copy mode */
- unlock_page(opage);
+ folio_unlock(ofolio);
}
nbh = nilfs_btnode_create_block(btnc, newkey);
@@ -239,7 +239,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
return 0;
failed_unlock:
- unlock_page(obh->b_page);
+ folio_unlock(obh->b_folio);
return err;
}
@@ -252,15 +252,15 @@ void nilfs_btnode_commit_change_key(struct address_space *btnc,
{
struct buffer_head *obh = ctxt->bh, *nbh = ctxt->newbh;
__u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey;
- struct page *opage;
+ struct folio *ofolio;
if (oldkey == newkey)
return;
if (nbh == NULL) { /* blocksize == pagesize */
- opage = obh->b_page;
- if (unlikely(oldkey != opage->index))
- NILFS_PAGE_BUG(opage,
+ ofolio = obh->b_folio;
+ if (unlikely(oldkey != ofolio->index))
+ NILFS_FOLIO_BUG(ofolio,
"invalid oldkey %lld (newkey=%lld)",
(unsigned long long)oldkey,
(unsigned long long)newkey);
@@ -271,8 +271,8 @@ void nilfs_btnode_commit_change_key(struct address_space *btnc,
__xa_set_mark(&btnc->i_pages, newkey, PAGECACHE_TAG_DIRTY);
xa_unlock_irq(&btnc->i_pages);
- opage->index = obh->b_blocknr = newkey;
- unlock_page(opage);
+ ofolio->index = obh->b_blocknr = newkey;
+ folio_unlock(ofolio);
} else {
nilfs_copy_buffer(nbh, obh);
mark_buffer_dirty(nbh);
diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c
index 5e70a3478afe..785c910cdd0f 100644
--- a/fs/nilfs2/btree.c
+++ b/fs/nilfs2/btree.c
@@ -1426,6 +1426,28 @@ static void nilfs_btree_shrink(struct nilfs_bmap *btree,
path[level].bp_bh = NULL;
}
+/**
+ * nilfs_btree_discard - discard the last node for the mapping transformation
+ * @btree: bmap struct of btree
+ * @path: array of nilfs_btree_path struct
+ * @level: level of the B-tree node being operated on
+ * @keyp: argument for passing a key (unused)
+ * @ptrp: argument for passing a pointer (unused)
+ */
+static void nilfs_btree_discard(struct nilfs_bmap *btree,
+ struct nilfs_btree_path *path, int level,
+ __u64 *keyp, __u64 *ptrp)
+{
+ struct nilfs_btree_node *root = nilfs_btree_get_root(btree);
+
+ nilfs_btree_node_delete(root, 0, NULL, NULL,
+ NILFS_BTREE_ROOT_NCHILDREN_MAX);
+ nilfs_btree_node_set_level(root, level);
+
+ nilfs_btnode_delete(path[level].bp_bh);
+ path[level].bp_bh = NULL;
+}
+
static void nilfs_btree_nop(struct nilfs_bmap *btree,
struct nilfs_btree_path *path,
int level, __u64 *keyp, __u64 *ptrp)
@@ -1436,7 +1458,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree,
struct nilfs_btree_path *path,
int *levelp,
struct nilfs_bmap_stats *stats,
- struct inode *dat)
+ struct inode *dat, bool deform)
{
struct buffer_head *bh;
struct nilfs_btree_node *node, *parent, *sib;
@@ -1523,15 +1545,17 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree,
if (nilfs_btree_node_get_nchildren(node) - 1 <=
NILFS_BTREE_ROOT_NCHILDREN_MAX) {
path[level].bp_op = nilfs_btree_shrink;
- stats->bs_nblocks += 2;
- level++;
- path[level].bp_op = nilfs_btree_nop;
- goto shrink_root_child;
+ } else if (deform) {
+ path[level].bp_op = nilfs_btree_discard;
} else {
path[level].bp_op = nilfs_btree_do_delete;
stats->bs_nblocks++;
goto out;
}
+ stats->bs_nblocks += 2;
+ level++;
+ path[level].bp_op = nilfs_btree_nop;
+ goto shrink_root_child;
}
}
@@ -1582,7 +1606,7 @@ static void nilfs_btree_commit_delete(struct nilfs_bmap *btree,
nilfs_bmap_set_dirty(btree);
}
-static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key)
+static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key, bool deform)
{
struct nilfs_btree_path *path;
@@ -1602,7 +1626,8 @@ static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key)
dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL;
- ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat);
+ ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat,
+ deform);
if (ret < 0)
goto out;
nilfs_btree_commit_delete(btree, path, level, dat);
diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c
index 2d8dc6b35b54..834ce02f2afb 100644
--- a/fs/nilfs2/direct.c
+++ b/fs/nilfs2/direct.c
@@ -144,7 +144,7 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
return ret;
}
-static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key)
+static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key, bool deform)
{
union nilfs_bmap_ptr_req req;
struct inode *dat;
@@ -234,7 +234,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap,
/* no need to allocate any resource for conversion */
/* delete */
- ret = bmap->b_ops->bop_delete(bmap, key);
+ ret = bmap->b_ops->bop_delete(bmap, key, true);
if (ret < 0)
return ret;
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index 144e200c4909..dd40357d836b 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -73,7 +73,7 @@ struct buffer_head *nilfs_grab_buffer(struct inode *inode,
*/
void nilfs_forget_buffer(struct buffer_head *bh)
{
- struct page *page = bh->b_page;
+ struct folio *folio = bh->b_folio;
const unsigned long clear_bits =
(BIT(BH_Uptodate) | BIT(BH_Dirty) | BIT(BH_Mapped) |
BIT(BH_Async_Write) | BIT(BH_NILFS_Volatile) |
@@ -82,12 +82,12 @@ void nilfs_forget_buffer(struct buffer_head *bh)
lock_buffer(bh);
set_mask_bits(&bh->b_state, clear_bits, 0);
- if (nilfs_page_buffers_clean(page))
- __nilfs_clear_page_dirty(page);
+ if (nilfs_folio_buffers_clean(folio))
+ __nilfs_clear_page_dirty(&folio->page);
bh->b_blocknr = -1;
- ClearPageUptodate(page);
- ClearPageMappedToDisk(page);
+ folio_clear_uptodate(folio);
+ folio_clear_mappedtodisk(folio);
unlock_buffer(bh);
brelse(bh);
}
@@ -132,48 +132,49 @@ void nilfs_copy_buffer(struct buffer_head *dbh, struct buffer_head *sbh)
}
/**
- * nilfs_page_buffers_clean - check if a page has dirty buffers or not.
- * @page: page to be checked
+ * nilfs_folio_buffers_clean - Check if a folio has dirty buffers or not.
+ * @folio: Folio to be checked.
*
- * nilfs_page_buffers_clean() returns zero if the page has dirty buffers.
- * Otherwise, it returns non-zero value.
+ * nilfs_folio_buffers_clean() returns false if the folio has dirty buffers.
+ * Otherwise, it returns true.
*/
-int nilfs_page_buffers_clean(struct page *page)
+bool nilfs_folio_buffers_clean(struct folio *folio)
{
struct buffer_head *bh, *head;
- bh = head = page_buffers(page);
+ bh = head = folio_buffers(folio);
do {
if (buffer_dirty(bh))
- return 0;
+ return false;
bh = bh->b_this_page;
} while (bh != head);
- return 1;
+ return true;
}
-void nilfs_page_bug(struct page *page)
+void nilfs_folio_bug(struct folio *folio)
{
+ struct buffer_head *bh, *head;
struct address_space *m;
unsigned long ino;
- if (unlikely(!page)) {
- printk(KERN_CRIT "NILFS_PAGE_BUG(NULL)\n");
+ if (unlikely(!folio)) {
+ printk(KERN_CRIT "NILFS_FOLIO_BUG(NULL)\n");
return;
}
- m = page->mapping;
+ m = folio->mapping;
ino = m ? m->host->i_ino : 0;
- printk(KERN_CRIT "NILFS_PAGE_BUG(%p): cnt=%d index#=%llu flags=0x%lx "
+ printk(KERN_CRIT "NILFS_FOLIO_BUG(%p): cnt=%d index#=%llu flags=0x%lx "
"mapping=%p ino=%lu\n",
- page, page_ref_count(page),
- (unsigned long long)page->index, page->flags, m, ino);
+ folio, folio_ref_count(folio),
+ (unsigned long long)folio->index, folio->flags, m, ino);
- if (page_has_buffers(page)) {
- struct buffer_head *bh, *head;
+ head = folio_buffers(folio);
+ if (head) {
int i = 0;
- bh = head = page_buffers(page);
+ bh = head;
do {
printk(KERN_CRIT
" BH[%d] %p: cnt=%d block#=%llu state=0x%lx\n",
@@ -241,6 +242,7 @@ static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
int nilfs_copy_dirty_pages(struct address_space *dmap,
struct address_space *smap)
{
+ struct inode *smap_inode = smap->host;
struct folio_batch fbatch;
unsigned int i;
pgoff_t index = 0;
@@ -256,8 +258,19 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
struct folio *folio = fbatch.folios[i], *dfolio;
folio_lock(folio);
- if (unlikely(!folio_test_dirty(folio)))
- NILFS_PAGE_BUG(&folio->page, "inconsistent dirty state");
+ if (unlikely(!folio_test_dirty(folio))) {
+ if (WARN_ONCE(!sb_rdonly(smap_inode->i_sb),
+ "inconsistent dirty state\n"))
+ goto unlock_folio;
+
+ /*
+ * If the filesystem has been forced to read-only
+ * due to metadata corruption.
+ */
+ folio_unlock(folio);
+ err = -EROFS;
+ break;
+ }
dfolio = filemap_grab_folio(dmap, folio->index);
if (unlikely(IS_ERR(dfolio))) {
@@ -267,7 +280,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
break;
}
if (unlikely(!folio_buffers(folio)))
- NILFS_PAGE_BUG(&folio->page,
+ NILFS_FOLIO_BUG(folio,
"found empty page in dat page cache");
nilfs_copy_page(&dfolio->page, &folio->page, 1);
@@ -275,6 +288,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
folio_unlock(dfolio);
folio_put(dfolio);
+unlock_folio:
folio_unlock(folio);
}
folio_batch_release(&fbatch);
diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
index 21ddcdd4d63e..bff6257ef83b 100644
--- a/fs/nilfs2/page.h
+++ b/fs/nilfs2/page.h
@@ -36,8 +36,8 @@ struct buffer_head *nilfs_grab_buffer(struct inode *, struct address_space *,
unsigned long, unsigned long);
void nilfs_forget_buffer(struct buffer_head *);
void nilfs_copy_buffer(struct buffer_head *, struct buffer_head *);
-int nilfs_page_buffers_clean(struct page *);
-void nilfs_page_bug(struct page *);
+bool nilfs_folio_buffers_clean(struct folio *);
+void nilfs_folio_bug(struct folio *);
int nilfs_copy_dirty_pages(struct address_space *, struct address_space *);
void nilfs_copy_back_pages(struct address_space *, struct address_space *);
@@ -49,8 +49,8 @@ unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
sector_t start_blk,
sector_t *blkoff);
-#define NILFS_PAGE_BUG(page, m, a...) \
- do { nilfs_page_bug(page); BUG(); } while (0)
+#define NILFS_FOLIO_BUG(folio, m, a...) \
+ do { nilfs_folio_bug(folio); BUG(); } while (0)
static inline struct buffer_head *
nilfs_page_get_nth_block(struct page *page, unsigned int count)
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index b1730b1d88ba..be6730482520 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -1744,17 +1744,18 @@ static int nilfs_segctor_write(struct nilfs_sc_info *sci,
return ret;
}
-static void nilfs_end_page_io(struct page *page, int err)
+static void nilfs_end_folio_io(struct folio *folio, int err)
{
- if (!page)
+ if (!folio)
return;
- if (buffer_nilfs_node(page_buffers(page)) && !PageWriteback(page)) {
+ if (buffer_nilfs_node(folio_buffers(folio)) &&
+ !folio_test_writeback(folio)) {
/*
* For b-tree node pages, this function may be called twice
* or more because they might be split in a segment.
*/
- if (PageDirty(page)) {
+ if (folio_test_dirty(folio)) {
/*
* For pages holding split b-tree node buffers, dirty
* flag on the buffers may be cleared discretely.
@@ -1762,24 +1763,31 @@ static void nilfs_end_page_io(struct page *page, int err)
* remaining buffers, and it must be cancelled if
* all the buffers get cleaned later.
*/
- lock_page(page);
- if (nilfs_page_buffers_clean(page))
- __nilfs_clear_page_dirty(page);
- unlock_page(page);
+ folio_lock(folio);
+ if (nilfs_folio_buffers_clean(folio))
+ __nilfs_clear_page_dirty(&folio->page);
+ folio_unlock(folio);
}
return;
}
if (!err) {
- if (!nilfs_page_buffers_clean(page))
- __set_page_dirty_nobuffers(page);
- ClearPageError(page);
+ if (!nilfs_folio_buffers_clean(folio))
+ filemap_dirty_folio(folio->mapping, folio);
+ folio_clear_error(folio);
} else {
- __set_page_dirty_nobuffers(page);
- SetPageError(page);
+ filemap_dirty_folio(folio->mapping, folio);
+ folio_set_error(folio);
}
- end_page_writeback(page);
+ folio_end_writeback(folio);
+}
+
+static void nilfs_end_page_io(struct page *page, int err)
+{
+ if (!page)
+ return;
+ nilfs_end_folio_io(page_folio(page), err);
}
static void nilfs_abort_logs(struct list_head *logs, int err)
@@ -2553,6 +2561,10 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
break;
nilfs_warn(sb, "error %d cleaning segments", err);
+
+ if (unlikely(err == -EROFS))
+ goto out_unlock;
+
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(sci->sc_interval);
}
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 05fdbbc63e1f..d1891dd4a510 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -450,6 +450,12 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
nilfs->ns_inode_size);
return -EINVAL;
}
+ if (NILFS_SR_BYTES(nilfs->ns_inode_size) > nilfs->ns_blocksize) {
+ nilfs_err(nilfs->ns_sb,
+ "too large inode size for super root: %d bytes",
+ nilfs->ns_inode_size);
+ return -EINVAL;
+ }
nilfs->ns_first_ino = le32_to_cpu(sbp->s_first_ino);
if (nilfs->ns_first_ino < NILFS_USER_INO) {
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index f4798d613dc2..f1b8dc8dcc8d 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -972,11 +972,13 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
{
struct fsnotify_group *group;
struct fsnotify_event *fsn_event;
+ unsigned int info_mode;
void __user *p;
int ret = -ENOTTY;
size_t send_len = 0;
group = file->private_data;
+ info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
p = (void __user *) arg;
@@ -984,7 +986,8 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case FIONREAD:
spin_lock(&group->notification_lock);
list_for_each_entry(fsn_event, &group->notification_list, list)
- send_len += FAN_EVENT_METADATA_LEN;
+ send_len += fanotify_event_len(info_mode,
+ FANOTIFY_E(fsn_event));
spin_unlock(&group->notification_lock);
ret = put_user(send_len, (int __user *) p);
break;
@@ -1186,16 +1189,18 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
__u32 mask, unsigned int fan_flags)
{
+ __u32 old_mask;
bool recalc;
spin_lock(&fsn_mark->lock);
- if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
+ if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS)) {
+ old_mask = fsn_mark->mask;
fsn_mark->mask |= mask;
- else
+ recalc = old_mask != fsn_mark->mask;
+ } else {
fsn_mark->ignore_mask |= mask;
-
- recalc = fsnotify_calc_mask(fsn_mark) &
- ~fsnotify_conn_mask(fsn_mark->connector);
+ recalc = true;
+ }
recalc |= fanotify_mark_update_flags(fsn_mark, fan_flags);
spin_unlock(&fsn_mark->lock);
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 804af9d6078b..b2c75477f549 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -540,7 +540,6 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
{
struct fsnotify_mark *fsn_mark;
struct inotify_inode_mark *i_mark;
- __u32 old_mask, new_mask;
int replace = !(arg & IN_MASK_ADD);
int create = (arg & IN_MASK_CREATE);
int ret;
@@ -556,27 +555,15 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
spin_lock(&fsn_mark->lock);
- old_mask = fsn_mark->mask;
if (replace) {
fsn_mark->mask = 0;
fsn_mark->flags &= ~INOTIFY_MARK_FLAGS;
}
fsn_mark->mask |= inotify_arg_to_mask(inode, arg);
fsn_mark->flags |= inotify_arg_to_flags(arg);
- new_mask = fsn_mark->mask;
spin_unlock(&fsn_mark->lock);
- if (old_mask != new_mask) {
- /* more bits in old than in new? */
- int dropped = (old_mask & ~new_mask);
- /* more bits in this fsn_mark than the inode's mask? */
- int do_inode = (new_mask & ~inode->i_fsnotify_mask);
-
- /* update the inode with this new fsn_mark */
- if (dropped || do_inode)
- fsnotify_recalc_mask(inode->i_fsnotify_marks);
-
- }
+ fsnotify_recalc_mask(inode->i_fsnotify_marks);
/* return the wd */
ret = i_mark->wd;
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index a590c5418613..37d5f0ff575f 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -790,7 +790,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
if (err)
goto out;
- attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id);
+ attr = mi_find_attr(ni, mi, NULL, type, name, name_len,
+ &le->id);
if (!attr) {
err = -EINVAL;
goto bad_inode;
@@ -1198,7 +1199,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
goto out;
}
- attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, &le->id);
+ attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0, &le->id);
if (!attr) {
err = -EINVAL;
goto out;
@@ -1834,7 +1835,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
goto out;
}
- attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0,
+ attr = mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0,
&le->id);
if (!attr) {
err = -EINVAL;
@@ -2079,8 +2080,8 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
}
/* Look for required attribute. */
- attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL,
- 0, &le->id);
+ attr = mi_find_attr(ni, mi, NULL, ATTR_DATA,
+ NULL, 0, &le->id);
if (!attr) {
err = -EINVAL;
goto out;
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index c265710d25b4..7952ef0c585b 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -75,7 +75,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni)
{
const struct ATTRIB *attr;
- attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
+ attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO)) :
NULL;
}
@@ -89,7 +89,7 @@ struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni)
{
const struct ATTRIB *attr;
- attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
+ attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO5)) :
NULL;
@@ -201,7 +201,8 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
*mi = &ni->mi;
/* Look for required attribute in primary record. */
- return mi_find_attr(&ni->mi, attr, type, name, name_len, NULL);
+ return mi_find_attr(ni, &ni->mi, attr, type, name, name_len,
+ NULL);
}
/* First look for list entry of required type. */
@@ -217,7 +218,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
return NULL;
/* Look for required attribute. */
- attr = mi_find_attr(m, NULL, type, name, name_len, &le->id);
+ attr = mi_find_attr(ni, m, NULL, type, name, name_len, &le->id);
if (!attr)
goto out;
@@ -259,7 +260,7 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr,
if (mi)
*mi = &ni->mi;
/* Enum attributes in primary record. */
- return mi_enum_attr(&ni->mi, attr);
+ return mi_enum_attr(ni, &ni->mi, attr);
}
/* Get next list entry. */
@@ -275,7 +276,7 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr,
*mi = mi2;
/* Find attribute in loaded record. */
- return rec_find_attr_le(mi2, le2);
+ return rec_find_attr_le(ni, mi2, le2);
}
/*
@@ -293,7 +294,8 @@ struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
if (!ni->attr_list.size) {
if (pmi)
*pmi = &ni->mi;
- return mi_find_attr(&ni->mi, NULL, type, name, name_len, NULL);
+ return mi_find_attr(ni, &ni->mi, NULL, type, name, name_len,
+ NULL);
}
le = al_find_ex(ni, NULL, type, name, name_len, NULL);
@@ -319,7 +321,7 @@ struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
if (pmi)
*pmi = mi;
- attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id);
+ attr = mi_find_attr(ni, mi, NULL, type, name, name_len, &le->id);
if (!attr)
return NULL;
@@ -400,7 +402,8 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
int diff;
if (base_only || type == ATTR_LIST || !ni->attr_list.size) {
- attr = mi_find_attr(&ni->mi, NULL, type, name, name_len, id);
+ attr = mi_find_attr(ni, &ni->mi, NULL, type, name, name_len,
+ id);
if (!attr)
return -ENOENT;
@@ -439,7 +442,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
al_remove_le(ni, le);
- attr = mi_find_attr(mi, NULL, type, name, name_len, id);
+ attr = mi_find_attr(ni, mi, NULL, type, name, name_len, id);
if (!attr)
return -ENOENT;
@@ -487,7 +490,7 @@ ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi,
name = le->name;
}
- attr = mi_insert_attr(mi, type, name, name_len, asize, name_off);
+ attr = mi_insert_attr(ni, mi, type, name, name_len, asize, name_off);
if (!attr) {
if (le_added)
al_remove_le(ni, le);
@@ -675,7 +678,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
if (err)
return err;
- attr_list = mi_find_attr(&ni->mi, NULL, ATTR_LIST, NULL, 0, NULL);
+ attr_list = mi_find_attr(ni, &ni->mi, NULL, ATTR_LIST, NULL, 0, NULL);
if (!attr_list)
return 0;
@@ -697,7 +700,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
if (!mi)
return 0;
- attr = mi_find_attr(mi, NULL, le->type, le_name(le),
+ attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le),
le->name_len, &le->id);
if (!attr)
return 0;
@@ -733,7 +736,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
goto out;
}
- attr = mi_find_attr(mi, NULL, le->type, le_name(le),
+ attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le),
le->name_len, &le->id);
if (!attr) {
/* Should never happened, 'cause already checked. */
@@ -742,7 +745,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
asize = le32_to_cpu(attr->size);
/* Insert into primary record. */
- attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le),
+ attr_ins = mi_insert_attr(ni, &ni->mi, le->type, le_name(le),
le->name_len, asize,
le16_to_cpu(attr->name_off));
if (!attr_ins) {
@@ -770,7 +773,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
if (!mi)
continue;
- attr = mi_find_attr(mi, NULL, le->type, le_name(le),
+ attr = mi_find_attr(ni, mi, NULL, le->type, le_name(le),
le->name_len, &le->id);
if (!attr)
continue;
@@ -818,10 +821,23 @@ int ni_create_attr_list(struct ntfs_inode *ni)
rs = sbi->record_size;
/*
- * Skip estimating exact memory requirement.
- * Looks like one record_size is always enough.
+ * Compute the exact size of the attribute list. Each attribute in the
+ * record yields one ATTR_LIST_ENTRY of le_size(name_len) bytes. The
+ * minimum on-disk attribute is SIZEOF_RESIDENT (0x18) bytes, but an
+ * unnamed one expands to le_size(0) (0x20) here, so a record crafted
+ * with many such attributes needs more than a single record_size; the
+ * previous fixed kzalloc(record_size) could therefore be overflowed by
+ * an attacker-controlled record.
*/
- le = kmalloc(al_aligned(rs), GFP_NOFS);
+ lsize = 0;
+ attr = NULL;
+ while ((attr = mi_enum_attr(ni, &ni->mi, attr)))
+ lsize += le_size(attr->name_len);
+
+ if (!lsize)
+ return -EINVAL;
+
+ le = kzalloc(al_aligned(lsize), GFP_NOFS);
if (!le)
return -ENOMEM;
@@ -831,9 +847,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
attr = NULL;
nb = 0;
free_b = 0;
- attr = NULL;
- for (; (attr = mi_enum_attr(&ni->mi, attr)); le = Add2Ptr(le, sz)) {
+ for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) {
sz = le_size(attr->name_len);
le->type = attr->type;
le->size = cpu_to_le16(sz);
@@ -888,7 +903,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
u32 asize = le32_to_cpu(b->size);
u16 name_off = le16_to_cpu(b->name_off);
- attr = mi_insert_attr(mi, b->type, Add2Ptr(b, name_off),
+ attr = mi_insert_attr(ni, mi, b->type, Add2Ptr(b, name_off),
b->name_len, asize, name_off);
if (!attr)
goto out;
@@ -911,7 +926,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
goto out;
}
- attr = mi_insert_attr(&ni->mi, ATTR_LIST, NULL, 0,
+ attr = mi_insert_attr(ni, &ni->mi, ATTR_LIST, NULL, 0,
lsize + SIZEOF_RESIDENT, SIZEOF_RESIDENT);
if (!attr)
goto out;
@@ -995,13 +1010,13 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
mi = rb_entry(node, struct mft_inode, node);
if (is_mft_data &&
- (mi_enum_attr(mi, NULL) ||
+ (mi_enum_attr(ni, mi, NULL) ||
vbo <= ((u64)mi->rno << sbi->record_bits))) {
/* We can't accept this record 'cause MFT's bootstrapping. */
continue;
}
if (is_mft &&
- mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, NULL)) {
+ mi_find_attr(ni, mi, NULL, ATTR_DATA, NULL, 0, NULL)) {
/*
* This child record already has a ATTR_DATA.
* So it can't accept any other records.
@@ -1010,7 +1025,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
}
if ((type != ATTR_NAME || name_len) &&
- mi_find_attr(mi, NULL, type, name, name_len, NULL)) {
+ mi_find_attr(ni, mi, NULL, type, name, name_len, NULL)) {
/* Only indexed attributes can share same record. */
continue;
}
@@ -1159,7 +1174,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
/* Estimate the result of moving all possible attributes away. */
attr = NULL;
- while ((attr = mi_enum_attr(&ni->mi, attr))) {
+ while ((attr = mi_enum_attr(ni, &ni->mi, attr))) {
if (attr->type == ATTR_STD)
continue;
if (attr->type == ATTR_LIST)
@@ -1177,7 +1192,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
attr = NULL;
for (;;) {
- attr = mi_enum_attr(&ni->mi, attr);
+ attr = mi_enum_attr(ni, &ni->mi, attr);
if (!attr) {
/* We should never be here 'cause we have already check this case. */
err = -EINVAL;
@@ -1261,7 +1276,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni)
for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) {
mi = rb_entry(node, struct mft_inode, node);
- attr = mi_enum_attr(mi, NULL);
+ attr = mi_enum_attr(ni, mi, NULL);
if (!attr) {
mft_min = mi->rno;
@@ -1282,7 +1297,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni)
ni_remove_mi(ni, mi_new);
}
- attr = mi_find_attr(&ni->mi, NULL, ATTR_DATA, NULL, 0, NULL);
+ attr = mi_find_attr(ni, &ni->mi, NULL, ATTR_DATA, NULL, 0, NULL);
if (!attr) {
err = -EINVAL;
goto out;
@@ -1399,7 +1414,7 @@ int ni_expand_list(struct ntfs_inode *ni)
continue;
/* Find attribute in primary record. */
- attr = rec_find_attr_le(&ni->mi, le);
+ attr = rec_find_attr_le(ni, &ni->mi, le);
if (!attr) {
err = -EINVAL;
goto out;
@@ -2707,6 +2722,15 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
err = unc_size;
else if (!unc_size || unc_size > frame_size)
err = -EINVAL;
+ else if (unc_size < frame_size) {
+ /*
+ * Partial decompress: zero the [unc_size, frame_size)
+ * tail. decompress_lznt() leaves it untouched, so
+ * without this the freshly vmapped pages would expose
+ * uninitialized kernel memory to userspace.
+ */
+ memset(frame_mem + unc_size, 0, frame_size - unc_size);
+ }
}
if (!err && valid_size < frame_vbo + frame_size) {
size_t ok = valid_size - frame_vbo;
@@ -3419,7 +3443,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
if (!mi->dirty)
continue;
- is_empty = !mi_enum_attr(mi, NULL);
+ is_empty = !mi_enum_attr(ni, mi, NULL);
if (is_empty)
clear_rec_inuse(mi->mrec);
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index ecb7f9694d70..c293e0988162 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -647,6 +647,14 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c)
return NULL;
}
+/*
+ * dp_range_ok - true if [j, j + count) fits in a page_lcns[cap] array.
+ */
+static inline bool dp_range_ok(size_t j, u32 count, u32 cap)
+{
+ return j < cap && count <= cap - j;
+}
+
/*
* find_dp - Search for a @vcn in Dirty Page Table.
*/
@@ -789,6 +797,20 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes)
return true;
}
+static bool check_dp_table(const struct RESTART_TABLE *dptbl)
+{
+ u32 rsize = le16_to_cpu(dptbl->size);
+ struct DIR_PAGE_ENTRY *dp = NULL;
+
+ while ((dp = enum_rstbl((struct RESTART_TABLE *)dptbl, dp))) {
+ if (struct_size(dp, page_lcns, le32_to_cpu(dp->lcns_follow)) >
+ rsize)
+ return false;
+ }
+
+ return true;
+}
+
/*
* free_rsttbl_idx - Free a previously allocated index a Restart Table.
*/
@@ -853,6 +875,9 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl,
u32 used = le16_to_cpu(tbl->used);
struct RESTART_TABLE *rt;
+ if (used + add > U16_MAX)
+ return NULL;
+
rt = init_rsttbl(esize, used + add);
if (!rt)
return NULL;
@@ -2274,7 +2299,15 @@ static int read_log_rec_buf(struct ntfs_log *log,
*/
for (;;) {
bool usa_error;
- u32 tail = log->page_size - off;
+ u32 tail;
+
+ /* off comes from the on-disk restart area; bound it. */
+ if (off > log->page_size) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ tail = log->page_size - off;
if (tail >= data_len)
tail = data_len;
@@ -4282,6 +4315,11 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
goto out;
}
+ if (!check_dp_table(rt)) {
+ err = -EINVAL;
+ goto out;
+ }
+
dptbl = kmemdup(rt, t32, GFP_NOFS);
if (!dptbl) {
err = -ENOMEM;
@@ -5076,6 +5114,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
/* Shorten length by any Lcns which were deleted. */
saved_len = dlen;
+ if (!dp_range_ok(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn),
+ le16_to_cpu(lrh->lcns_follow),
+ le32_to_cpu(dp->lcns_follow))) {
+ err = -EINVAL;
+ goto out;
+ }
+
for (i = le16_to_cpu(lrh->lcns_follow); i; i--) {
size_t j;
u32 alen, voff;
diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c
index a38547bd12bb..3909c95d9bed 100644
--- a/fs/ntfs3/fsntfs.c
+++ b/fs/ntfs3/fsntfs.c
@@ -2313,8 +2313,8 @@ int ntfs_reparse_init(struct ntfs_sb_info *sbi)
goto out;
}
- root_r = resident_data(attr);
- if (root_r->type != ATTR_ZERO ||
+ root_r = resident_data_ex(attr, sizeof(struct INDEX_ROOT));
+ if (!root_r || root_r->type != ATTR_ZERO ||
root_r->rule != NTFS_COLLATION_TYPE_UINTS) {
err = -EINVAL;
goto out;
@@ -2351,8 +2351,8 @@ int ntfs_objid_init(struct ntfs_sb_info *sbi)
goto out;
}
- root = resident_data(attr);
- if (root->type != ATTR_ZERO ||
+ root = resident_data_ex(attr, sizeof(struct INDEX_ROOT));
+ if (!root || root->type != ATTR_ZERO ||
root->rule != NTFS_COLLATION_TYPE_UINTS) {
err = -EINVAL;
goto out;
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 68c4fe1bf8a5..5e7536ad0b94 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -740,23 +740,24 @@ int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi);
void mi_put(struct mft_inode *mi);
int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno);
int mi_read(struct mft_inode *mi, bool is_mft);
-struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr);
-// TODO: id?
-struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr,
- enum ATTR_TYPE type, const __le16 *name,
- u8 name_len, const __le16 *id);
-static inline struct ATTRIB *rec_find_attr_le(struct mft_inode *rec,
+struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr);
+struct ATTRIB *mi_find_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr, enum ATTR_TYPE type,
+ const __le16 *name, u8 name_len, const __le16 *id);
+static inline struct ATTRIB *rec_find_attr_le(struct ntfs_inode *ni,
+ struct mft_inode *rec,
struct ATTR_LIST_ENTRY *le)
{
- return mi_find_attr(rec, NULL, le->type, le_name(le), le->name_len,
+ return mi_find_attr(ni, rec, NULL, le->type, le_name(le), le->name_len,
&le->id);
}
int mi_write(struct mft_inode *mi, int wait);
int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
__le16 flags, bool is_mft);
-struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
- const __le16 *name, u8 name_len, u32 asize,
- u16 name_off);
+struct ATTRIB *mi_insert_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ enum ATTR_TYPE type, const __le16 *name,
+ u8 name_len, u32 asize, u16 name_off);
bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
struct ATTRIB *attr);
diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c
index f810f0419d25..0f8ba76094fe 100644
--- a/fs/ntfs3/record.c
+++ b/fs/ntfs3/record.c
@@ -31,7 +31,7 @@ static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type,
*
* Return: Unused attribute id that is less than mrec->next_attr_id.
*/
-static __le16 mi_new_attt_id(struct mft_inode *mi)
+static __le16 mi_new_attt_id(struct ntfs_inode *ni, struct mft_inode *mi)
{
u16 free_id, max_id, t16;
struct MFT_REC *rec = mi->mrec;
@@ -52,7 +52,7 @@ static __le16 mi_new_attt_id(struct mft_inode *mi)
attr = NULL;
for (;;) {
- attr = mi_enum_attr(mi, attr);
+ attr = mi_enum_attr(ni, mi, attr);
if (!attr) {
rec->next_attr_id = cpu_to_le16(max_id + 1);
mi->dirty = true;
@@ -195,13 +195,14 @@ int mi_read(struct mft_inode *mi, bool is_mft)
* NOTE: mi->mrec - memory of size sbi->record_size
* here we sure that mi->mrec->total == sbi->record_size (see mi_read)
*/
-struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
+struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr)
{
const struct MFT_REC *rec = mi->mrec;
u32 used = le32_to_cpu(rec->used);
u32 t32, off, asize, prev_type;
u16 t16;
- u64 data_size, alloc_size, tot_size;
+ u64 svcn, evcn, data_size, alloc_size, tot_size;
if (!attr) {
u32 total = le32_to_cpu(rec->total);
@@ -209,11 +210,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
off = le16_to_cpu(rec->attr_off);
if (used > total)
- return NULL;
+ goto out;
if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 ||
- !IS_ALIGNED(off, 4)) {
- return NULL;
+ !IS_ALIGNED(off, 8)) {
+ goto out;
}
/* Skip non-resident records. */
@@ -236,11 +237,14 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
off += asize;
}
- /* Can we use the first field (attr->type). */
- /* NOTE: this code also checks attr->size availability. */
+ /*
+ * Can we use the first fields:
+ * attr->type,
+ * attr->size
+ */
if (off + 8 > used) {
static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8);
- return NULL;
+ goto out;
}
if (attr->type == ATTR_END) {
@@ -251,105 +255,144 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
/* 0x100 is last known attribute for now. */
t32 = le32_to_cpu(attr->type);
if (!t32 || (t32 & 0xf) || (t32 > 0x100))
- return NULL;
+ goto out;
/* attributes in record must be ordered by type */
if (t32 < prev_type)
- return NULL;
+ goto out;
asize = le32_to_cpu(attr->size);
+ if (!IS_ALIGNED(asize, 8))
+ goto out;
+
/* Check overflow and boundary. */
if (off + asize < off || off + asize > used)
- return NULL;
+ goto out;
+
+ /* Can we use the field attr->non_res. */
+ if (off + 9 > used)
+ goto out;
/* Check size of attribute. */
if (!attr->non_res) {
/* Check resident fields. */
if (asize < SIZEOF_RESIDENT)
- return NULL;
+ goto out;
t16 = le16_to_cpu(attr->res.data_off);
if (t16 > asize)
- return NULL;
+ goto out;
if (le32_to_cpu(attr->res.data_size) > asize - t16)
- return NULL;
+ goto out;
t32 = sizeof(short) * attr->name_len;
if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
- return NULL;
+ goto out;
return attr;
}
/* Check nonresident fields. */
if (attr->non_res != 1)
- return NULL;
+ goto out;
/* Can we use memory including attr->nres.valid_size? */
if (asize < SIZEOF_NONRESIDENT)
- return NULL;
+ goto out;
t16 = le16_to_cpu(attr->nres.run_off);
if (t16 > asize)
- return NULL;
+ goto out;
t32 = sizeof(short) * attr->name_len;
if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
- return NULL;
+ goto out;
- /* Check start/end vcn. */
- if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1)
- return NULL;
+ /*
+ * Check start/end vcn. svcn == 0 with evcn == -1 (U64_MAX) is the
+ * sentinel for an empty non-resident attribute (no allocated
+ * clusters) and must be accepted: "svcn > evcn + 1" tolerates it,
+ * since "(u64)-1 + 1" is 0 and "0 > 0" is false.
+ *
+ * For a non-empty attribute evcn is a cluster index and must lie
+ * within the volume (sbi->used.bitmap.nbits, set up in
+ * ntfs_init_from_boot() before any caller of mi_enum_attr() runs).
+ * Bounding evcn also prevents a malformed value close to U64_MAX
+ * from slipping through the near-wrap "evcn + 1" upper bound.
+ */
+ svcn = le64_to_cpu(attr->nres.svcn);
+ evcn = le64_to_cpu(attr->nres.evcn);
+ if (svcn > evcn + 1)
+ goto out;
+
+ if (is_attr_ext(attr)) {
+ /* sparsed/compressed attribute. */
+#ifdef CONFIG_NTFS3_64BIT_CLUSTER
+ /* No limits. */
+#else
+ /* Check evcn fits into 32 bits. */
+ if (evcn != U64_MAX && evcn >= (1ull << 32))
+ goto out;
+#endif
+ } else {
+ /* Check out of volume for normal attribute. */
+ if (evcn != U64_MAX && evcn >= mi->sbi->used.bitmap.nbits)
+ goto out;
+ }
data_size = le64_to_cpu(attr->nres.data_size);
if (le64_to_cpu(attr->nres.valid_size) > data_size)
- return NULL;
+ goto out;
alloc_size = le64_to_cpu(attr->nres.alloc_size);
if (data_size > alloc_size)
- return NULL;
+ goto out;
t32 = mi->sbi->cluster_mask;
if (alloc_size & t32)
- return NULL;
+ goto out;
if (!attr->nres.svcn && is_attr_ext(attr)) {
/* First segment of sparse/compressed attribute */
/* Can we use memory including attr->nres.total_size? */
if (asize < SIZEOF_NONRESIDENT_EX)
- return NULL;
+ goto out;
tot_size = le64_to_cpu(attr->nres.total_size);
if (tot_size & t32)
- return NULL;
+ goto out;
if (tot_size > alloc_size)
- return NULL;
+ goto out;
} else {
if (attr->nres.c_unit)
- return NULL;
+ goto out;
if (alloc_size > mi->sbi->volume.size)
- return NULL;
+ goto out;
}
return attr;
+
+out:
+ _ntfs_bad_inode(&ni->vfs_inode);
+ return NULL;
}
/*
* mi_find_attr - Find the attribute by type and name and id.
*/
-struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr,
- enum ATTR_TYPE type, const __le16 *name,
- u8 name_len, const __le16 *id)
+struct ATTRIB *mi_find_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ struct ATTRIB *attr, enum ATTR_TYPE type,
+ const __le16 *name, u8 name_len, const __le16 *id)
{
u32 type_in = le32_to_cpu(type);
u32 atype;
next_attr:
- attr = mi_enum_attr(mi, attr);
+ attr = mi_enum_attr(ni, mi, attr);
if (!attr)
return NULL;
@@ -457,9 +500,9 @@ int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
*
* Return: Not full constructed attribute or NULL if not possible to create.
*/
-struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
- const __le16 *name, u8 name_len, u32 asize,
- u16 name_off)
+struct ATTRIB *mi_insert_attr(struct ntfs_inode *ni, struct mft_inode *mi,
+ enum ATTR_TYPE type, const __le16 *name,
+ u8 name_len, u32 asize, u16 name_off)
{
size_t tail;
struct ATTRIB *attr;
@@ -478,7 +521,7 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
* at which we should insert it.
*/
attr = NULL;
- while ((attr = mi_enum_attr(mi, attr))) {
+ while ((attr = mi_enum_attr(ni, mi, attr))) {
int diff = compare_attr(attr, type, name, name_len, upcase);
if (diff < 0)
@@ -498,7 +541,7 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
tail = used - PtrOffset(rec, attr);
}
- id = mi_new_attt_id(mi);
+ id = mi_new_attt_id(ni, mi);
memmove(Add2Ptr(attr, asize), attr, tail);
memset(attr, 0, asize);
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index a4cc56df549a..935115a75ea1 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -57,6 +57,7 @@
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/nls.h>
+#include <linux/overflow.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
@@ -870,7 +871,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
struct ntfs_sb_info *sbi = sb->s_fs_info;
int err;
u32 mb, gb, boot_sector_size, sct_per_clst, record_size;
- u64 sectors, clusters, mlcn, mlcn2, dev_size0;
+ u64 sectors, clusters, mlcn, mlcn2, mft_pos, mft2_pos, dev_size0;
struct NTFS_BOOT *boot;
struct buffer_head *bh;
struct MFT_REC *rec;
@@ -939,7 +940,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
mlcn2 = le64_to_cpu(boot->mft2_clst);
sectors = le64_to_cpu(boot->sectors_per_volume);
- if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) {
+ /*
+ * Convert mlcn/mlcn2 to sector positions before comparing with
+ * 'sectors'. All three are u64 values that come from the boot
+ * sector, so use check_mul_overflow() to keep a wraparound from
+ * silently bypassing the comparison.
+ */
+ if (check_mul_overflow(mlcn, (u64)sct_per_clst, &mft_pos) ||
+ check_mul_overflow(mlcn2, (u64)sct_per_clst, &mft2_pos) ||
+ mft_pos >= sectors || mft2_pos >= sectors) {
ntfs_err(
sb,
"%s: start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index 1eace7c1b5bd..8b5162df8551 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -146,26 +146,29 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
for (off = 0; off < size; off += ea_size) {
const struct EA_FULL *ef = Add2Ptr(ea_p, off);
u32 bytes = size - off;
+ size_t need;
/* Check if we can use field ea->size. */
if (bytes < sizeof(ef->size))
goto out1;
+ /* Check if we can use fields ef->name_len and ef->elength. */
+ if (bytes < offsetof(struct EA_FULL, name))
+ goto out1;
+
+ /* Size needed to hold this record's name and value. */
+ need = struct_size(ef, name,
+ 1 + ef->name_len + le16_to_cpu(ef->elength));
+
if (ef->size) {
ea_size = le32_to_cpu(ef->size);
- if (ea_size > bytes)
+ /* ef->size must fit the list and cover the record. */
+ if (ea_size > bytes || ea_size < need)
goto out1;
continue;
}
- /* Check if we can use fields ef->name_len and ef->elength. */
- if (bytes < offsetof(struct EA_FULL, name))
- goto out1;
-
- ea_size = ALIGN(struct_size(ef, name,
- 1 + ef->name_len +
- le16_to_cpu(ef->elength)),
- 4);
+ ea_size = ALIGN(need, 4);
if (ea_size > bytes)
goto out1;
}
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 62464d194da3..095ce96c2484 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -109,8 +109,7 @@ static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)
return ocfs2_acl;
}
-static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode,
- int type,
+static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode, int type,
struct buffer_head *di_bh)
{
int name_index;
@@ -348,63 +347,105 @@ int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)
* Initialize the ACLs of a new inode. If parent directory has default ACL,
* then clone to new inode. Called from ocfs2_mknod.
*/
-int ocfs2_init_acl(handle_t *handle,
- struct inode *inode,
- struct inode *dir,
- struct buffer_head *di_bh,
- struct buffer_head *dir_bh,
- struct ocfs2_alloc_context *meta_ac,
- struct ocfs2_alloc_context *data_ac)
+void ocfs2_acl_init_release(struct ocfs2_acl_state *state)
+{
+ posix_acl_release(state->default_acl);
+ posix_acl_release(state->acl);
+ state->default_acl = NULL;
+ state->acl = NULL;
+}
+
+int ocfs2_acl_init_prepare(struct inode *inode, struct inode *dir,
+ struct buffer_head *dir_bh,
+ struct ocfs2_acl_state *state)
{
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
- struct posix_acl *acl = NULL;
- int ret = 0, ret2;
- umode_t mode;
-
- if (!S_ISLNK(inode->i_mode)) {
- if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
- down_read(&OCFS2_I(dir)->ip_xattr_sem);
- acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
- dir_bh);
- up_read(&OCFS2_I(dir)->ip_xattr_sem);
- if (IS_ERR(acl))
- return PTR_ERR(acl);
+ int ret = 0;
+
+ state->default_acl = NULL;
+ state->acl = NULL;
+ state->mode = inode->i_mode;
+
+ if (S_ISLNK(inode->i_mode))
+ return 0;
+
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ down_read(&OCFS2_I(dir)->ip_xattr_sem);
+ state->default_acl =
+ ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT, dir_bh);
+ up_read(&OCFS2_I(dir)->ip_xattr_sem);
+ if (IS_ERR(state->default_acl)) {
+ ret = PTR_ERR(state->default_acl);
+ state->default_acl = NULL;
+ return ret;
}
- if (!acl) {
- mode = inode->i_mode & ~current_umask();
- ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
- if (ret) {
- mlog_errno(ret);
+ if (state->default_acl) {
+ state->acl = posix_acl_dup(state->default_acl);
+ if (!state->acl) {
+ ret = -ENOMEM;
goto cleanup;
}
+ ret = __posix_acl_create(&state->acl, GFP_NOFS,
+ &state->mode);
+ if (ret < 0)
+ goto cleanup;
+ if (ret == 0) {
+ posix_acl_release(state->acl);
+ state->acl = NULL;
+ }
+ if (!S_ISDIR(inode->i_mode)) {
+ posix_acl_release(state->default_acl);
+ state->default_acl = NULL;
+ }
+ } else {
+ state->mode &= ~current_umask();
}
+ } else {
+ state->mode &= ~current_umask();
}
- if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) {
- if (S_ISDIR(inode->i_mode)) {
+
+ return 0;
+cleanup:
+ ocfs2_acl_init_release(state);
+ return ret;
+}
+
+int ocfs2_init_acl(handle_t *handle, struct inode *inode,
+ struct buffer_head *di_bh,
+ struct ocfs2_alloc_context *meta_ac,
+ struct ocfs2_alloc_context *data_ac,
+ struct ocfs2_acl_state *state)
+{
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ int ret = 0;
+
+ if (S_ISLNK(inode->i_mode))
+ return 0;
+
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ if (S_ISDIR(inode->i_mode) && state->default_acl) {
ret = ocfs2_set_acl(handle, inode, di_bh,
- ACL_TYPE_DEFAULT, acl,
- meta_ac, data_ac);
+ ACL_TYPE_DEFAULT,
+ state->default_acl, meta_ac,
+ data_ac);
if (ret)
- goto cleanup;
+ return ret;
}
- mode = inode->i_mode;
- ret = __posix_acl_create(&acl, GFP_NOFS, &mode);
- if (ret < 0)
- return ret;
+ }
- ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
- if (ret2) {
- mlog_errno(ret2);
- ret = ret2;
- goto cleanup;
- }
- if (ret > 0) {
- ret = ocfs2_set_acl(handle, inode,
- di_bh, ACL_TYPE_ACCESS,
- acl, meta_ac, data_ac);
+ ret = ocfs2_acl_set_mode(inode, di_bh, handle, state->mode);
+ if (ret) {
+ mlog_errno(ret);
+ return ret;
+ }
+
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ if (state->acl) {
+ ret = ocfs2_set_acl(handle, inode, di_bh,
+ ACL_TYPE_ACCESS, state->acl,
+ meta_ac, data_ac);
}
}
-cleanup:
- posix_acl_release(acl);
+
return ret;
}
diff --git a/fs/ocfs2/acl.h b/fs/ocfs2/acl.h
index 667c6f03fa60..a91f9ce278d6 100644
--- a/fs/ocfs2/acl.h
+++ b/fs/ocfs2/acl.h
@@ -20,9 +20,20 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu);
int ocfs2_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
struct posix_acl *acl, int type);
extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
-extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
- struct buffer_head *, struct buffer_head *,
- struct ocfs2_alloc_context *,
- struct ocfs2_alloc_context *);
+struct ocfs2_acl_state {
+ struct posix_acl *default_acl;
+ struct posix_acl *acl;
+ umode_t mode;
+};
+
+int ocfs2_acl_init_prepare(struct inode *inode, struct inode *dir,
+ struct buffer_head *dir_bh,
+ struct ocfs2_acl_state *state);
+void ocfs2_acl_init_release(struct ocfs2_acl_state *state);
+int ocfs2_init_acl(handle_t *handle, struct inode *inode,
+ struct buffer_head *di_bh,
+ struct ocfs2_alloc_context *meta_ac,
+ struct ocfs2_alloc_context *data_ac,
+ struct ocfs2_acl_state *state);
#endif /* OCFS2_ACL_H */
diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c
index 21472e3ed182..37c5d462f599 100644
--- a/fs/ocfs2/cluster/heartbeat.c
+++ b/fs/ocfs2/cluster/heartbeat.c
@@ -42,6 +42,14 @@ static DECLARE_RWSEM(o2hb_callback_sem);
* whenever any of the threads sees activity from the node in its region.
*/
static DEFINE_SPINLOCK(o2hb_live_lock);
+/*
+ * Serializes region pin/unpin dependency management (o2hb_dependent_users
+ * and the o2nm_depend_item()/o2nm_undepend_item() calls). o2hb_region_pin()
+ * has to drop o2hb_live_lock across the sleeping o2nm_depend_item(), so the
+ * spinlock alone can no longer keep pin and unpin mutually exclusive; this
+ * mutex, taken outside o2hb_live_lock, does.
+ */
+static DEFINE_MUTEX(o2hb_dependency_mutex);
static struct list_head o2hb_live_slots[O2NM_MAX_NODES];
static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)];
static LIST_HEAD(o2hb_node_events);
@@ -137,7 +145,7 @@ static unsigned int o2hb_dependent_users;
* In global heartbeat mode, we pin/unpin all o2hb regions. This solution
* works for both file system and userdlm domains.
*/
-static int o2hb_region_pin(const char *region_uuid);
+static int o2hb_region_pin(const char *region_uuid, bool from_callback);
static void o2hb_region_unpin(const char *region_uuid);
/* Only sets a new threshold if there are no active regions.
@@ -1448,13 +1456,38 @@ void o2hb_init(void)
o2hb_debug_init();
}
-/* if we're already in a callback then we're already serialized by the sem */
-static void o2hb_fill_node_map_from_callback(unsigned long *map,
- unsigned int bits)
+static void __o2hb_fill_node_map(unsigned long *map, unsigned int bits)
{
bitmap_copy(map, o2hb_live_node_bitmap, bits);
}
+void o2hb_callback_read_lock(void)
+{
+ down_read(&o2hb_callback_sem);
+}
+
+void o2hb_callback_read_unlock(void)
+{
+ up_read(&o2hb_callback_sem);
+}
+
+void o2hb_synchronize_callbacks(void)
+{
+ down_write(&o2hb_callback_sem);
+ up_write(&o2hb_callback_sem);
+}
+
+/*
+ * Callers must already hold o2hb_callback_sem for read or write so the copy
+ * stays serialized with callback delivery.
+ */
+void o2hb_fill_node_map_locked(unsigned long *map, unsigned int bits)
+{
+ spin_lock(&o2hb_live_lock);
+ __o2hb_fill_node_map(map, bits);
+ spin_unlock(&o2hb_live_lock);
+}
+
/*
* get a map of all nodes that are heartbeating in any regions
*/
@@ -1462,11 +1495,9 @@ void o2hb_fill_node_map(unsigned long *map, unsigned int bits)
{
/* callers want to serialize this map and callbacks so that they
* can trust that they don't miss nodes coming to the party */
- down_read(&o2hb_callback_sem);
- spin_lock(&o2hb_live_lock);
- o2hb_fill_node_map_from_callback(map, bits);
- spin_unlock(&o2hb_live_lock);
- up_read(&o2hb_callback_sem);
+ o2hb_callback_read_lock();
+ o2hb_fill_node_map_locked(map, bits);
+ o2hb_callback_read_unlock();
}
EXPORT_SYMBOL_GPL(o2hb_fill_node_map);
@@ -2107,6 +2138,7 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group,
* If global heartbeat active and there are dependent users,
* pin all regions if quorum region count <= CUT_OFF
*/
+ mutex_lock(&o2hb_dependency_mutex);
spin_lock(&o2hb_live_lock);
if (!o2hb_dependent_users)
@@ -2114,10 +2146,11 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group,
if (bitmap_weight(o2hb_quorum_region_bitmap,
O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF)
- o2hb_region_pin(NULL);
+ o2hb_region_pin(NULL, true);
unlock:
spin_unlock(&o2hb_live_lock);
+ mutex_unlock(&o2hb_dependency_mutex);
}
static ssize_t o2hb_heartbeat_group_dead_threshold_show(struct config_item *item,
@@ -2254,48 +2287,113 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);
* In local, we only pin the matching region. In global we pin all the active
* regions.
*/
-static int o2hb_region_pin(const char *region_uuid)
+static int o2hb_region_pin(const char *region_uuid, bool from_callback)
{
- int ret = 0, found = 0;
- struct o2hb_region *reg;
+ int ret = 0, found;
+ struct o2hb_region *reg, *pinned;
char *uuid;
assert_spin_locked(&o2hb_live_lock);
- list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) {
- if (reg->hr_item_dropped)
- continue;
+ do {
+ found = 0;
+ pinned = NULL;
- uuid = config_item_name(®->hr_item);
+ list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) {
+ if (reg->hr_item_dropped)
+ continue;
- /* local heartbeat */
- if (region_uuid) {
- if (strcmp(region_uuid, uuid))
+ uuid = config_item_name(®->hr_item);
+
+ /* local heartbeat */
+ if (region_uuid) {
+ if (strcmp(region_uuid, uuid))
+ continue;
+ found = 1;
+ }
+
+ if (reg->hr_item_pinned || reg->hr_item_dropped) {
+ if (found)
+ break;
continue;
- found = 1;
+ }
+
+ /*
+ * Found a region that needs pinning. Take a reference
+ * so it stays alive while we drop the lock below.
+ */
+ pinned = reg;
+ config_item_get(®->hr_item);
+ break;
}
- if (reg->hr_item_pinned || reg->hr_item_dropped)
- goto skip_pin;
+ if (!pinned)
+ break;
+
+ uuid = config_item_name(&pinned->hr_item);
+
+ /*
+ * o2nm_depend_item() -> configfs_depend_item() can sleep (it
+ * takes the configfs root inode rwsem), so it must not run
+ * under o2hb_live_lock. Drop the lock across it; @pinned is
+ * kept alive by the reference taken above. The region list may
+ * change while unlocked, so we rescan from the top afterwards.
+ */
+ spin_unlock(&o2hb_live_lock);
/* Ignore ENOENT only for local hb (userdlm domain) */
- ret = o2nm_depend_item(®->hr_item);
+ if (from_callback)
+ ret = o2nm_depend_item_unlocked(&pinned->hr_item);
+ else
+ ret = o2nm_depend_item(&pinned->hr_item);
+
+ spin_lock(&o2hb_live_lock);
if (!ret) {
- mlog(ML_CLUSTER, "Pin region %s\n", uuid);
- reg->hr_item_pinned = 1;
- } else {
- if (ret == -ENOENT && found)
- ret = 0;
- else {
- mlog(ML_ERROR, "Pin region %s fails with %d\n",
- uuid, ret);
+ /*
+ * o2hb_live_lock was dropped across o2nm_depend_item().
+ * o2hb_set_quorum_device() runs in the heartbeat thread
+ * without o2hb_dependency_mutex, so for global heartbeat
+ * it may have crossed O2HB_PIN_CUT_OFF and unpinned the
+ * regions while we slept. If that happened this pin is
+ * no longer wanted; undo it and stop rather than
+ * resurrecting it on the rescan below.
+ */
+ if (!region_uuid &&
+ bitmap_weight(o2hb_quorum_region_bitmap,
+ O2NM_MAX_REGIONS) > O2HB_PIN_CUT_OFF) {
+ o2nm_undepend_item(&pinned->hr_item);
+ spin_unlock(&o2hb_live_lock);
+ config_item_put(&pinned->hr_item);
+ spin_lock(&o2hb_live_lock);
break;
}
+ mlog(ML_CLUSTER, "Pin region %s\n", uuid);
+ pinned->hr_item_pinned = 1;
+ } else if (ret == -ENOENT && (found || !region_uuid)) {
+ /*
+ * For local hb (found): ignore ENOENT from userdlm
+ * domains as before. For global hb (!region_uuid):
+ * the region may have been detached from configfs
+ * while the lock was dropped — skip it and continue
+ * pinning the remaining regions.
+ */
+ ret = 0;
+ } else {
+ mlog(ML_ERROR, "Pin region %s fails with %d\n",
+ uuid, ret);
}
-skip_pin:
- if (found)
- break;
- }
+
+ /*
+ * config_item_put() may drop the last reference and run
+ * o2hb_region_release(), which also grabs o2hb_live_lock and
+ * can sleep, so it must happen with the lock released.
+ */
+ spin_unlock(&o2hb_live_lock);
+ config_item_put(&pinned->hr_item);
+ spin_lock(&o2hb_live_lock);
+
+ /* local hb pins a single matching region */
+ } while (!ret && !region_uuid);
return ret;
}
@@ -2340,12 +2438,13 @@ static int o2hb_region_inc_user(const char *region_uuid)
{
int ret = 0;
+ mutex_lock(&o2hb_dependency_mutex);
spin_lock(&o2hb_live_lock);
/* local heartbeat */
if (!o2hb_global_heartbeat_active()) {
- ret = o2hb_region_pin(region_uuid);
- goto unlock;
+ ret = o2hb_region_pin(region_uuid, false);
+ goto unlock;
}
/*
@@ -2357,16 +2456,23 @@ static int o2hb_region_inc_user(const char *region_uuid)
goto unlock;
if (bitmap_weight(o2hb_quorum_region_bitmap,
- O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF)
- ret = o2hb_region_pin(NULL);
+ O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF) {
+ ret = o2hb_region_pin(NULL, false);
+ if (ret) {
+ o2hb_region_unpin(NULL);
+ o2hb_dependent_users--;
+ }
+ }
unlock:
spin_unlock(&o2hb_live_lock);
+ mutex_unlock(&o2hb_dependency_mutex);
return ret;
}
static void o2hb_region_dec_user(const char *region_uuid)
{
+ mutex_lock(&o2hb_dependency_mutex);
spin_lock(&o2hb_live_lock);
/* local heartbeat */
@@ -2385,6 +2491,7 @@ static void o2hb_region_dec_user(const char *region_uuid)
unlock:
spin_unlock(&o2hb_live_lock);
+ mutex_unlock(&o2hb_dependency_mutex);
}
int o2hb_register_callback(const char *region_uuid,
@@ -2459,7 +2566,7 @@ int o2hb_check_node_heartbeating_no_sem(u8 node_num)
unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
spin_lock(&o2hb_live_lock);
- o2hb_fill_node_map_from_callback(testing_map, O2NM_MAX_NODES);
+ __o2hb_fill_node_map(testing_map, O2NM_MAX_NODES);
spin_unlock(&o2hb_live_lock);
if (!test_bit(node_num, testing_map)) {
mlog(ML_HEARTBEAT,
@@ -2476,7 +2583,7 @@ int o2hb_check_node_heartbeating_from_callback(u8 node_num)
{
unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
- o2hb_fill_node_map_from_callback(testing_map, O2NM_MAX_NODES);
+ o2hb_fill_node_map_locked(testing_map, O2NM_MAX_NODES);
if (!test_bit(node_num, testing_map)) {
mlog(ML_HEARTBEAT,
"node (%u) does not have heartbeating enabled.\n",
diff --git a/fs/ocfs2/cluster/heartbeat.h b/fs/ocfs2/cluster/heartbeat.h
index 8ef8c1b9eeb7..2ca2b657583c 100644
--- a/fs/ocfs2/cluster/heartbeat.h
+++ b/fs/ocfs2/cluster/heartbeat.h
@@ -58,6 +58,11 @@ int o2hb_register_callback(const char *region_uuid,
struct o2hb_callback_func *hc);
void o2hb_unregister_callback(const char *region_uuid,
struct o2hb_callback_func *hc);
+void o2hb_callback_read_lock(void);
+void o2hb_callback_read_unlock(void);
+void o2hb_synchronize_callbacks(void);
+void o2hb_fill_node_map_locked(unsigned long *map,
+ unsigned int bits);
void o2hb_fill_node_map(unsigned long *map,
unsigned int bits);
void o2hb_exit(void);
diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c
index 2f61d39e4e50..0edfd21be80e 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -325,6 +325,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
struct o2nm_node *node = to_o2nm_node(item);
struct o2nm_cluster *cluster;
unsigned long tmp;
+ bool starting = false;
char *p = (char *)page;
ssize_t ret;
@@ -361,6 +362,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
ret = o2net_start_listening(node);
if (ret)
goto out;
+ starting = true;
}
if (!tmp && cluster->cl_has_local &&
@@ -373,6 +375,8 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
if (node->nd_local) {
cluster->cl_has_local = tmp;
cluster->cl_local_node = node->nd_num;
+ if (starting)
+ o2net_complete_start_listening(node);
}
ret = count;
@@ -776,6 +780,12 @@ int o2nm_depend_item(struct config_item *item)
return configfs_depend_item(&o2nm_cluster_group.cs_subsys, item);
}
+int o2nm_depend_item_unlocked(struct config_item *item)
+{
+ return configfs_depend_item_unlocked(&o2nm_cluster_group.cs_subsys,
+ item);
+}
+
void o2nm_undepend_item(struct config_item *item)
{
configfs_undepend_item(item);
diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h
index 3490e77a952d..2f72f56996bd 100644
--- a/fs/ocfs2/cluster/nodemanager.h
+++ b/fs/ocfs2/cluster/nodemanager.h
@@ -64,6 +64,7 @@ void o2nm_node_get(struct o2nm_node *node);
void o2nm_node_put(struct o2nm_node *node);
int o2nm_depend_item(struct config_item *item);
+int o2nm_depend_item_unlocked(struct config_item *item);
void o2nm_undepend_item(struct config_item *item);
int o2nm_depend_this_node(void);
void o2nm_undepend_this_node(void);
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index 960080753d3b..2008bd847986 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -105,6 +105,8 @@ static struct socket *o2net_listen_sock;
* destroying the work queue.
*/
static struct workqueue_struct *o2net_wq;
+/* Heartbeat callbacks stay registered across local-node off/on. */
+static bool o2net_listening;
static struct work_struct o2net_listen_work;
static struct o2hb_callback_func o2net_hb_up, o2net_hb_down;
@@ -1691,6 +1693,19 @@ static void o2net_still_up(struct work_struct *work)
/* ------------------------------------------------------------ */
+static void o2net_hb_node_up(struct o2net_node *nn)
+{
+ /* ensure an immediate connect attempt */
+ nn->nn_last_connect_attempt = jiffies -
+ (msecs_to_jiffies(o2net_reconnect_delay()) + 1);
+
+ spin_lock(&nn->nn_lock);
+ atomic_set(&nn->nn_timeout, 0);
+ if (nn->nn_persistent_error)
+ o2net_set_nn_state(nn, NULL, 0, 0);
+ spin_unlock(&nn->nn_lock);
+}
+
void o2net_disconnect_node(struct o2nm_node *node)
{
struct o2net_node *nn = o2net_nn_from_num(node->nd_num);
@@ -1712,41 +1727,39 @@ void o2net_disconnect_node(struct o2nm_node *node)
static void o2net_hb_node_down_cb(struct o2nm_node *node, int node_num,
void *data)
{
+ u8 this_node;
+
o2quo_hb_down(node_num);
if (!node)
- return;
+ goto out;
- if (node_num != o2nm_this_node())
+ this_node = o2nm_this_node();
+ if (!READ_ONCE(o2net_listening) || this_node == O2NM_MAX_NODES)
+ goto out;
+
+ if (node_num != this_node)
o2net_disconnect_node(node);
+out:
BUG_ON(atomic_read(&o2net_connected_peers) < 0);
}
static void o2net_hb_node_up_cb(struct o2nm_node *node, int node_num,
void *data)
{
- struct o2net_node *nn = o2net_nn_from_num(node_num);
+ u8 this_node;
o2quo_hb_up(node_num);
BUG_ON(!node);
- /* ensure an immediate connect attempt */
- nn->nn_last_connect_attempt = jiffies -
- (msecs_to_jiffies(o2net_reconnect_delay()) + 1);
+ this_node = o2nm_this_node();
+ if (!READ_ONCE(o2net_listening) || this_node == O2NM_MAX_NODES)
+ return;
- if (node_num != o2nm_this_node()) {
- /* believe it or not, accept and node heartbeating testing
- * can succeed for this node before we got here.. so
- * only use set_nn_state to clear the persistent error
- * if that hasn't already happened */
- spin_lock(&nn->nn_lock);
- atomic_set(&nn->nn_timeout, 0);
- if (nn->nn_persistent_error)
- o2net_set_nn_state(nn, NULL, 0, 0);
- spin_unlock(&nn->nn_lock);
- }
+ if (node_num != this_node)
+ o2net_hb_node_up(o2net_nn_from_num(node_num));
}
void o2net_unregister_hb_callbacks(void)
@@ -1755,6 +1768,37 @@ void o2net_unregister_hb_callbacks(void)
o2hb_unregister_callback(NULL, &o2net_hb_down);
}
+/*
+ * Delay heartbeat-driven network work until the local node is fully published
+ * through o2nm_this_node(), then replay the nodes that are already live while
+ * callback delivery stays blocked.
+ */
+void o2net_complete_start_listening(struct o2nm_node *node)
+{
+ unsigned long live_nodes[BITS_TO_LONGS(O2NM_MAX_NODES)];
+ unsigned long node_num;
+ u8 local_node;
+
+ local_node = o2nm_this_node();
+ if (WARN_ON_ONCE(local_node == O2NM_MAX_NODES))
+ return;
+ if (WARN_ON_ONCE(local_node != node->nd_num))
+ return;
+ if (WARN_ON_ONCE(!o2net_wq))
+ return;
+
+ o2hb_callback_read_lock();
+ WRITE_ONCE(o2net_listening, true);
+ o2hb_fill_node_map_locked(live_nodes, O2NM_MAX_NODES);
+ for_each_set_bit(node_num, live_nodes, O2NM_MAX_NODES) {
+ if (node_num == local_node)
+ continue;
+
+ o2net_hb_node_up(o2net_nn_from_num(node_num));
+ }
+ o2hb_callback_read_unlock();
+}
+
int o2net_register_hb_callbacks(void)
{
int ret;
@@ -2030,6 +2074,8 @@ int o2net_start_listening(struct o2nm_node *node)
{
int ret = 0;
+ if (WARN_ON_ONCE(READ_ONCE(o2net_listening)))
+ return -EBUSY;
BUG_ON(o2net_wq != NULL);
BUG_ON(o2net_listen_sock != NULL);
@@ -2061,6 +2107,9 @@ void o2net_stop_listening(struct o2nm_node *node)
BUG_ON(o2net_wq == NULL);
BUG_ON(o2net_listen_sock == NULL);
+ WRITE_ONCE(o2net_listening, false);
+ o2hb_synchronize_callbacks();
+
/* stop the listening socket from generating work */
write_lock_bh(&sock->sk->sk_callback_lock);
sock->sk->sk_data_ready = sock->sk->sk_user_data;
diff --git a/fs/ocfs2/cluster/tcp.h b/fs/ocfs2/cluster/tcp.h
index a75b551d31c7..2e86d42b5faf 100644
--- a/fs/ocfs2/cluster/tcp.h
+++ b/fs/ocfs2/cluster/tcp.h
@@ -96,6 +96,7 @@ struct o2nm_node;
int o2net_register_hb_callbacks(void);
void o2net_unregister_hb_callbacks(void);
int o2net_start_listening(struct o2nm_node *node);
+void o2net_complete_start_listening(struct o2nm_node *node);
void o2net_stop_listening(struct o2nm_node *node);
void o2net_disconnect_node(struct o2nm_node *node);
int o2net_num_connected_peers(void);
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index da1ab3282c18..49eb03107caa 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -1883,7 +1883,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode,
i += le16_to_cpu(de->rec_len);
}
offset = i;
- ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
+ ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1))
| offset;
*f_version = inode_query_iversion(inode);
}
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index d610da8e2f24..d84e816c70c5 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -3112,6 +3112,12 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data,
name = migrate->name;
namelen = migrate->namelen;
+ if (namelen > DLM_LOCKID_NAME_MAX) {
+ mlog(ML_ERROR, "%s: invalid name length %u in migrate request\n",
+ dlm->name, namelen);
+ ret = -EINVAL;
+ goto leave;
+ }
hash = dlm_lockid_hash(name, namelen);
/* preallocate.. if this fails, abort */
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 50da8af988c1..739da8bd7dbf 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1359,6 +1359,15 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
if (!dlm_grab(dlm))
return -EINVAL;
+ if (mres->lockname_len > DLM_LOCKID_NAME_MAX ||
+ mres->num_locks > DLM_MAX_MIGRATABLE_LOCKS ||
+ be16_to_cpu(msg->data_len) < struct_size(mres, ml, mres->num_locks)) {
+ mlog(ML_ERROR, "%s: invalid lockres migration message from %u\n",
+ dlm->name, mres->master);
+ dlm_put(dlm);
+ return -EINVAL;
+ }
+
if (!dlm_joined(dlm)) {
mlog(ML_ERROR, "Domain %s not joined! "
"lockres %.*s, master %u\n",
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 4e6d8a3f727d..113102009431 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -252,6 +252,7 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
sigset_t oldset;
int did_block_signals = 0;
struct ocfs2_dentry_lock *dl = NULL;
+ struct ocfs2_acl_state acl_state = { 0 };
trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name,
(unsigned long long)OCFS2_I(dir)->ip_blkno,
@@ -326,10 +327,14 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
}
}
+ status = ocfs2_acl_init_prepare(inode, dir, parent_fe_bh, &acl_state);
+ if (status < 0)
+ goto leave;
+
/* calculate meta data/clusters for setting security and acl xattr */
- status = ocfs2_calc_xattr_init(dir, parent_fe_bh, mode,
- &si, &want_clusters,
- &xattr_credits, &want_meta);
+ status = ocfs2_calc_xattr_init(dir, mode, &si, &want_clusters,
+ &xattr_credits, &want_meta,
+ &acl_state);
if (status < 0) {
mlog_errno(status);
goto leave;
@@ -407,8 +412,8 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
inc_nlink(dir);
}
- status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh,
- meta_ac, data_ac);
+ status = ocfs2_init_acl(handle, inode, new_fe_bh, meta_ac, data_ac,
+ &acl_state);
if (status < 0) {
mlog_errno(status);
@@ -473,6 +478,8 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
brelse(parent_fe_bh);
kfree(si.value);
+ ocfs2_acl_init_release(&acl_state);
+
ocfs2_free_dir_lookup_result(&lookup);
if (inode_ac)
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index c71b79b5fb9b..b821f65035e8 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -116,6 +116,33 @@ static int ocfs2_validate_refcount_block(struct super_block *sb,
le32_to_cpu(rb->rf_fs_generation));
goto out;
}
+
+ /*
+ * rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when
+ * this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL);
+ * in that case the same union bytes hold an extent list (rf_list)
+ * instead, which is validated by ocfs2_validate_extent_block().
+ */
+ if (!(le32_to_cpu(rb->rf_flags) & OCFS2_REFCOUNT_TREE_FL)) {
+ if (le16_to_cpu(rb->rf_records.rl_count) !=
+ ocfs2_refcount_recs_per_rb(sb)) {
+ rc = ocfs2_error(sb,
+ "Refcount block #%llu has an invalid rl_count of %u\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(rb->rf_records.rl_count));
+ goto out;
+ }
+
+ if (le16_to_cpu(rb->rf_records.rl_used) >
+ le16_to_cpu(rb->rf_records.rl_count)) {
+ rc = ocfs2_error(sb,
+ "Refcount block #%llu has an invalid rl_used of %u (rl_count %u)\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(rb->rf_records.rl_used),
+ le16_to_cpu(rb->rf_records.rl_count));
+ goto out;
+ }
+ }
out:
return rc;
}
@@ -3365,10 +3392,9 @@ static int ocfs2_replace_cow(struct ocfs2_cow_context *context)
cow_start += num_clusters;
}
- if (ocfs2_dealloc_has_cluster(&context->dealloc)) {
+ if (ocfs2_dealloc_has_cluster(&context->dealloc))
ocfs2_schedule_truncate_log_flush(osb, 1);
- ocfs2_run_deallocs(osb, &context->dealloc);
- }
+ ocfs2_run_deallocs(osb, &context->dealloc);
return ret;
}
@@ -3851,10 +3877,9 @@ static int ocfs2_attach_refcount_tree(struct inode *inode,
ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
brelse(ref_root_bh);
- if (!ret && ocfs2_dealloc_has_cluster(&dealloc)) {
+ if (!ret && ocfs2_dealloc_has_cluster(&dealloc))
ocfs2_schedule_truncate_log_flush(osb, 1);
- ocfs2_run_deallocs(osb, &dealloc);
- }
+ ocfs2_run_deallocs(osb, &dealloc);
out:
/*
* Empty the extent map so that we may get the right extent
@@ -4140,10 +4165,9 @@ static int ocfs2_create_reflink_node(struct inode *s_inode,
ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
brelse(ref_root_bh);
out:
- if (ocfs2_dealloc_has_cluster(&dealloc)) {
+ if (ocfs2_dealloc_has_cluster(&dealloc))
ocfs2_schedule_truncate_log_flush(osb, 1);
- ocfs2_run_deallocs(osb, &dealloc);
- }
+ ocfs2_run_deallocs(osb, &dealloc);
return ret;
}
@@ -4696,10 +4720,9 @@ loff_t ocfs2_reflink_remap_blocks(struct inode *s_inode,
}
out:
- if (ocfs2_dealloc_has_cluster(&dealloc)) {
+ if (ocfs2_dealloc_has_cluster(&dealloc))
ocfs2_schedule_truncate_log_flush(osb, 1);
- ocfs2_run_deallocs(osb, &dealloc);
- }
+ ocfs2_run_deallocs(osb, &dealloc);
return ret;
}
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 186183b90fa4..3023b33afef4 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -607,13 +607,10 @@ int ocfs2_calc_security_init(struct inode *dir,
return ret;
}
-int ocfs2_calc_xattr_init(struct inode *dir,
- struct buffer_head *dir_bh,
- umode_t mode,
+int ocfs2_calc_xattr_init(struct inode *dir, umode_t mode,
struct ocfs2_security_xattr_info *si,
- int *want_clusters,
- int *xattr_credits,
- int *want_meta)
+ int *want_clusters, int *xattr_credits,
+ int *want_meta, struct ocfs2_acl_state *acl_state)
{
int ret = 0;
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
@@ -624,19 +621,15 @@ int ocfs2_calc_xattr_init(struct inode *dir,
si->value_len);
if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
- down_read(&OCFS2_I(dir)->ip_xattr_sem);
- acl_len = ocfs2_xattr_get_nolock(dir, dir_bh,
- OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT,
- "", NULL, 0);
- up_read(&OCFS2_I(dir)->ip_xattr_sem);
- if (acl_len > 0) {
- a_size = ocfs2_xattr_entry_real_size(0, acl_len);
- if (S_ISDIR(mode))
- a_size <<= 1;
- } else if (acl_len != 0 && acl_len != -ENODATA) {
- ret = acl_len;
- mlog_errno(ret);
- return ret;
+ if (acl_state->default_acl && S_ISDIR(mode)) {
+ acl_len = acl_state->default_acl->a_count *
+ sizeof(struct ocfs2_acl_entry);
+ a_size += ocfs2_xattr_entry_real_size(0, acl_len);
+ }
+ if (acl_state->acl) {
+ acl_len = acl_state->acl->a_count *
+ sizeof(struct ocfs2_acl_entry);
+ a_size += ocfs2_xattr_entry_real_size(0, acl_len);
}
}
@@ -679,14 +672,33 @@ int ocfs2_calc_xattr_init(struct inode *dir,
new_clusters);
*want_clusters += new_clusters;
}
- if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL &&
- acl_len > OCFS2_XATTR_INLINE_SIZE) {
- /* for directory, it has DEFAULT and ACCESS two types of acls */
- new_clusters = (S_ISDIR(mode) ? 2 : 1) *
- ocfs2_clusters_for_bytes(dir->i_sb, acl_len);
- *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb,
- new_clusters);
- *want_clusters += new_clusters;
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ if (acl_state->default_acl && S_ISDIR(mode)) {
+ acl_len = acl_state->default_acl->a_count *
+ sizeof(struct ocfs2_acl_entry);
+ if (acl_len > OCFS2_XATTR_INLINE_SIZE) {
+ new_clusters =
+ ocfs2_clusters_for_bytes(dir->i_sb,
+ acl_len);
+ *xattr_credits +=
+ ocfs2_clusters_to_blocks(dir->i_sb,
+ new_clusters);
+ *want_clusters += new_clusters;
+ }
+ }
+ if (acl_state->acl) {
+ acl_len = acl_state->acl->a_count *
+ sizeof(struct ocfs2_acl_entry);
+ if (acl_len > OCFS2_XATTR_INLINE_SIZE) {
+ new_clusters =
+ ocfs2_clusters_for_bytes(dir->i_sb,
+ acl_len);
+ *xattr_credits +=
+ ocfs2_clusters_to_blocks(dir->i_sb,
+ new_clusters);
+ *want_clusters += new_clusters;
+ }
+ }
}
return ret;
@@ -3445,9 +3457,10 @@ static int __ocfs2_xattr_set_handle(struct inode *inode,
}
/*
- * This function only called duing creating inode
- * for init security/acl xattrs of the new inode.
- * All transanction credits have been reserved in mknod.
+ * This helper is only for setting initial ACL or security xattrs on an inode
+ * that is still unpublished, unhashed, and unattached to a dentry.
+ * Ordinary xattr updates must use ocfs2_xattr_set().
+ * All transaction credits have been reserved in mknod or symlink callers.
*/
int ocfs2_xattr_set_handle(handle_t *handle,
struct inode *inode,
@@ -3504,8 +3517,6 @@ int ocfs2_xattr_set_handle(handle_t *handle,
xis.inode_bh = xbs.inode_bh = di_bh;
di = (struct ocfs2_dinode *)di_bh->b_data;
- down_write(&OCFS2_I(inode)->ip_xattr_sem);
-
ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis);
if (ret)
goto cleanup;
@@ -3518,7 +3529,6 @@ int ocfs2_xattr_set_handle(handle_t *handle,
ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt);
cleanup:
- up_write(&OCFS2_I(inode)->ip_xattr_sem);
brelse(xbs.xattr_bh);
ocfs2_xattr_bucket_free(xbs.bucket);
@@ -7196,10 +7206,9 @@ int ocfs2_reflink_xattrs(struct inode *old_inode,
ref_tree, 1);
brelse(ref_root_bh);
- if (ocfs2_dealloc_has_cluster(&dealloc)) {
+ if (ocfs2_dealloc_has_cluster(&dealloc))
ocfs2_schedule_truncate_log_flush(OCFS2_SB(old_inode->i_sb), 1);
- ocfs2_run_deallocs(OCFS2_SB(old_inode->i_sb), &dealloc);
- }
+ ocfs2_run_deallocs(OCFS2_SB(old_inode->i_sb), &dealloc);
out:
return ret;
@@ -7218,6 +7227,7 @@ int ocfs2_init_security_and_acl(struct inode *dir,
{
int ret = 0;
struct buffer_head *dir_bh = NULL;
+ struct ocfs2_acl_state acl_state = { 0 };
ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
if (ret) {
@@ -7230,10 +7240,17 @@ int ocfs2_init_security_and_acl(struct inode *dir,
mlog_errno(ret);
goto leave;
}
- ret = ocfs2_init_acl(NULL, inode, dir, NULL, dir_bh, NULL, NULL);
+
+ ret = ocfs2_acl_init_prepare(inode, dir, dir_bh, &acl_state);
+ if (ret)
+ goto unlock;
+
+ ret = ocfs2_init_acl(NULL, inode, NULL, NULL, NULL, &acl_state);
if (ret)
mlog_errno(ret);
+unlock:
+ ocfs2_acl_init_release(&acl_state);
ocfs2_inode_unlock(dir, 0);
brelse(dir_bh);
leave:
diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h
index 00308b57f64f..5901f2095bc5 100644
--- a/fs/ocfs2/xattr.h
+++ b/fs/ocfs2/xattr.h
@@ -55,9 +55,12 @@ int ocfs2_init_security_set(handle_t *, struct inode *,
int ocfs2_calc_security_init(struct inode *,
struct ocfs2_security_xattr_info *,
int *, int *, struct ocfs2_alloc_context **);
-int ocfs2_calc_xattr_init(struct inode *, struct buffer_head *,
- umode_t, struct ocfs2_security_xattr_info *,
- int *, int *, int *);
+
+struct ocfs2_acl_state;
+int ocfs2_calc_xattr_init(struct inode *dir, umode_t mode,
+ struct ocfs2_security_xattr_info *si,
+ int *want_clusters, int *xattr_credits,
+ int *want_meta, struct ocfs2_acl_state *acl_state);
/*
* xattrs can live inside an inode, as part of an external xattr block,
diff --git a/fs/orangefs/devorangefs-req.c b/fs/orangefs/devorangefs-req.c
index 33ee8cb32f83..e5c60da7e677 100644
--- a/fs/orangefs/devorangefs-req.c
+++ b/fs/orangefs/devorangefs-req.c
@@ -474,6 +474,7 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb,
op->downcall.trailer_size, iter)) {
gossip_err("%s: failed to copy trailer.\n", __func__);
vfree(op->downcall.trailer_buf);
+ op->downcall.trailer_buf = NULL;
goto Efault;
}
diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c
index cd4bfd92ebd6..f17c562cb790 100644
--- a/fs/orangefs/orangefs-debugfs.c
+++ b/fs/orangefs/orangefs-debugfs.c
@@ -529,6 +529,7 @@ static int orangefs_prepare_cdm_array(char *debug_array_string)
cds_delimiter = strchr(cds_head, '\n');
*cds_delimiter = '\0';
+ cds_head = skip_spaces(cds_head);
keyword_len = strcspn(cds_head, " ");
cdm_array[i].keyword = kzalloc(keyword_len + 1, GFP_KERNEL);
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 50ad8246ed18..00d35e01a224 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -674,7 +674,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&server->smb2slowcmd[i], 0);
server->time_per_cmd[i] = 0;
server->slowest_cmd[i] = 0;
- server->fastest_cmd[0] = 0;
+ server->fastest_cmd[i] = 0;
}
#endif /* CONFIG_CIFS_STATS2 */
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index a2887b978dc8..a1c5ef3b2f1e 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1323,8 +1323,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
*/
lock_two_nondirectories(target_inode, src_inode);
- if (len == 0)
- len = src_inode->i_size - off;
+ if (len == 0) {
+ loff_t src_size = i_size_read(src_inode);
+
+ if (off > src_size) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+ len = src_size - off;
+ if (!len) {
+ rc = 0;
+ goto unlock;
+ }
+ }
cifs_dbg(FYI, "clone range\n");
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 433f546055b9..70ed4d7e4f37 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -122,6 +122,8 @@ static inline void free_tgts(struct cache_entry *ce)
kfree(t->name);
kfree(t);
}
+
+ WRITE_ONCE(ce->tgthint, NULL);
}
static inline void flush_cache_ent(struct cache_entry *ce)
@@ -871,13 +873,22 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
goto out_free_path;
}
- if (ref)
- rc = setup_referral(path, ce, ref, get_tgt_name(ce));
- else
+ if (ref) {
+ char *target = get_tgt_name(ce);
+
+ if (IS_ERR(target)) {
+ rc = PTR_ERR(target);
+ goto out_unlock;
+ }
+ rc = setup_referral(path, ce, ref, target);
+ } else {
rc = 0;
+ }
+
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
+out_unlock:
up_read(&htable_rw_lock);
out_free_path:
@@ -917,10 +928,17 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
goto out_unlock;
}
- if (ref)
- rc = setup_referral(path, ce, ref, get_tgt_name(ce));
- else
+ if (ref) {
+ char *target = get_tgt_name(ce);
+
+ if (IS_ERR(target)) {
+ rc = PTR_ERR(target);
+ goto out_unlock;
+ }
+ rc = setup_referral(path, ce, ref, target);
+ } else {
rc = 0;
+ }
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
@@ -961,7 +979,8 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
t = READ_ONCE(ce->tgthint);
- if (unlikely(!strcasecmp(it->it_name, t->name)))
+ /* Check 't' in case ce->tgthint was cleared by free_tgts() */
+ if (t && unlikely(!strcasecmp(it->it_name, t->name)))
goto out_unlock;
list_for_each_entry(t, &ce->tlist, list) {
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index c80b291a14a5..6acadfcbadc8 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -747,7 +747,7 @@ static int cifs_query_path_info(const unsigned int xid,
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
- SMB2_WSL_XATTR_MODE_SIZE, 4);
+ SMB2_WSL_XATTR_DEV_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXDEV has wrong size. */
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index 576c72691e37..134155669a59 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -48,7 +48,10 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
- len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
+ len = le32_to_cpu(p->ErrorDataLength);
+ if (len > end - ((u8 *)p + sizeof(*p)))
+ return ERR_PTR(-EINVAL);
+ len = ALIGN(len, 8);
if (len > end - ((u8 *)p + sizeof(*p)))
return ERR_PTR(-EINVAL);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 7f26e6fdd6ab..8bb739d150c0 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4452,8 +4452,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
if (rc)
return rc;
- if (server == NULL)
- return -ECONNABORTED;
+ if (!server) {
+ rc = -ECONNABORTED;
+ goto free_req;
+ }
shdr = &req->hdr;
shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
@@ -4482,8 +4484,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->iter,
true, need_invalidate);
- if (!rdata->mr)
- return -EAGAIN;
+ if (!rdata->mr) {
+ rc = -EAGAIN;
+ goto free_req;
+ }
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate)
@@ -4526,6 +4530,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
*buf = req;
return rc;
+
+free_req:
+ cifs_small_buf_release(req);
+ return rc;
}
static void
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index bb8332caa9a7..e5b77d875478 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -547,6 +547,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account);
user = ksmbd_alloc_user(&resp->login_response, resp_ext);
+ kvfree(resp_ext);
if (!user) {
ksmbd_debug(AUTH, "login failure\n");
retval = -ENOMEM;
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 8a19058efc3f..8edae6eada3d 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -11,6 +11,7 @@
#include "server.h"
#include "smb_common.h"
#include "mgmt/ksmbd_ida.h"
+#include "mgmt/user_session.h"
#include "connection.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
@@ -123,6 +124,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
kvfree(conn->request_buf);
kfree(conn->preauth_info);
kfree(conn->mechToken);
+ ksmbd_preauth_session_destroy(conn);
ksmbd_conn_put(conn);
}
@@ -161,6 +163,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
init_waitqueue_head(&conn->r_count_q);
INIT_LIST_HEAD(&conn->requests);
INIT_LIST_HEAD(&conn->async_requests);
+ INIT_LIST_HEAD(&conn->preauth_sess_table);
spin_lock_init(&conn->request_lock);
spin_lock_init(&conn->credits_lock);
ida_init(&conn->async_ida);
@@ -252,6 +255,22 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
up_read(&conn_list_lock);
}
+void ksmbd_conn_abort(struct ksmbd_conn *conn)
+{
+ bool shutdown = false;
+
+ spin_lock(&conn->request_lock);
+ if (!ksmbd_conn_exiting(conn) && !ksmbd_conn_releasing(conn)) {
+ ksmbd_conn_set_exiting(conn);
+ shutdown = true;
+ }
+ spin_unlock(&conn->request_lock);
+ wake_up_all(&conn->req_running_q);
+
+ if (shutdown && conn->transport->ops->shutdown)
+ conn->transport->ops->shutdown(conn->transport);
+}
+
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
{
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index e277c224ef34..01b9020146a4 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -165,6 +165,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
void ksmbd_conn_put(struct ksmbd_conn *conn);
+void ksmbd_conn_abort(struct ksmbd_conn *conn);
int ksmbd_conn_wq_init(void);
void ksmbd_conn_wq_destroy(void);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c
index e9b3a8e04966..3d7c9b6b032e 100644
--- a/fs/smb/server/mgmt/user_config.c
+++ b/fs/smb/server/mgmt/user_config.c
@@ -26,6 +26,7 @@ struct ksmbd_user *ksmbd_login_user(const char *account)
resp_ext = ksmbd_ipc_login_request_ext(account);
user = ksmbd_alloc_user(resp, resp_ext);
+ kvfree(resp_ext);
out:
kvfree(resp);
return user;
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index d9768f88aabc..8423568710e7 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -370,6 +370,17 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
return sess;
}
+void ksmbd_preauth_session_destroy(struct ksmbd_conn *conn)
+{
+ struct preauth_session *sess, *tmp;
+
+ list_for_each_entry_safe(sess, tmp, &conn->preauth_sess_table,
+ preauth_entry) {
+ list_del(&sess->preauth_entry);
+ kfree(sess);
+ }
+}
+
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id)
{
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index c5749d6ec715..523b68c4ca40 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -100,6 +100,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id);
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id);
+void ksmbd_preauth_session_destroy(struct ksmbd_conn *conn);
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
unsigned long long id);
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 21869bcd7f2c..247595d8cb90 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -170,8 +170,10 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (conn->ops->is_transform_hdr &&
conn->ops->is_transform_hdr(work->request_buf)) {
rc = conn->ops->decrypt_req(work);
- if (rc < 0)
+ if (rc < 0) {
+ ksmbd_conn_abort(conn);
return;
+ }
work->encrypted = true;
}
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index bcf05caa2304..4c50b7f57f76 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -262,8 +262,10 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ /*
+ * Durable handles are in-memory only. Do not advertise persistent
+ * handles until CA recovery and fencing are implemented.
+ */
}
/**
@@ -286,10 +288,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
-
- INIT_LIST_HEAD(&conn->preauth_sess_table);
+ /* See init_smb3_02_server(): persistent handles require CA recovery. */
return 0;
}
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index d97ffe3d2a71..807bfcebb5c2 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1163,7 +1163,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
GFP_KERNEL);
if (!conn->preauth_info) {
rc = -ENOMEM;
- rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
goto err_out;
}
@@ -1260,7 +1260,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_conn_set_need_setup(conn);
err_out:
- if (rc)
+ if (rc && rsp->hdr.Status == STATUS_SUCCESS)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
if (!rc)
@@ -2036,12 +2036,11 @@ int smb2_tree_connect(struct ksmbd_work *work)
write_unlock(&sess->tree_conns_lock);
rsp->StructureSize = cpu_to_le16(16);
out_err1:
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share &&
- test_share_config_flag(share,
- KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY))
- rsp->Capabilities = SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
- else
- rsp->Capabilities = 0;
+ /*
+ * A configured CA share is not continuously available until persistent
+ * open recovery, ownership fencing, and failover are implemented.
+ */
+ rsp->Capabilities = 0;
rsp->Reserved = 0;
/* default manual caching */
rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
@@ -2996,29 +2995,23 @@ int smb2_open(struct ksmbd_work *work)
if (dh_info.reconnected == true) {
rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
lc, sess->user, name);
- if (rc) {
- ksmbd_put_durable_fd(dh_info.fp);
+ if (rc)
goto err_out2;
- }
rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
- if (rc) {
- ksmbd_put_durable_fd(dh_info.fp);
+ if (rc)
goto err_out2;
- }
fp = dh_info.fp;
if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
- ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
file_info = FILE_OPENED;
rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
- ksmbd_put_durable_fd(fp);
if (rc)
goto err_out2;
@@ -3788,6 +3781,20 @@ int smb2_open(struct ksmbd_work *work)
ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
}
+ if (dh_info.reconnected) {
+ /*
+ * If reconnect succeeded, fp was republished in the
+ * session file table. On a later error, ksmbd_fd_put()
+ * above drops the session reference; drop the durable
+ * lookup reference through the same session-aware path so
+ * final close removes the volatile id before freeing fp.
+ */
+ if (rc && fp == dh_info.fp)
+ ksmbd_fd_put(work, dh_info.fp);
+ else
+ ksmbd_put_durable_fd(dh_info.fp);
+ }
+
kfree(name);
kfree(lc);
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 06936ae521cf..cf72d0942e16 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1135,7 +1135,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
struct dentry *parent = path->dentry->d_parent;
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
- int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
+ int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
+ unsigned int dacloffset;
+ size_t dacl_struct_end;
u16 num_aces, ace_cnt = 0;
char *aces_base;
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@@ -1144,8 +1146,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
parent, &parent_pntsd);
if (pntsd_size <= 0)
return -ENOENT;
+
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
- if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
+ if (!dacloffset ||
+ check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
+ dacl_struct_end > (size_t)pntsd_size) {
rc = -EINVAL;
goto free_parent_pntsd;
}
@@ -1367,7 +1372,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
struct smb_ntsd *pntsd = NULL;
struct smb_acl *pdacl;
struct posix_acl *posix_acls;
- int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
+ int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
+ unsigned int dacl_offset;
+ size_t dacl_struct_end;
struct smb_sid sid;
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
struct smb_ace *ace;
@@ -1386,7 +1393,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
dacl_offset = le32_to_cpu(pntsd->dacloffset);
if (!dacl_offset ||
- (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+ check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
+ dacl_struct_end > (size_t)pntsd_size)
goto err_out;
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
@@ -1410,7 +1418,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
DELETE;
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
- aces_size = acl_size - sizeof(struct smb_acl);
+ aces_size = pdacl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
@@ -1434,7 +1442,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
id_to_sid(uid, sid_type, &sid);
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
- aces_size = acl_size - sizeof(struct smb_acl);
+ aces_size = pdacl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 554a6c92a1cc..bbe039a05b1c 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -498,6 +498,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_rpc_command *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_rpc_command))
+ return -EINVAL;
+
if (check_add_overflow(sizeof(struct ksmbd_rpc_command),
resp->payload_sz, &msg_sz))
return -EINVAL;
@@ -507,6 +510,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_spnego_authen_response *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_spnego_authen_response))
+ return -EINVAL;
+
msg_sz = sizeof(struct ksmbd_spnego_authen_response) +
resp->session_key_len + resp->spnego_blob_len;
break;
@@ -515,6 +521,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_share_config_response *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_share_config_response))
+ return -EINVAL;
+
if (resp->payload_sz) {
if (resp->payload_sz < resp->veto_list_sz)
return -EINVAL;
@@ -529,6 +538,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_login_response_ext *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_login_response_ext))
+ return -EINVAL;
+
if (resp->ngroups) {
if (resp->ngroups < 0 ||
resp->ngroups > NGROUPS_MAX) {
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index b3a1df597d50..faf6f7a34a2b 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -468,6 +468,11 @@ static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
atomic_dec(&active_num_conn);
}
+static void ksmbd_tcp_shutdown(struct ksmbd_transport *t)
+{
+ kernel_sock_shutdown(TCP_TRANS(t)->sock, SHUT_RDWR);
+}
+
static void tcp_destroy_socket(struct socket *ksmbd_socket)
{
int ret;
@@ -721,5 +726,6 @@ static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
.read = ksmbd_tcp_read,
.writev = ksmbd_tcp_writev,
.disconnect = ksmbd_tcp_disconnect,
+ .shutdown = ksmbd_tcp_shutdown,
.free_transport = ksmbd_tcp_free_transport,
};
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 69f807aff769..d7c291bede3f 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -333,9 +333,6 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
struct file_lock_context *ctx = locks_inode_context(file_inode(filp));
int error = 0;
- if (start == end)
- return 0;
-
if (!ctx || list_empty_careful(&ctx->flc_posix))
return 0;
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 12db854e4c51..c3d46baf2c16 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -971,12 +971,8 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
static bool ksmbd_durable_scavenger_alive(void)
{
- mutex_lock(&durable_scavenger_lock);
- if (!durable_scavenger_running) {
- mutex_unlock(&durable_scavenger_lock);
+ if (!durable_scavenger_running)
return false;
- }
- mutex_unlock(&durable_scavenger_lock);
if (kthread_should_stop())
return false;
@@ -1085,9 +1081,7 @@ static int ksmbd_durable_scavenger(void *dummy)
break;
}
- mutex_lock(&durable_scavenger_lock);
durable_scavenger_running = false;
- mutex_unlock(&durable_scavenger_lock);
module_put(THIS_MODULE);
@@ -1109,9 +1103,12 @@ void ksmbd_launch_ksmbd_durable_scavenger(void)
server_conf.dh_task = kthread_run(ksmbd_durable_scavenger,
(void *)NULL, "ksmbd-durable-scavenger");
- if (IS_ERR(server_conf.dh_task))
+ if (IS_ERR(server_conf.dh_task)) {
pr_err("cannot start conn thread, err : %ld\n",
PTR_ERR(server_conf.dh_task));
+ server_conf.dh_task = NULL;
+ durable_scavenger_running = false;
+ }
mutex_unlock(&durable_scavenger_lock);
}
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 25bf038b880a..2091af741632 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -295,7 +295,7 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
{
int remaining = length;
- if (length == 0)
+ if (length == 0 || offset < 0)
return 0;
else if (buffer == NULL)
return min(length, entry->length - offset);
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 4ac459c7efc3..c4a9009cb56a 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -511,6 +511,8 @@ static inline struct eventfs_inode *init_ei(struct eventfs_inode *ei, const char
if (!ei->name)
return NULL;
kref_init(&ei->kref);
+ INIT_LIST_HEAD(&ei->children);
+ INIT_LIST_HEAD(&ei->list);
return ei;
}
@@ -797,8 +799,6 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
ei->entries = entries;
ei->nr_entries = size;
ei->data = data;
- INIT_LIST_HEAD(&ei->children);
- INIT_LIST_HEAD(&ei->list);
mutex_lock(&eventfs_mutex);
if (!parent->is_freed)
@@ -874,9 +874,6 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
*/
ei->attr.mode |= EVENTFS_SAVE_UID | EVENTFS_SAVE_GID;
- INIT_LIST_HEAD(&ei->children);
- INIT_LIST_HEAD(&ei->list);
-
ti = get_tracefs(inode);
ti->flags |= TRACEFS_EVENT_INODE;
ti->private = ei;
diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
index e564d5ff8781..bb8242be8c16 100644
--- a/fs/ubifs/auth.c
+++ b/fs/ubifs/auth.c
@@ -218,7 +218,7 @@ int ubifs_sb_verify_signature(struct ubifs_info *c,
signode = snod->node;
- if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) {
+ if (le32_to_cpu(signode->len) > snod->len - sizeof(struct ubifs_sig_node)) {
ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len));
err = -EINVAL;
goto out_destroy;
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 848c46c5e9d3..6e40689845bc 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -529,7 +529,7 @@ static int udf_do_extend_file(struct inode *inode,
sb->s_blocksize - 1) & ~(sb->s_blocksize - 1));
iinfo->i_lenExtents =
(iinfo->i_lenExtents + sb->s_blocksize - 1) &
- ~(sb->s_blocksize - 1);
+ ~((u64)sb->s_blocksize - 1);
}
add = 0;
diff --git a/fs/udf/partition.c b/fs/udf/partition.c
index 2b85c9501bed..ad8dcedca263 100644
--- a/fs/udf/partition.c
+++ b/fs/udf/partition.c
@@ -55,7 +55,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block,
map = &sbi->s_partmaps[partition];
vdata = &map->s_type_specific.s_virtual;
- if (block > vdata->s_num_entries) {
+ if (block >= vdata->s_num_entries) {
udf_debug("Trying to access block beyond end of VAT (%u max %u)\n",
block, vdata->s_num_entries);
return 0xFFFFFFFF;
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 1debc30203b3..82a165c4dce3 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -2011,6 +2011,17 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
return 0;
}
+static void udf_mark_buffer_dirty(struct buffer_head *bh)
+{
+ /*
+ * We set buffer uptodate unconditionally here to avoid spurious
+ * warnings from mark_buffer_dirty() when previous EIO has marked
+ * the buffer as !uptodate
+ */
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+}
+
static void udf_finalize_lvid(struct logicalVolIntegrityDesc *lvid)
{
struct timespec64 ts;
@@ -2046,7 +2057,7 @@ static void udf_open_lvid(struct super_block *sb)
UDF_SET_FLAG(sb, UDF_FLAG_INCONSISTENT);
udf_finalize_lvid(lvid);
- mark_buffer_dirty(bh);
+ udf_mark_buffer_dirty(bh);
sbi->s_lvid_dirty = 0;
mutex_unlock(&sbi->s_alloc_mutex);
/* Make opening of filesystem visible on the media immediately */
@@ -2079,14 +2090,8 @@ static void udf_close_lvid(struct super_block *sb)
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_INCONSISTENT))
lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE);
- /*
- * We set buffer uptodate unconditionally here to avoid spurious
- * warnings from mark_buffer_dirty() when previous EIO has marked
- * the buffer as !uptodate
- */
- set_buffer_uptodate(bh);
udf_finalize_lvid(lvid);
- mark_buffer_dirty(bh);
+ udf_mark_buffer_dirty(bh);
sbi->s_lvid_dirty = 0;
mutex_unlock(&sbi->s_alloc_mutex);
/* Make closing of filesystem visible on the media immediately */
@@ -2379,7 +2384,7 @@ static int udf_sync_fs(struct super_block *sb, int wait)
* Blockdevice will be synced later so we don't have to submit
* the buffer for IO
*/
- mark_buffer_dirty(bh);
+ udf_mark_buffer_dirty(bh);
sbi->s_lvid_dirty = 0;
}
mutex_unlock(&sbi->s_alloc_mutex);
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index f7eaf7b14594..ee78403bf7c9 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -36,6 +36,8 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
/* Reserve one byte for terminating \0 */
tolen--;
while (elen < fromlen) {
+ if (fromlen - elen < sizeof(struct pathComponent))
+ return -EIO;
pc = (struct pathComponent *)(from + elen);
elen += sizeof(struct pathComponent);
switch (pc->componentType) {
diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h
index 699c1a37b8e7..0462bb9da651 100644
--- a/include/acpi/pcc.h
+++ b/include/acpi/pcc.h
@@ -38,7 +38,6 @@ struct pcc_mbox_chan {
extern struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
-extern int pcc_mbox_ioremap(struct mbox_chan *chan);
#else
static inline struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
@@ -46,10 +45,6 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
return ERR_PTR(-ENODEV);
}
static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }
-static inline int pcc_mbox_ioremap(struct mbox_chan *chan)
-{
- return 0;
-};
#endif
#endif /* _PCC_H */
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 3f34ebb27525..f85bbb7b8c6c 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -418,6 +418,8 @@ int acpi_processor_power_init(struct acpi_processor *pr);
int acpi_processor_power_exit(struct acpi_processor *pr);
int acpi_processor_power_state_has_changed(struct acpi_processor *pr);
int acpi_processor_hotplug(struct acpi_processor *pr);
+void acpi_processor_register_idle_driver(void);
+void acpi_processor_unregister_idle_driver(void);
#else
static inline int acpi_processor_power_init(struct acpi_processor *pr)
{
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 398fa28c388d..fd20812cbcc3 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -482,7 +482,7 @@ static inline void bpf_obj_memcpy(struct btf_record *rec,
if (IS_ERR_OR_NULL(rec)) {
if (long_memcpy)
- bpf_long_memcpy(dst, src, round_up(size, 8));
+ bpf_long_memcpy(dst, src, size);
else
memcpy(dst, src, size);
return;
@@ -505,7 +505,7 @@ static inline void copy_map_value(struct bpf_map *map, void *dst, void *src)
static inline void copy_map_value_long(struct bpf_map *map, void *dst, void *src)
{
- bpf_obj_memcpy(map->record, dst, src, map->value_size, true);
+ bpf_obj_memcpy(map->record, dst, src, round_up(map->value_size, 8), true);
}
static inline void bpf_obj_memzero(struct btf_record *rec, void *dst, u32 size)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 7db1c0759c09..a066f58db5f5 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1236,8 +1236,8 @@ efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
/*
* efi_runtime_service() function identifiers.
- * "NONE" is used by efi_recover_from_page_fault() to check if the page
- * fault happened while executing an efi runtime service.
+ * "NONE" is used by efi_crash_gracefully_on_page_fault() to check if the
+ * page fault happened while executing an efi runtime service.
*/
enum efi_rts_ids {
EFI_NONE,
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 90081afa10ce..77fb0c510119 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -1117,6 +1117,27 @@ struct irq_domain_chip_generic {
struct irq_chip_generic *gc[];
};
+/**
+ * struct irq_domain_chip_generic_info - Generic chip information structure
+ * @name: Name of the generic interrupt chip
+ * @handler: Interrupt handler used by the generic interrupt chip
+ * @irqs_per_chip: Number of interrupts each chip handles (max 32)
+ * @num_ct: Number of irq_chip_type instances associated with each
+ * chip
+ * @irq_flags_to_clear: IRQ_* bits to clear in the mapping function
+ * @irq_flags_to_set: IRQ_* bits to set in the mapping function
+ * @gc_flags: Generic chip specific setup flags
+ */
+struct irq_domain_chip_generic_info {
+ const char *name;
+ irq_flow_handler_t handler;
+ unsigned int irqs_per_chip;
+ unsigned int num_ct;
+ unsigned int irq_flags_to_clear;
+ unsigned int irq_flags_to_set;
+ enum irq_gc_flags gc_flags;
+};
+
/* Generic chip callback functions */
void irq_gc_noop(struct irq_data *d);
void irq_gc_mask_disable_reg(struct irq_data *d);
@@ -1153,6 +1174,20 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc,
struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq);
+#ifdef CONFIG_GENERIC_IRQ_CHIP
+int irq_domain_alloc_generic_chips(struct irq_domain *d,
+ const struct irq_domain_chip_generic_info *info);
+void irq_domain_remove_generic_chips(struct irq_domain *d);
+#else
+static inline int
+irq_domain_alloc_generic_chips(struct irq_domain *d,
+ const struct irq_domain_chip_generic_info *info)
+{
+ return -EINVAL;
+}
+static inline void irq_domain_remove_generic_chips(struct irq_domain *d) { }
+#endif /* CONFIG_GENERIC_IRQ_CHIP */
+
int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
int num_ct, const char *name,
irq_flow_handler_t handler,
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 51c254b7fec2..4b7cbaeeff94 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -141,6 +141,7 @@ struct irq_domain_chip_generic;
* purposes related to the irq domain.
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
* @msi_parent_ops: Pointer to MSI parent domain methods for per device domain init
+ * @exit: Function called when the domain is destroyed
*
* Revmap data, used internally by the irq domain code:
* @revmap_size: Size of the linear map table @revmap[]
@@ -169,6 +170,7 @@ struct irq_domain {
#ifdef CONFIG_GENERIC_MSI_IRQ
const struct msi_parent_ops *msi_parent_ops;
#endif
+ void (*exit)(struct irq_domain *d);
/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max;
@@ -208,6 +210,9 @@ enum {
/* Irq domain is a MSI device domain */
IRQ_DOMAIN_FLAG_MSI_DEVICE = (1 << 9),
+ /* Irq domain must destroy generic chips when removed */
+ IRQ_DOMAIN_FLAG_DESTROY_GC = (1 << 10),
+
/*
* Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
* for implementation specific purposes and ignored by the
@@ -257,6 +262,47 @@ static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa)
}
void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
+
+struct irq_domain_chip_generic_info;
+
+/**
+ * struct irq_domain_info - Domain information structure
+ * @fwnode: firmware node for the interrupt controller
+ * @domain_flags: Additional flags to add to the domain flags
+ * @size: Size of linear map; 0 for radix mapping only
+ * @hwirq_max: Maximum number of interrupts supported by controller
+ * @direct_max: Maximum value of direct maps;
+ * Use ~0 for no limit; 0 for no direct mapping
+ * @ops: Domain operation callbacks
+ * @host_data: Controller private data pointer
+ * @dgc_info: Geneneric chip information structure pointer used to
+ * create generic chips for the domain if not NULL.
+ * @init: Function called when the domain is created.
+ * Allow to do some additional domain initialisation.
+ * @exit: Function called when the domain is destroyed.
+ * Allow to do some additional cleanup operation.
+ */
+struct irq_domain_info {
+ struct fwnode_handle *fwnode;
+ unsigned int domain_flags;
+ unsigned int size;
+ irq_hw_number_t hwirq_max;
+ int direct_max;
+ const struct irq_domain_ops *ops;
+ void *host_data;
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ /**
+ * @parent: Pointer to the parent irq domain used in a hierarchy domain
+ */
+ struct irq_domain *parent;
+#endif
+ struct irq_domain_chip_generic_info *dgc_info;
+ int (*init)(struct irq_domain *d);
+ void (*exit)(struct irq_domain *d);
+};
+
+struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
+
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 45d5b0a76b0b..a08d250b7c4d 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -186,6 +186,7 @@ struct kprobe_blacklist_entry {
struct list_head list;
unsigned long start_addr;
unsigned long end_addr;
+ struct rcu_head rcu;
};
#ifdef CONFIG_KPROBES
diff --git a/include/linux/list.h b/include/linux/list.h
index 164b4d0e9d2a..c7e05da7f9a3 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -142,10 +142,13 @@ static inline bool __list_del_entry_valid(struct list_head *entry)
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
+ *
+ * Must be inlined to ensure it can be safely called
+ * with initdata arguments.
*/
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
+static __always_inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
@@ -163,8 +166,12 @@ static inline void __list_add(struct list_head *new,
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
+ *
+ * Must be inlined to ensure it can be safely called
+ * with initdata arguments.
*/
-static inline void list_add(struct list_head *new, struct list_head *head)
+static __always_inline void list_add(struct list_head *new,
+ struct list_head *head)
{
__list_add(new, head, head->next);
}
diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h
index d01e850b570f..1ca191a7e53c 100644
--- a/include/linux/maple_tree.h
+++ b/include/linux/maple_tree.h
@@ -478,7 +478,7 @@ void mas_destroy(struct ma_state *mas);
int mas_expected_entries(struct ma_state *mas, unsigned long nr_entries);
void *mas_prev(struct ma_state *mas, unsigned long min);
-void *mas_prev_range(struct ma_state *mas, unsigned long max);
+void *mas_prev_range(struct ma_state *mas, unsigned long min);
void *mas_next(struct ma_state *mas, unsigned long max);
void *mas_next_range(struct ma_state *mas, unsigned long max);
diff --git a/include/linux/mfd/tps6594.h b/include/linux/mfd/tps6594.h
index 3f7c5e23cd4c..e754c01acc64 100644
--- a/include/linux/mfd/tps6594.h
+++ b/include/linux/mfd/tps6594.h
@@ -18,12 +18,13 @@ enum pmic_id {
TPS6594,
TPS6593,
LP8764,
+ TPS65224,
};
/* Macro to get page index from register address */
#define TPS6594_REG_TO_PAGE(reg) ((reg) >> 8)
-/* Registers for page 0 of TPS6594 */
+/* Registers for page 0 */
#define TPS6594_REG_DEV_REV 0x01
#define TPS6594_REG_NVM_CODE_1 0x02
@@ -56,9 +57,6 @@ enum pmic_id {
#define TPS6594_REG_GPIOX_OUT(gpio_inst) (TPS6594_REG_GPIO_OUT_1 + (gpio_inst) / 8)
#define TPS6594_REG_GPIOX_IN(gpio_inst) (TPS6594_REG_GPIO_IN_1 + (gpio_inst) / 8)
-#define TPS6594_REG_GPIO_IN_1 0x3f
-#define TPS6594_REG_GPIO_IN_2 0x40
-
#define TPS6594_REG_RAIL_SEL_1 0x41
#define TPS6594_REG_RAIL_SEL_2 0x42
#define TPS6594_REG_RAIL_SEL_3 0x43
@@ -70,13 +68,15 @@ enum pmic_id {
#define TPS6594_REG_FSM_TRIG_MASK_3 0x48
#define TPS6594_REG_MASK_BUCK1_2 0x49
+#define TPS65224_REG_MASK_BUCKS 0x49
#define TPS6594_REG_MASK_BUCK3_4 0x4a
#define TPS6594_REG_MASK_BUCK5 0x4b
#define TPS6594_REG_MASK_LDO1_2 0x4c
+#define TPS65224_REG_MASK_LDOS 0x4c
#define TPS6594_REG_MASK_LDO3_4 0x4d
#define TPS6594_REG_MASK_VMON 0x4e
-#define TPS6594_REG_MASK_GPIO1_8_FALL 0x4f
-#define TPS6594_REG_MASK_GPIO1_8_RISE 0x50
+#define TPS6594_REG_MASK_GPIO_FALL 0x4f
+#define TPS6594_REG_MASK_GPIO_RISE 0x50
#define TPS6594_REG_MASK_GPIO9_11 0x51
#define TPS6594_REG_MASK_STARTUP 0x52
#define TPS6594_REG_MASK_MISC 0x53
@@ -174,6 +174,10 @@ enum pmic_id {
#define TPS6594_REG_REGISTER_LOCK 0xa1
+#define TPS65224_REG_SRAM_ACCESS_1 0xa2
+#define TPS65224_REG_SRAM_ACCESS_2 0xa3
+#define TPS65224_REG_SRAM_ADDR_CTRL 0xa4
+#define TPS65224_REG_RECOV_CNT_PFSM_INCR 0xa5
#define TPS6594_REG_MANUFACTURING_VER 0xa6
#define TPS6594_REG_CUSTOMER_NVM_ID_REG 0xa7
@@ -182,6 +186,9 @@ enum pmic_id {
#define TPS6594_REG_SOFT_REBOOT_REG 0xab
+#define TPS65224_REG_ADC_CTRL 0xac
+#define TPS65224_REG_ADC_RESULT_REG_1 0xad
+#define TPS65224_REG_ADC_RESULT_REG_2 0xae
#define TPS6594_REG_RTC_SECONDS 0xb5
#define TPS6594_REG_RTC_MINUTES 0xb6
#define TPS6594_REG_RTC_HOURS 0xb7
@@ -199,6 +206,7 @@ enum pmic_id {
#define TPS6594_REG_RTC_CTRL_1 0xc2
#define TPS6594_REG_RTC_CTRL_2 0xc3
+#define TPS65224_REG_STARTUP_CTRL 0xc3
#define TPS6594_REG_RTC_STATUS 0xc4
#define TPS6594_REG_RTC_INTERRUPTS 0xc5
#define TPS6594_REG_RTC_COMP_LSB 0xc6
@@ -214,13 +222,17 @@ enum pmic_id {
#define TPS6594_REG_PFSM_DELAY_REG_2 0xce
#define TPS6594_REG_PFSM_DELAY_REG_3 0xcf
#define TPS6594_REG_PFSM_DELAY_REG_4 0xd0
+#define TPS65224_REG_ADC_GAIN_COMP_REG 0xd0
+#define TPS65224_REG_CRC_CALC_CONTROL 0xef
+#define TPS65224_REG_REGMAP_USER_CRC_LOW 0xf0
+#define TPS65224_REG_REGMAP_USER_CRC_HIGH 0xf1
-/* Registers for page 1 of TPS6594 */
+/* Registers for page 1 */
#define TPS6594_REG_SERIAL_IF_CONFIG 0x11a
#define TPS6594_REG_I2C1_ID 0x122
#define TPS6594_REG_I2C2_ID 0x123
-/* Registers for page 4 of TPS6594 */
+/* Registers for page 4 */
#define TPS6594_REG_WD_ANSWER_REG 0x401
#define TPS6594_REG_WD_QUESTION_ANSW_CNT 0x402
#define TPS6594_REG_WD_WIN1_CFG 0x403
@@ -241,16 +253,26 @@ enum pmic_id {
#define TPS6594_BIT_BUCK_PLDN BIT(5)
#define TPS6594_BIT_BUCK_RV_SEL BIT(7)
-/* BUCKX_CONF register field definition */
+/* TPS6594 BUCKX_CONF register field definition */
#define TPS6594_MASK_BUCK_SLEW_RATE GENMASK(2, 0)
#define TPS6594_MASK_BUCK_ILIM GENMASK(5, 3)
-/* BUCKX_PG_WINDOW register field definition */
+/* TPS65224 BUCKX_CONF register field definition */
+#define TPS65224_MASK_BUCK_SLEW_RATE GENMASK(1, 0)
+
+/* TPS6594 BUCKX_PG_WINDOW register field definition */
#define TPS6594_MASK_BUCK_OV_THR GENMASK(2, 0)
#define TPS6594_MASK_BUCK_UV_THR GENMASK(5, 3)
-/* BUCKX VSET */
-#define TPS6594_MASK_BUCKS_VSET GENMASK(7, 0)
+/* TPS65224 BUCKX_PG_WINDOW register field definition */
+#define TPS65224_MASK_BUCK_VMON_THR GENMASK(1, 0)
+
+/* TPS6594 BUCKX_VOUT register field definition */
+#define TPS6594_MASK_BUCKS_VSET GENMASK(7, 0)
+
+/* TPS65224 BUCKX_VOUT register field definition */
+#define TPS65224_MASK_BUCK1_VSET GENMASK(7, 0)
+#define TPS65224_MASK_BUCKS_VSET GENMASK(6, 0)
/* LDOX_CTRL register field definition */
#define TPS6594_BIT_LDO_EN BIT(0)
@@ -258,6 +280,7 @@ enum pmic_id {
#define TPS6594_BIT_LDO_VMON_EN BIT(4)
#define TPS6594_MASK_LDO_PLDN GENMASK(6, 5)
#define TPS6594_BIT_LDO_RV_SEL BIT(7)
+#define TPS65224_BIT_LDO_DISCHARGE_EN BIT(5)
/* LDORTC_CTRL register field definition */
#define TPS6594_BIT_LDORTC_DIS BIT(0)
@@ -271,6 +294,9 @@ enum pmic_id {
#define TPS6594_MASK_LDO_OV_THR GENMASK(2, 0)
#define TPS6594_MASK_LDO_UV_THR GENMASK(5, 3)
+/* LDOX_PG_WINDOW register field definition */
+#define TPS65224_MASK_LDO_VMON_THR GENMASK(1, 0)
+
/* VCCA_VMON_CTRL register field definition */
#define TPS6594_BIT_VMON_EN BIT(0)
#define TPS6594_BIT_VMON1_EN BIT(1)
@@ -278,10 +304,12 @@ enum pmic_id {
#define TPS6594_BIT_VMON2_EN BIT(3)
#define TPS6594_BIT_VMON2_RV_SEL BIT(4)
#define TPS6594_BIT_VMON_DEGLITCH_SEL BIT(5)
+#define TPS65224_BIT_VMON_DEGLITCH_SEL GENMASK(7, 5)
/* VCCA_PG_WINDOW register field definition */
#define TPS6594_MASK_VCCA_OV_THR GENMASK(2, 0)
#define TPS6594_MASK_VCCA_UV_THR GENMASK(5, 3)
+#define TPS65224_MASK_VCCA_VMON_THR GENMASK(1, 0)
#define TPS6594_BIT_VCCA_PG_SET BIT(6)
/* VMONX_PG_WINDOW register field definition */
@@ -289,6 +317,9 @@ enum pmic_id {
#define TPS6594_MASK_VMONX_UV_THR GENMASK(5, 3)
#define TPS6594_BIT_VMONX_RANGE BIT(6)
+/* VMONX_PG_WINDOW register field definition */
+#define TPS65224_MASK_VMONX_THR GENMASK(1, 0)
+
/* GPIOX_CONF register field definition */
#define TPS6594_BIT_GPIO_DIR BIT(0)
#define TPS6594_BIT_GPIO_OD BIT(1)
@@ -296,6 +327,8 @@ enum pmic_id {
#define TPS6594_BIT_GPIO_PU_PD_EN BIT(3)
#define TPS6594_BIT_GPIO_DEGLITCH_EN BIT(4)
#define TPS6594_MASK_GPIO_SEL GENMASK(7, 5)
+#define TPS65224_MASK_GPIO_SEL GENMASK(6, 5)
+#define TPS65224_MASK_GPIO_SEL_GPIO6 GENMASK(7, 5)
/* NPWRON_CONF register field definition */
#define TPS6594_BIT_NRSTOUT_OD BIT(0)
@@ -305,6 +338,12 @@ enum pmic_id {
#define TPS6594_BIT_ENABLE_POL BIT(5)
#define TPS6594_MASK_NPWRON_SEL GENMASK(7, 6)
+/* POWER_ON_CONFIG register field definition */
+#define TPS65224_BIT_NINT_ENDRV_PU_SEL BIT(0)
+#define TPS65224_BIT_NINT_ENDRV_SEL BIT(1)
+#define TPS65224_BIT_EN_PB_DEGL BIT(5)
+#define TPS65224_MASK_EN_PB_VSENSE_CONFIG GENMASK(7, 6)
+
/* GPIO_OUT_X register field definition */
#define TPS6594_BIT_GPIOX_OUT(gpio_inst) BIT((gpio_inst) % 8)
@@ -312,6 +351,12 @@ enum pmic_id {
#define TPS6594_BIT_GPIOX_IN(gpio_inst) BIT((gpio_inst) % 8)
#define TPS6594_BIT_NPWRON_IN BIT(3)
+/* GPIO_OUT_X register field definition */
+#define TPS65224_BIT_GPIOX_OUT(gpio_inst) BIT((gpio_inst))
+
+/* GPIO_IN_X register field definition */
+#define TPS65224_BIT_GPIOX_IN(gpio_inst) BIT((gpio_inst))
+
/* RAIL_SEL_1 register field definition */
#define TPS6594_MASK_BUCK1_GRP_SEL GENMASK(1, 0)
#define TPS6594_MASK_BUCK2_GRP_SEL GENMASK(3, 2)
@@ -343,6 +388,9 @@ enum pmic_id {
#define TPS6594_BIT_GPIOX_FSM_MASK(gpio_inst) BIT(((gpio_inst) << 1) % 8)
#define TPS6594_BIT_GPIOX_FSM_MASK_POL(gpio_inst) BIT(((gpio_inst) << 1) % 8 + 1)
+#define TPS65224_BIT_GPIOX_FSM_MASK(gpio_inst) BIT(((gpio_inst) << 1) % 6)
+#define TPS65224_BIT_GPIOX_FSM_MASK_POL(gpio_inst) BIT(((gpio_inst) << 1) % 6 + 1)
+
/* MASK_BUCKX register field definition */
#define TPS6594_BIT_BUCKX_OV_MASK(buck_inst) BIT(((buck_inst) << 2) % 8)
#define TPS6594_BIT_BUCKX_UV_MASK(buck_inst) BIT(((buck_inst) << 2) % 8 + 1)
@@ -361,22 +409,46 @@ enum pmic_id {
#define TPS6594_BIT_VMON2_OV_MASK BIT(5)
#define TPS6594_BIT_VMON2_UV_MASK BIT(6)
+/* MASK_BUCK Register field definition */
+#define TPS65224_BIT_BUCK1_UVOV_MASK BIT(0)
+#define TPS65224_BIT_BUCK2_UVOV_MASK BIT(1)
+#define TPS65224_BIT_BUCK3_UVOV_MASK BIT(2)
+#define TPS65224_BIT_BUCK4_UVOV_MASK BIT(4)
+
+/* MASK_LDO_VMON register field definition */
+#define TPS65224_BIT_LDO1_UVOV_MASK BIT(0)
+#define TPS65224_BIT_LDO2_UVOV_MASK BIT(1)
+#define TPS65224_BIT_LDO3_UVOV_MASK BIT(2)
+#define TPS65224_BIT_VCCA_UVOV_MASK BIT(4)
+#define TPS65224_BIT_VMON1_UVOV_MASK BIT(5)
+#define TPS65224_BIT_VMON2_UVOV_MASK BIT(6)
+
/* MASK_GPIOX register field definition */
#define TPS6594_BIT_GPIOX_FALL_MASK(gpio_inst) BIT((gpio_inst) < 8 ? \
(gpio_inst) : (gpio_inst) % 8)
#define TPS6594_BIT_GPIOX_RISE_MASK(gpio_inst) BIT((gpio_inst) < 8 ? \
(gpio_inst) : (gpio_inst) % 8 + 3)
+/* MASK_GPIOX register field definition */
+#define TPS65224_BIT_GPIOX_FALL_MASK(gpio_inst) BIT((gpio_inst))
+#define TPS65224_BIT_GPIOX_RISE_MASK(gpio_inst) BIT((gpio_inst))
/* MASK_STARTUP register field definition */
#define TPS6594_BIT_NPWRON_START_MASK BIT(0)
#define TPS6594_BIT_ENABLE_MASK BIT(1)
#define TPS6594_BIT_FSD_MASK BIT(4)
#define TPS6594_BIT_SOFT_REBOOT_MASK BIT(5)
+#define TPS65224_BIT_VSENSE_MASK BIT(0)
+#define TPS65224_BIT_PB_SHORT_MASK BIT(2)
/* MASK_MISC register field definition */
#define TPS6594_BIT_BIST_PASS_MASK BIT(0)
#define TPS6594_BIT_EXT_CLK_MASK BIT(1)
+#define TPS65224_BIT_REG_UNLOCK_MASK BIT(2)
#define TPS6594_BIT_TWARN_MASK BIT(3)
+#define TPS65224_BIT_PB_LONG_MASK BIT(4)
+#define TPS65224_BIT_PB_FALL_MASK BIT(5)
+#define TPS65224_BIT_PB_RISE_MASK BIT(6)
+#define TPS65224_BIT_ADC_CONV_READY_MASK BIT(7)
/* MASK_MODERATE_ERR register field definition */
#define TPS6594_BIT_BIST_FAIL_MASK BIT(1)
@@ -391,6 +463,8 @@ enum pmic_id {
#define TPS6594_BIT_ORD_SHUTDOWN_MASK BIT(1)
#define TPS6594_BIT_MCU_PWR_ERR_MASK BIT(2)
#define TPS6594_BIT_SOC_PWR_ERR_MASK BIT(3)
+#define TPS65224_BIT_COMM_ERR_MASK BIT(4)
+#define TPS65224_BIT_I2C2_ERR_MASK BIT(5)
/* MASK_COMM_ERR register field definition */
#define TPS6594_BIT_COMM_FRM_ERR_MASK BIT(0)
@@ -426,6 +500,12 @@ enum pmic_id {
#define TPS6594_BIT_BUCK3_4_INT BIT(1)
#define TPS6594_BIT_BUCK5_INT BIT(2)
+/* INT_BUCK register field definition */
+#define TPS65224_BIT_BUCK1_UVOV_INT BIT(0)
+#define TPS65224_BIT_BUCK2_UVOV_INT BIT(1)
+#define TPS65224_BIT_BUCK3_UVOV_INT BIT(2)
+#define TPS65224_BIT_BUCK4_UVOV_INT BIT(3)
+
/* INT_BUCKX register field definition */
#define TPS6594_BIT_BUCKX_OV_INT(buck_inst) BIT(((buck_inst) << 2) % 8)
#define TPS6594_BIT_BUCKX_UV_INT(buck_inst) BIT(((buck_inst) << 2) % 8 + 1)
@@ -437,6 +517,14 @@ enum pmic_id {
#define TPS6594_BIT_LDO3_4_INT BIT(1)
#define TPS6594_BIT_VCCA_INT BIT(4)
+/* INT_LDO_VMON register field definition */
+#define TPS65224_BIT_LDO1_UVOV_INT BIT(0)
+#define TPS65224_BIT_LDO2_UVOV_INT BIT(1)
+#define TPS65224_BIT_LDO3_UVOV_INT BIT(2)
+#define TPS65224_BIT_VCCA_UVOV_INT BIT(4)
+#define TPS65224_BIT_VMON1_UVOV_INT BIT(5)
+#define TPS65224_BIT_VMON2_UVOV_INT BIT(6)
+
/* INT_LDOX register field definition */
#define TPS6594_BIT_LDOX_OV_INT(ldo_inst) BIT(((ldo_inst) << 2) % 8)
#define TPS6594_BIT_LDOX_UV_INT(ldo_inst) BIT(((ldo_inst) << 2) % 8 + 1)
@@ -462,17 +550,32 @@ enum pmic_id {
/* INT_GPIOX register field definition */
#define TPS6594_BIT_GPIOX_INT(gpio_inst) BIT(gpio_inst)
+/* INT_GPIO register field definition */
+#define TPS65224_BIT_GPIO1_INT BIT(0)
+#define TPS65224_BIT_GPIO2_INT BIT(1)
+#define TPS65224_BIT_GPIO3_INT BIT(2)
+#define TPS65224_BIT_GPIO4_INT BIT(3)
+#define TPS65224_BIT_GPIO5_INT BIT(4)
+#define TPS65224_BIT_GPIO6_INT BIT(5)
+
/* INT_STARTUP register field definition */
#define TPS6594_BIT_NPWRON_START_INT BIT(0)
+#define TPS65224_BIT_VSENSE_INT BIT(0)
#define TPS6594_BIT_ENABLE_INT BIT(1)
#define TPS6594_BIT_RTC_INT BIT(2)
+#define TPS65224_BIT_PB_SHORT_INT BIT(2)
#define TPS6594_BIT_FSD_INT BIT(4)
#define TPS6594_BIT_SOFT_REBOOT_INT BIT(5)
/* INT_MISC register field definition */
#define TPS6594_BIT_BIST_PASS_INT BIT(0)
#define TPS6594_BIT_EXT_CLK_INT BIT(1)
+#define TPS65224_BIT_REG_UNLOCK_INT BIT(2)
#define TPS6594_BIT_TWARN_INT BIT(3)
+#define TPS65224_BIT_PB_LONG_INT BIT(4)
+#define TPS65224_BIT_PB_FALL_INT BIT(5)
+#define TPS65224_BIT_PB_RISE_INT BIT(6)
+#define TPS65224_BIT_ADC_CONV_READY_INT BIT(7)
/* INT_MODERATE_ERR register field definition */
#define TPS6594_BIT_TSD_ORD_INT BIT(0)
@@ -488,6 +591,7 @@ enum pmic_id {
#define TPS6594_BIT_TSD_IMM_INT BIT(0)
#define TPS6594_BIT_VCCA_OVP_INT BIT(1)
#define TPS6594_BIT_PFSM_ERR_INT BIT(2)
+#define TPS65224_BIT_BG_XMON_INT BIT(3)
/* INT_FSM_ERR register field definition */
#define TPS6594_BIT_IMM_SHUTDOWN_INT BIT(0)
@@ -496,6 +600,7 @@ enum pmic_id {
#define TPS6594_BIT_SOC_PWR_ERR_INT BIT(3)
#define TPS6594_BIT_COMM_ERR_INT BIT(4)
#define TPS6594_BIT_READBACK_ERR_INT BIT(5)
+#define TPS65224_BIT_I2C2_ERR_INT BIT(5)
#define TPS6594_BIT_ESM_INT BIT(6)
#define TPS6594_BIT_WD_INT BIT(7)
@@ -536,8 +641,18 @@ enum pmic_id {
#define TPS6594_BIT_VMON2_OV_STAT BIT(5)
#define TPS6594_BIT_VMON2_UV_STAT BIT(6)
+/* STAT_LDO_VMON register field definition */
+#define TPS65224_BIT_LDO1_UVOV_STAT BIT(0)
+#define TPS65224_BIT_LDO2_UVOV_STAT BIT(1)
+#define TPS65224_BIT_LDO3_UVOV_STAT BIT(2)
+#define TPS65224_BIT_VCCA_UVOV_STAT BIT(4)
+#define TPS65224_BIT_VMON1_UVOV_STAT BIT(5)
+#define TPS65224_BIT_VMON2_UVOV_STAT BIT(6)
+
/* STAT_STARTUP register field definition */
+#define TPS65224_BIT_VSENSE_STAT BIT(0)
#define TPS6594_BIT_ENABLE_STAT BIT(1)
+#define TPS65224_BIT_PB_LEVEL_STAT BIT(2)
/* STAT_MISC register field definition */
#define TPS6594_BIT_EXT_CLK_STAT BIT(1)
@@ -549,6 +664,7 @@ enum pmic_id {
/* STAT_SEVERE_ERR register field definition */
#define TPS6594_BIT_TSD_IMM_STAT BIT(0)
#define TPS6594_BIT_VCCA_OVP_STAT BIT(1)
+#define TPS65224_BIT_BG_XMON_STAT BIT(3)
/* STAT_READBACK_ERR register field definition */
#define TPS6594_BIT_EN_DRV_READBACK_STAT BIT(0)
@@ -597,6 +713,8 @@ enum pmic_id {
#define TPS6594_BIT_BB_CHARGER_EN BIT(0)
#define TPS6594_BIT_BB_ICHR BIT(1)
#define TPS6594_MASK_BB_VEOC GENMASK(3, 2)
+#define TPS65224_BIT_I2C1_SPI_CRC_EN BIT(4)
+#define TPS65224_BIT_I2C2_CRC_EN BIT(5)
#define TPS6594_BB_EOC_RDY BIT(7)
/* ENABLE_DRV_REG register field definition */
@@ -617,6 +735,7 @@ enum pmic_id {
#define TPS6594_BIT_NRSTOUT_SOC_IN BIT(2)
#define TPS6594_BIT_FORCE_EN_DRV_LOW BIT(3)
#define TPS6594_BIT_SPMI_LPM_EN BIT(4)
+#define TPS65224_BIT_TSD_DISABLE BIT(5)
/* RECOV_CNT_REG_1 register field definition */
#define TPS6594_MASK_RECOV_CNT GENMASK(3, 0)
@@ -671,15 +790,27 @@ enum pmic_id {
/* ESM_SOC_START_REG register field definition */
#define TPS6594_BIT_ESM_SOC_START BIT(0)
+/* ESM_MCU_START_REG register field definition */
+#define TPS65224_BIT_ESM_MCU_START BIT(0)
+
/* ESM_SOC_MODE_CFG register field definition */
#define TPS6594_MASK_ESM_SOC_ERR_CNT_TH GENMASK(3, 0)
#define TPS6594_BIT_ESM_SOC_ENDRV BIT(5)
#define TPS6594_BIT_ESM_SOC_EN BIT(6)
#define TPS6594_BIT_ESM_SOC_MODE BIT(7)
+/* ESM_MCU_MODE_CFG register field definition */
+#define TPS65224_MASK_ESM_MCU_ERR_CNT_TH GENMASK(3, 0)
+#define TPS65224_BIT_ESM_MCU_ENDRV BIT(5)
+#define TPS65224_BIT_ESM_MCU_EN BIT(6)
+#define TPS65224_BIT_ESM_MCU_MODE BIT(7)
+
/* ESM_SOC_ERR_CNT_REG register field definition */
#define TPS6594_MASK_ESM_SOC_ERR_CNT GENMASK(4, 0)
+/* ESM_MCU_ERR_CNT_REG register field definition */
+#define TPS6594_MASK_ESM_MCU_ERR_CNT GENMASK(4, 0)
+
/* REGISTER_LOCK register field definition */
#define TPS6594_BIT_REGISTER_LOCK_STATUS BIT(0)
@@ -687,6 +818,29 @@ enum pmic_id {
#define TPS6594_MASK_VMON1_SLEW_RATE GENMASK(2, 0)
#define TPS6594_MASK_VMON2_SLEW_RATE GENMASK(5, 3)
+/* SRAM_ACCESS_1 Register field definition */
+#define TPS65224_MASk_SRAM_UNLOCK_SEQ GENMASK(7, 0)
+
+/* SRAM_ACCESS_2 Register field definition */
+#define TPS65224_BIT_SRAM_WRITE_MODE BIT(0)
+#define TPS65224_BIT_OTP_PROG_USER BIT(1)
+#define TPS65224_BIT_OTP_PROG_PFSM BIT(2)
+#define TPS65224_BIT_OTP_PROG_STATUS BIT(3)
+#define TPS65224_BIT_SRAM_UNLOCKED BIT(6)
+#define TPS65224_USER_PROG_ALLOWED BIT(7)
+
+/* SRAM_ADDR_CTRL Register field definition */
+#define TPS65224_MASk_SRAM_SEL GENMASK(1, 0)
+
+/* RECOV_CNT_PFSM_INCR Register field definition */
+#define TPS65224_BIT_INCREMENT_RECOV_CNT BIT(0)
+
+/* MANUFACTURING_VER Register field definition */
+#define TPS65224_MASK_SILICON_REV GENMASK(7, 0)
+
+/* CUSTOMER_NVM_ID_REG Register field definition */
+#define TPS65224_MASK_CUSTOMER_NVM_ID GENMASK(7, 0)
+
/* SOFT_REBOOT_REG register field definition */
#define TPS6594_BIT_SOFT_REBOOT BIT(0)
@@ -755,14 +909,83 @@ enum pmic_id {
#define TPS6594_BIT_I2C2_CRC_EN BIT(2)
#define TPS6594_MASK_T_CRC GENMASK(7, 3)
+/* ADC_CTRL Register field definition */
+#define TPS65224_BIT_ADC_START BIT(0)
+#define TPS65224_BIT_ADC_CONT_CONV BIT(1)
+#define TPS65224_BIT_ADC_THERMAL_SEL BIT(2)
+#define TPS65224_BIT_ADC_RDIV_EN BIT(3)
+#define TPS65224_BIT_ADC_STATUS BIT(7)
+
+/* ADC_RESULT_REG_1 Register field definition */
+#define TPS65224_MASK_ADC_RESULT_11_4 GENMASK(7, 0)
+
+/* ADC_RESULT_REG_2 Register field definition */
+#define TPS65224_MASK_ADC_RESULT_3_0 GENMASK(7, 4)
+
+/* STARTUP_CTRL Register field definition */
+#define TPS65224_MASK_STARTUP_DEST GENMASK(6, 5)
+#define TPS65224_BIT_FIRST_STARTUP_DONE BIT(7)
+
+/* SCRATCH_PAD_REG_1 Register field definition */
+#define TPS6594_MASK_SCRATCH_PAD_1 GENMASK(7, 0)
+
+/* SCRATCH_PAD_REG_2 Register field definition */
+#define TPS6594_MASK_SCRATCH_PAD_2 GENMASK(7, 0)
+
+/* SCRATCH_PAD_REG_3 Register field definition */
+#define TPS6594_MASK_SCRATCH_PAD_3 GENMASK(7, 0)
+
+/* SCRATCH_PAD_REG_4 Register field definition */
+#define TPS6594_MASK_SCRATCH_PAD_4 GENMASK(7, 0)
+
+/* PFSM_DELAY_REG_1 Register field definition */
+#define TPS6594_MASK_PFSM_DELAY1 GENMASK(7, 0)
+
+/* PFSM_DELAY_REG_2 Register field definition */
+#define TPS6594_MASK_PFSM_DELAY2 GENMASK(7, 0)
+
+/* PFSM_DELAY_REG_3 Register field definition */
+#define TPS6594_MASK_PFSM_DELAY3 GENMASK(7, 0)
+
+/* PFSM_DELAY_REG_4 Register field definition */
+#define TPS6594_MASK_PFSM_DELAY4 GENMASK(7, 0)
+
+/* CRC_CALC_CONTROL Register field definition */
+#define TPS65224_BIT_RUN_CRC_BIST BIT(0)
+#define TPS65224_BIT_RUN_CRC_UPDATE BIT(1)
+
+/* ADC_GAIN_COMP_REG Register field definition */
+#define TPS65224_MASK_ADC_GAIN_COMP GENMASK(7, 0)
+
+/* REGMAP_USER_CRC_LOW Register field definition */
+#define TPS65224_MASK_REGMAP_USER_CRC16_LOW GENMASK(7, 0)
+
+/* REGMAP_USER_CRC_HIGH Register field definition */
+#define TPS65224_MASK_REGMAP_USER_CRC16_HIGH GENMASK(7, 0)
+
+/* WD_ANSWER_REG Register field definition */
+#define TPS6594_MASK_WD_ANSWER GENMASK(7, 0)
+
/* WD_QUESTION_ANSW_CNT register field definition */
#define TPS6594_MASK_WD_QUESTION GENMASK(3, 0)
#define TPS6594_MASK_WD_ANSW_CNT GENMASK(5, 4)
+#define TPS65224_BIT_INT_TOP_STATUS BIT(7)
+
+/* WD WIN1_CFG register field definition */
+#define TPS6594_MASK_WD_WIN1_CFG GENMASK(6, 0)
+
+/* WD WIN2_CFG register field definition */
+#define TPS6594_MASK_WD_WIN2_CFG GENMASK(6, 0)
+
+/* WD LongWin register field definition */
+#define TPS6594_MASK_WD_LONGWIN_CFG GENMASK(7, 0)
/* WD_MODE_REG register field definition */
#define TPS6594_BIT_WD_RETURN_LONGWIN BIT(0)
#define TPS6594_BIT_WD_MODE_SELECT BIT(1)
#define TPS6594_BIT_WD_PWRHOLD BIT(2)
+#define TPS65224_BIT_WD_ENDRV_SEL BIT(6)
+#define TPS65224_BIT_WD_CNT_SEL BIT(7)
/* WD_QA_CFG register field definition */
#define TPS6594_MASK_WD_QUESTION_SEED GENMASK(3, 0)
@@ -993,6 +1216,106 @@ enum tps6594_irqs {
#define TPS6594_IRQ_NAME_ALARM "alarm"
#define TPS6594_IRQ_NAME_POWERUP "powerup"
+/* IRQs */
+enum tps65224_irqs {
+ /* INT_BUCK register */
+ TPS65224_IRQ_BUCK1_UVOV,
+ TPS65224_IRQ_BUCK2_UVOV,
+ TPS65224_IRQ_BUCK3_UVOV,
+ TPS65224_IRQ_BUCK4_UVOV,
+ /* INT_LDO_VMON register */
+ TPS65224_IRQ_LDO1_UVOV,
+ TPS65224_IRQ_LDO2_UVOV,
+ TPS65224_IRQ_LDO3_UVOV,
+ TPS65224_IRQ_VCCA_UVOV,
+ TPS65224_IRQ_VMON1_UVOV,
+ TPS65224_IRQ_VMON2_UVOV,
+ /* INT_GPIO register */
+ TPS65224_IRQ_GPIO1,
+ TPS65224_IRQ_GPIO2,
+ TPS65224_IRQ_GPIO3,
+ TPS65224_IRQ_GPIO4,
+ TPS65224_IRQ_GPIO5,
+ TPS65224_IRQ_GPIO6,
+ /* INT_STARTUP register */
+ TPS65224_IRQ_VSENSE,
+ TPS65224_IRQ_ENABLE,
+ TPS65224_IRQ_PB_SHORT,
+ TPS65224_IRQ_FSD,
+ TPS65224_IRQ_SOFT_REBOOT,
+ /* INT_MISC register */
+ TPS65224_IRQ_BIST_PASS,
+ TPS65224_IRQ_EXT_CLK,
+ TPS65224_IRQ_REG_UNLOCK,
+ TPS65224_IRQ_TWARN,
+ TPS65224_IRQ_PB_LONG,
+ TPS65224_IRQ_PB_FALL,
+ TPS65224_IRQ_PB_RISE,
+ TPS65224_IRQ_ADC_CONV_READY,
+ /* INT_MODERATE_ERR register */
+ TPS65224_IRQ_TSD_ORD,
+ TPS65224_IRQ_BIST_FAIL,
+ TPS65224_IRQ_REG_CRC_ERR,
+ TPS65224_IRQ_RECOV_CNT,
+ /* INT_SEVERE_ERR register */
+ TPS65224_IRQ_TSD_IMM,
+ TPS65224_IRQ_VCCA_OVP,
+ TPS65224_IRQ_PFSM_ERR,
+ TPS65224_IRQ_BG_XMON,
+ /* INT_FSM_ERR register */
+ TPS65224_IRQ_IMM_SHUTDOWN,
+ TPS65224_IRQ_ORD_SHUTDOWN,
+ TPS65224_IRQ_MCU_PWR_ERR,
+ TPS65224_IRQ_SOC_PWR_ERR,
+ TPS65224_IRQ_COMM_ERR,
+ TPS65224_IRQ_I2C2_ERR,
+};
+
+#define TPS65224_IRQ_NAME_BUCK1_UVOV "buck1_uvov"
+#define TPS65224_IRQ_NAME_BUCK2_UVOV "buck2_uvov"
+#define TPS65224_IRQ_NAME_BUCK3_UVOV "buck3_uvov"
+#define TPS65224_IRQ_NAME_BUCK4_UVOV "buck4_uvov"
+#define TPS65224_IRQ_NAME_LDO1_UVOV "ldo1_uvov"
+#define TPS65224_IRQ_NAME_LDO2_UVOV "ldo2_uvov"
+#define TPS65224_IRQ_NAME_LDO3_UVOV "ldo3_uvov"
+#define TPS65224_IRQ_NAME_VCCA_UVOV "vcca_uvov"
+#define TPS65224_IRQ_NAME_VMON1_UVOV "vmon1_uvov"
+#define TPS65224_IRQ_NAME_VMON2_UVOV "vmon2_uvov"
+#define TPS65224_IRQ_NAME_GPIO1 "gpio1"
+#define TPS65224_IRQ_NAME_GPIO2 "gpio2"
+#define TPS65224_IRQ_NAME_GPIO3 "gpio3"
+#define TPS65224_IRQ_NAME_GPIO4 "gpio4"
+#define TPS65224_IRQ_NAME_GPIO5 "gpio5"
+#define TPS65224_IRQ_NAME_GPIO6 "gpio6"
+#define TPS65224_IRQ_NAME_VSENSE "vsense"
+#define TPS65224_IRQ_NAME_ENABLE "enable"
+#define TPS65224_IRQ_NAME_PB_SHORT "pb_short"
+#define TPS65224_IRQ_NAME_FSD "fsd"
+#define TPS65224_IRQ_NAME_SOFT_REBOOT "soft_reboot"
+#define TPS65224_IRQ_NAME_BIST_PASS "bist_pass"
+#define TPS65224_IRQ_NAME_EXT_CLK "ext_clk"
+#define TPS65224_IRQ_NAME_REG_UNLOCK "reg_unlock"
+#define TPS65224_IRQ_NAME_TWARN "twarn"
+#define TPS65224_IRQ_NAME_PB_LONG "pb_long"
+#define TPS65224_IRQ_NAME_PB_FALL "pb_fall"
+#define TPS65224_IRQ_NAME_PB_RISE "pb_rise"
+#define TPS65224_IRQ_NAME_ADC_CONV_READY "adc_conv_ready"
+#define TPS65224_IRQ_NAME_TSD_ORD "tsd_ord"
+#define TPS65224_IRQ_NAME_BIST_FAIL "bist_fail"
+#define TPS65224_IRQ_NAME_REG_CRC_ERR "reg_crc_err"
+#define TPS65224_IRQ_NAME_RECOV_CNT "recov_cnt"
+#define TPS65224_IRQ_NAME_TSD_IMM "tsd_imm"
+#define TPS65224_IRQ_NAME_VCCA_OVP "vcca_ovp"
+#define TPS65224_IRQ_NAME_PFSM_ERR "pfsm_err"
+#define TPS65224_IRQ_NAME_BG_XMON "bg_xmon"
+#define TPS65224_IRQ_NAME_IMM_SHUTDOWN "imm_shutdown"
+#define TPS65224_IRQ_NAME_ORD_SHUTDOWN "ord_shutdown"
+#define TPS65224_IRQ_NAME_MCU_PWR_ERR "mcu_pwr_err"
+#define TPS65224_IRQ_NAME_SOC_PWR_ERR "soc_pwr_err"
+#define TPS65224_IRQ_NAME_COMM_ERR "comm_err"
+#define TPS65224_IRQ_NAME_I2C2_ERR "i2c2_err"
+#define TPS65224_IRQ_NAME_POWERUP "powerup"
+
/**
* struct tps6594 - device private data structure
*
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eefbe40295b1..b6e63bcfff30 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1307,6 +1307,9 @@ struct netdev_net_notifier {
* int (*ndo_mdb_del)(struct net_device *dev, struct nlattr *tb[],
* struct netlink_ext_ack *extack);
* Deletes the MDB entry from dev.
+ * int (*ndo_mdb_del_bulk)(struct net_device *dev, struct nlattr *tb[],
+ * struct netlink_ext_ack *extack);
+ * Bulk deletes MDB entries from dev.
* int (*ndo_mdb_dump)(struct net_device *dev, struct sk_buff *skb,
* struct netlink_callback *cb);
* Dumps MDB entries from dev. The first argument (marker) in the netlink
@@ -1591,9 +1594,16 @@ struct net_device_ops {
int (*ndo_mdb_del)(struct net_device *dev,
struct nlattr *tb[],
struct netlink_ext_ack *extack);
+ int (*ndo_mdb_del_bulk)(struct net_device *dev,
+ struct nlattr *tb[],
+ struct netlink_ext_ack *extack);
int (*ndo_mdb_dump)(struct net_device *dev,
struct sk_buff *skb,
struct netlink_callback *cb);
+ int (*ndo_mdb_get)(struct net_device *dev,
+ struct nlattr *tb[], u32 portid,
+ u32 seq,
+ struct netlink_ext_ack *extack);
int (*ndo_bridge_setlink)(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags,
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index e2a99bf46cdf..4259917885c1 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -348,7 +348,7 @@ struct nfs4_layoutreturn {
struct nfs4_xdr_opaque_data ld_private;
};
-#define PNFS_LAYOUTSTATS_MAXSIZE 256
+#define PNFS_LAYOUTSTATS_MAXSIZE 384
struct nfs42_layoutstat_args;
struct nfs42_layoutstat_devinfo;
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 8e4906c26593..815850270be5 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -2024,4 +2024,85 @@ struct nvme_completion {
#define NVME_MINOR(ver) (((ver) >> 8) & 0xff)
#define NVME_TERTIARY(ver) ((ver) & 0xff)
+enum {
+ NVME_AEN_RESV_LOG_PAGE_AVALIABLE = 0x00,
+};
+
+enum {
+ NVME_PR_LOG_EMPTY_LOG_PAGE = 0x00,
+ NVME_PR_LOG_REGISTRATION_PREEMPTED = 0x01,
+ NVME_PR_LOG_RESERVATION_RELEASED = 0x02,
+ NVME_PR_LOG_RESERVATOPM_PREEMPTED = 0x03,
+};
+
+enum {
+ NVME_PR_NOTIFY_BIT_REG_PREEMPTED = 1,
+ NVME_PR_NOTIFY_BIT_RESV_RELEASED = 2,
+ NVME_PR_NOTIFY_BIT_RESV_PREEMPTED = 3,
+};
+
+struct nvme_pr_log {
+ __le64 count;
+ __u8 type;
+ __u8 nr_pages;
+ __u8 rsvd1[2];
+ __le32 nsid;
+ __u8 rsvd2[48];
+};
+
+struct nvmet_pr_register_data {
+ __le64 crkey;
+ __le64 nrkey;
+};
+
+struct nvmet_pr_acquire_data {
+ __le64 crkey;
+ __le64 prkey;
+};
+
+struct nvmet_pr_release_data {
+ __le64 crkey;
+};
+
+enum nvme_pr_capabilities {
+ NVME_PR_SUPPORT_PTPL = 1,
+ NVME_PR_SUPPORT_WRITE_EXCLUSIVE = 1 << 1,
+ NVME_PR_SUPPORT_EXCLUSIVE_ACCESS = 1 << 2,
+ NVME_PR_SUPPORT_WRITE_EXCLUSIVE_REG_ONLY = 1 << 3,
+ NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_REG_ONLY = 1 << 4,
+ NVME_PR_SUPPORT_WRITE_EXCLUSIVE_ALL_REGS = 1 << 5,
+ NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_ALL_REGS = 1 << 6,
+ NVME_PR_SUPPORT_IEKEY_VER_1_3_DEF = 1 << 7,
+};
+
+enum nvme_pr_register_action {
+ NVME_PR_REGISTER_ACT_REG = 0,
+ NVME_PR_REGISTER_ACT_UNREG = 1,
+ NVME_PR_REGISTER_ACT_REPLACE = 1 << 1,
+};
+
+enum nvme_pr_acquire_action {
+ NVME_PR_ACQUIRE_ACT_ACQUIRE = 0,
+ NVME_PR_ACQUIRE_ACT_PREEMPT = 1,
+ NVME_PR_ACQUIRE_ACT_PREEMPT_AND_ABORT = 1 << 1,
+};
+
+enum nvme_pr_release_action {
+ NVME_PR_RELEASE_ACT_RELEASE = 0,
+ NVME_PR_RELEASE_ACT_CLEAR = 1,
+};
+
+enum nvme_pr_change_ptpl {
+ NVME_PR_CPTPL_NO_CHANGE = 0,
+ NVME_PR_CPTPL_RESV = 1 << 30,
+ NVME_PR_CPTPL_CLEARED = 2 << 30,
+ NVME_PR_CPTPL_PERSIST = 3 << 30,
+};
+
+#define NVME_PR_IGNORE_KEY (1 << 3)
+
+/* Section 8.3.4.5.2 of the NVMe 2.1 */
+#define NVME_AUTH_DHCHAP_MAX_HASH_IDS 30
+#define NVME_AUTH_DHCHAP_MAX_DH_IDS 30
+
#endif /* _LINUX_NVME_H */
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 4d7756087b6b..a692d9d979a6 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -11,6 +11,7 @@
#ifndef __LINUX_OF_GRAPH_H
#define __LINUX_OF_GRAPH_H
+#include <linux/cleanup.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -37,14 +38,43 @@ struct of_endpoint {
for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
child = of_graph_get_next_endpoint(parent, child))
+/**
+ * for_each_of_graph_port - iterate over every port in a device or ports node
+ * @parent: parent device or ports node containing port
+ * @child: loop variable pointing to the current port node
+ *
+ * When breaking out of the loop, and continue to use the @child, you need to
+ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
+ */
+#define for_each_of_graph_port(parent, child) \
+ for (struct device_node *child __free(device_node) = of_graph_get_next_port(parent, NULL);\
+ child != NULL; child = of_graph_get_next_port(parent, child))
+
+/**
+ * for_each_of_graph_port_endpoint - iterate over every endpoint in a port node
+ * @parent: parent port node
+ * @child: loop variable pointing to the current endpoint node
+ *
+ * When breaking out of the loop, and continue to use the @child, you need to
+ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
+ */
+#define for_each_of_graph_port_endpoint(parent, child) \
+ for (struct device_node *child __free(device_node) = of_graph_get_next_port_endpoint(parent, NULL);\
+ child != NULL; child = of_graph_get_next_port_endpoint(parent, child))
+
#ifdef CONFIG_OF
bool of_graph_is_present(const struct device_node *node);
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint);
-int of_graph_get_endpoint_count(const struct device_node *np);
+unsigned int of_graph_get_endpoint_count(const struct device_node *np);
+unsigned int of_graph_get_port_count(struct device_node *np);
struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *previous);
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+ struct device_node *port);
+struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port,
+ struct device_node *prev);
struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg);
struct device_node *of_graph_get_remote_endpoint(
@@ -68,7 +98,12 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
return -ENOSYS;
}
-static inline int of_graph_get_endpoint_count(const struct device_node *np)
+static inline unsigned int of_graph_get_endpoint_count(const struct device_node *np)
+{
+ return 0;
+}
+
+static inline unsigned int of_graph_get_port_count(struct device_node *np)
{
return 0;
}
@@ -86,6 +121,20 @@ static inline struct device_node *of_graph_get_next_endpoint(
return NULL;
}
+static inline struct device_node *of_graph_get_next_port(
+ const struct device_node *parent,
+ struct device_node *previous)
+{
+ return NULL;
+}
+
+static inline struct device_node *of_graph_get_next_port_endpoint(
+ const struct device_node *parent,
+ struct device_node *previous)
+{
+ return NULL;
+}
+
static inline struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg)
{
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index 7b949292908a..e0a3a12380e5 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -158,7 +158,7 @@ void rcu_scheduler_starting(void);
static inline void rcu_end_inkernel_boot(void) { }
static inline bool rcu_inkernel_boot_has_ended(void) { return true; }
static inline bool rcu_is_watching(void) { return true; }
-static inline void rcu_momentary_dyntick_idle(void) { }
+static inline void rcu_momentary_eqs(void) { }
static inline void kfree_rcu_scheduler_running(void) { }
static inline bool rcu_gp_might_be_stalled(void) { return false; }
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
index 559f758bf2ea..50d48af37abc 100644
--- a/include/linux/rcutree.h
+++ b/include/linux/rcutree.h
@@ -37,8 +37,7 @@ void synchronize_rcu_expedited(void);
void kvfree_call_rcu(struct rcu_head *head, void *ptr);
void rcu_barrier(void);
-bool rcu_eqs_special_set(int cpu);
-void rcu_momentary_dyntick_idle(void);
+void rcu_momentary_eqs(void);
void kfree_rcu_scheduler_running(void);
bool rcu_gp_might_be_stalled(void);
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index b4795698d8c2..de98462d5888 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -37,279 +37,13 @@
#include <linux/types.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/idr.h>
#include <linux/of.h>
-
-/**
- * struct resource_table - firmware resource table header
- * @ver: version number
- * @num: number of resource entries
- * @reserved: reserved (must be zero)
- * @offset: array of offsets pointing at the various resource entries
- *
- * A resource table is essentially a list of system resources required
- * by the remote processor. It may also include configuration entries.
- * If needed, the remote processor firmware should contain this table
- * as a dedicated ".resource_table" ELF section.
- *
- * Some resources entries are mere announcements, where the host is informed
- * of specific remoteproc configuration. Other entries require the host to
- * do something (e.g. allocate a system resource). Sometimes a negotiation
- * is expected, where the firmware requests a resource, and once allocated,
- * the host should provide back its details (e.g. address of an allocated
- * memory region).
- *
- * The header of the resource table, as expressed by this structure,
- * contains a version number (should we need to change this format in the
- * future), the number of available resource entries, and their offsets
- * in the table.
- *
- * Immediately following this header are the resource entries themselves,
- * each of which begins with a resource entry header (as described below).
- */
-struct resource_table {
- u32 ver;
- u32 num;
- u32 reserved[2];
- u32 offset[];
-} __packed;
-
-/**
- * struct fw_rsc_hdr - firmware resource entry header
- * @type: resource type
- * @data: resource data
- *
- * Every resource entry begins with a 'struct fw_rsc_hdr' header providing
- * its @type. The content of the entry itself will immediately follow
- * this header, and it should be parsed according to the resource type.
- */
-struct fw_rsc_hdr {
- u32 type;
- u8 data[];
-} __packed;
-
-/**
- * enum fw_resource_type - types of resource entries
- *
- * @RSC_CARVEOUT: request for allocation of a physically contiguous
- * memory region.
- * @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
- * @RSC_TRACE: announces the availability of a trace buffer into which
- * the remote processor will be writing logs.
- * @RSC_VDEV: declare support for a virtio device, and serve as its
- * virtio header.
- * @RSC_LAST: just keep this one at the end of standard resources
- * @RSC_VENDOR_START: start of the vendor specific resource types range
- * @RSC_VENDOR_END: end of the vendor specific resource types range
- *
- * For more details regarding a specific resource type, please see its
- * dedicated structure below.
- *
- * Please note that these values are used as indices to the rproc_handle_rsc
- * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
- * check the validity of an index before the lookup table is accessed, so
- * please update it as needed.
- */
-enum fw_resource_type {
- RSC_CARVEOUT = 0,
- RSC_DEVMEM = 1,
- RSC_TRACE = 2,
- RSC_VDEV = 3,
- RSC_LAST = 4,
- RSC_VENDOR_START = 128,
- RSC_VENDOR_END = 512,
-};
-
-#define FW_RSC_ADDR_ANY (-1)
-
-/**
- * struct fw_rsc_carveout - physically contiguous memory request
- * @da: device address
- * @pa: physical address
- * @len: length (in bytes)
- * @flags: iommu protection flags
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the requested memory region
- *
- * This resource entry requests the host to allocate a physically contiguous
- * memory region.
- *
- * These request entries should precede other firmware resource entries,
- * as other entries might request placing other data objects inside
- * these memory regions (e.g. data/code segments, trace resource entries, ...).
- *
- * Allocating memory this way helps utilizing the reserved physical memory
- * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
- * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
- * pressure is important; it may have a substantial impact on performance.
- *
- * If the firmware is compiled with static addresses, then @da should specify
- * the expected device address of this memory region. If @da is set to
- * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
- * overwrite @da with the dynamically allocated address.
- *
- * We will always use @da to negotiate the device addresses, even if it
- * isn't using an iommu. In that case, though, it will obviously contain
- * physical addresses.
- *
- * Some remote processors needs to know the allocated physical address
- * even if they do use an iommu. This is needed, e.g., if they control
- * hardware accelerators which access the physical memory directly (this
- * is the case with OMAP4 for instance). In that case, the host will
- * overwrite @pa with the dynamically allocated physical address.
- * Generally we don't want to expose physical addresses if we don't have to
- * (remote processors are generally _not_ trusted), so we might want to
- * change this to happen _only_ when explicitly required by the hardware.
- *
- * @flags is used to provide IOMMU protection flags, and @name should
- * (optionally) contain a human readable name of this carveout region
- * (mainly for debugging purposes).
- */
-struct fw_rsc_carveout {
- u32 da;
- u32 pa;
- u32 len;
- u32 flags;
- u32 reserved;
- u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_devmem - iommu mapping request
- * @da: device address
- * @pa: physical address
- * @len: length (in bytes)
- * @flags: iommu protection flags
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the requested region to be mapped
- *
- * This resource entry requests the host to iommu map a physically contiguous
- * memory region. This is needed in case the remote processor requires
- * access to certain memory-based peripherals; _never_ use it to access
- * regular memory.
- *
- * This is obviously only needed if the remote processor is accessing memory
- * via an iommu.
- *
- * @da should specify the required device address, @pa should specify
- * the physical address we want to map, @len should specify the size of
- * the mapping and @flags is the IOMMU protection flags. As always, @name may
- * (optionally) contain a human readable name of this mapping (mainly for
- * debugging purposes).
- *
- * Note: at this point we just "trust" those devmem entries to contain valid
- * physical addresses, but this isn't safe and will be changed: eventually we
- * want remoteproc implementations to provide us ranges of physical addresses
- * the firmware is allowed to request, and not allow firmwares to request
- * access to physical addresses that are outside those ranges.
- */
-struct fw_rsc_devmem {
- u32 da;
- u32 pa;
- u32 len;
- u32 flags;
- u32 reserved;
- u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_trace - trace buffer declaration
- * @da: device address
- * @len: length (in bytes)
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the trace buffer
- *
- * This resource entry provides the host information about a trace buffer
- * into which the remote processor will write log messages.
- *
- * @da specifies the device address of the buffer, @len specifies
- * its size, and @name may contain a human readable name of the trace buffer.
- *
- * After booting the remote processor, the trace buffers are exposed to the
- * user via debugfs entries (called trace0, trace1, etc..).
- */
-struct fw_rsc_trace {
- u32 da;
- u32 len;
- u32 reserved;
- u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_vdev_vring - vring descriptor entry
- * @da: device address
- * @align: the alignment between the consumer and producer parts of the vring
- * @num: num of buffers supported by this vring (must be power of two)
- * @notifyid: a unique rproc-wide notify index for this vring. This notify
- * index is used when kicking a remote processor, to let it know that this
- * vring is triggered.
- * @pa: physical address
- *
- * This descriptor is not a resource entry by itself; it is part of the
- * vdev resource type (see below).
- *
- * Note that @da should either contain the device address where
- * the remote processor is expecting the vring, or indicate that
- * dynamically allocation of the vring's device address is supported.
- */
-struct fw_rsc_vdev_vring {
- u32 da;
- u32 align;
- u32 num;
- u32 notifyid;
- u32 pa;
-} __packed;
-
-/**
- * struct fw_rsc_vdev - virtio device header
- * @id: virtio device id (as in virtio_ids.h)
- * @notifyid: a unique rproc-wide notify index for this vdev. This notify
- * index is used when kicking a remote processor, to let it know that the
- * status/features of this vdev have changes.
- * @dfeatures: specifies the virtio device features supported by the firmware
- * @gfeatures: a place holder used by the host to write back the
- * negotiated features that are supported by both sides.
- * @config_len: the size of the virtio config space of this vdev. The config
- * space lies in the resource table immediate after this vdev header.
- * @status: a place holder where the host will indicate its virtio progress.
- * @num_of_vrings: indicates how many vrings are described in this vdev header
- * @reserved: reserved (must be zero)
- * @vring: an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
- *
- * This resource is a virtio device header: it provides information about
- * the vdev, and is then used by the host and its peer remote processors
- * to negotiate and share certain virtio properties.
- *
- * By providing this resource entry, the firmware essentially asks remoteproc
- * to statically allocate a vdev upon registration of the rproc (dynamic vdev
- * allocation is not yet supported).
- *
- * Note:
- * 1. unlike virtualization systems, the term 'host' here means
- * the Linux side which is running remoteproc to control the remote
- * processors. We use the name 'gfeatures' to comply with virtio's terms,
- * though there isn't really any virtualized guest OS here: it's the host
- * which is responsible for negotiating the final features.
- * Yeah, it's a bit confusing.
- *
- * 2. immediately following this structure is the virtio config space for
- * this vdev (which is specific to the vdev; for more info, read the virtio
- * spec). The size of the config space is specified by @config_len.
- */
-struct fw_rsc_vdev {
- u32 id;
- u32 notifyid;
- u32 dfeatures;
- u32 gfeatures;
- u32 config_len;
- u8 status;
- u8 num_of_vrings;
- u8 reserved[2];
- struct fw_rsc_vdev_vring vring[];
-} __packed;
+#include <linux/rsc_table.h>
struct rproc;
@@ -412,7 +146,6 @@ struct rproc_ops {
* a message.
* @RPROC_RUNNING: device is up and running
* @RPROC_CRASHED: device has crashed; need to start recovery
- * @RPROC_DELETED: device is deleted
* @RPROC_ATTACHED: device has been booted by another entity and the core
* has attached to it
* @RPROC_DETACHED: device has been booted by another entity and waiting
@@ -430,10 +163,9 @@ enum rproc_state {
RPROC_SUSPENDED = 1,
RPROC_RUNNING = 2,
RPROC_CRASHED = 3,
- RPROC_DELETED = 4,
- RPROC_ATTACHED = 5,
- RPROC_DETACHED = 6,
- RPROC_LAST = 7,
+ RPROC_ATTACHED = 4,
+ RPROC_DETACHED = 5,
+ RPROC_LAST = 6,
};
/**
@@ -526,7 +258,10 @@ enum rproc_features {
* @subdevs: list of subdevices, to following the running state
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
* @index: index of this rproc device
+ * @attach_work: workqueue for attaching rproc
* @crash_handler: workqueue for handling a crash
+ * @crash_handler_lock: serializes crash handler queueing and deletion
+ * @deleting: remoteproc deletion has begun
* @crash_cnt: crash counter
* @recovery_disabled: flag that state if recovery was disabled
* @max_notifyid: largest allocated notify id.
@@ -568,7 +303,10 @@ struct rproc {
struct list_head subdevs;
struct idr notifyids;
int index;
+ struct work_struct attach_work;
struct work_struct crash_handler;
+ spinlock_t crash_handler_lock;
+ bool deleting;
unsigned int crash_cnt;
bool recovery_disabled;
int max_notifyid;
diff --git a/include/linux/rsc_table.h b/include/linux/rsc_table.h
new file mode 100644
index 000000000000..4cef11a2e3a2
--- /dev/null
+++ b/include/linux/rsc_table.h
@@ -0,0 +1,364 @@
+/*
+ * Resource table and its types data structure
+ *
+ * Copyright(c) 2011 Texas Instruments, Inc.
+ * Copyright(c) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RSC_TABLE_H
+#define RSC_TABLE_H
+
+/**
+ * struct resource_table - firmware resource table header
+ * @ver: version number
+ * @num: number of resource entries
+ * @reserved: reserved (must be zero)
+ * @offset: array of offsets pointing at the various resource entries
+ *
+ * A resource table is essentially a list of system resources required
+ * by the remote processor. It may also include configuration entries.
+ * If needed, the remote processor firmware should contain this table
+ * as a dedicated ".resource_table" ELF section.
+ *
+ * Some resources entries are mere announcements, where the host is informed
+ * of specific remoteproc configuration. Other entries require the host to
+ * do something (e.g. allocate a system resource). Sometimes a negotiation
+ * is expected, where the firmware requests a resource, and once allocated,
+ * the host should provide back its details (e.g. address of an allocated
+ * memory region).
+ *
+ * The header of the resource table, as expressed by this structure,
+ * contains a version number (should we need to change this format in the
+ * future), the number of available resource entries, and their offsets
+ * in the table.
+ *
+ * Immediately following this header are the resource entries themselves,
+ * each of which begins with a resource entry header (as described below).
+ */
+struct resource_table {
+ u32 ver;
+ u32 num;
+ u32 reserved[2];
+ u32 offset[];
+} __packed;
+
+/**
+ * struct fw_rsc_hdr - firmware resource entry header
+ * @type: resource type
+ * @data: resource data
+ *
+ * Every resource entry begins with a 'struct fw_rsc_hdr' header providing
+ * its @type. The content of the entry itself will immediately follow
+ * this header, and it should be parsed according to the resource type.
+ */
+struct fw_rsc_hdr {
+ u32 type;
+ u8 data[];
+} __packed;
+
+/**
+ * enum fw_resource_type - types of resource entries
+ *
+ * @RSC_CARVEOUT: request for allocation of a physically contiguous
+ * memory region.
+ * @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
+ * @RSC_TRACE: announces the availability of a trace buffer into which
+ * the remote processor will be writing logs.
+ * @RSC_VDEV: declare support for a virtio device, and serve as its
+ * virtio header.
+ * @RSC_LAST: just keep this one at the end of standard resources
+ * @RSC_VENDOR_START: start of the vendor specific resource types range
+ * @RSC_VENDOR_END: end of the vendor specific resource types range
+ *
+ * For more details regarding a specific resource type, please see its
+ * dedicated structure below.
+ *
+ * Please note that these values are used as indices to the rproc_handle_rsc
+ * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
+ * check the validity of an index before the lookup table is accessed, so
+ * please update it as needed.
+ */
+enum fw_resource_type {
+ RSC_CARVEOUT = 0,
+ RSC_DEVMEM = 1,
+ RSC_TRACE = 2,
+ RSC_VDEV = 3,
+ RSC_LAST = 4,
+ RSC_VENDOR_START = 128,
+ RSC_VENDOR_END = 512,
+};
+
+#define FW_RSC_ADDR_ANY (-1)
+
+/**
+ * struct fw_rsc_carveout - physically contiguous memory request
+ * @da: device address
+ * @pa: physical address
+ * @len: length (in bytes)
+ * @flags: iommu protection flags
+ * @reserved: reserved (must be zero)
+ * @name: human-readable name of the requested memory region
+ *
+ * This resource entry requests the host to allocate a physically contiguous
+ * memory region.
+ *
+ * These request entries should precede other firmware resource entries,
+ * as other entries might request placing other data objects inside
+ * these memory regions (e.g. data/code segments, trace resource entries, ...).
+ *
+ * Allocating memory this way helps utilizing the reserved physical memory
+ * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
+ * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
+ * pressure is important; it may have a substantial impact on performance.
+ *
+ * If the firmware is compiled with static addresses, then @da should specify
+ * the expected device address of this memory region. If @da is set to
+ * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
+ * overwrite @da with the dynamically allocated address.
+ *
+ * We will always use @da to negotiate the device addresses, even if it
+ * isn't using an iommu. In that case, though, it will obviously contain
+ * physical addresses.
+ *
+ * Some remote processors needs to know the allocated physical address
+ * even if they do use an iommu. This is needed, e.g., if they control
+ * hardware accelerators which access the physical memory directly (this
+ * is the case with OMAP4 for instance). In that case, the host will
+ * overwrite @pa with the dynamically allocated physical address.
+ * Generally we don't want to expose physical addresses if we don't have to
+ * (remote processors are generally _not_ trusted), so we might want to
+ * change this to happen _only_ when explicitly required by the hardware.
+ *
+ * @flags is used to provide IOMMU protection flags, and @name should
+ * (optionally) contain a human readable name of this carveout region
+ * (mainly for debugging purposes).
+ */
+struct fw_rsc_carveout {
+ u32 da;
+ u32 pa;
+ u32 len;
+ u32 flags;
+ u32 reserved;
+ u8 name[32];
+} __packed;
+
+/**
+ * struct fw_rsc_devmem - iommu mapping request
+ * @da: device address
+ * @pa: physical address
+ * @len: length (in bytes)
+ * @flags: iommu protection flags
+ * @reserved: reserved (must be zero)
+ * @name: human-readable name of the requested region to be mapped
+ *
+ * This resource entry requests the host to iommu map a physically contiguous
+ * memory region. This is needed in case the remote processor requires
+ * access to certain memory-based peripherals; _never_ use it to access
+ * regular memory.
+ *
+ * This is obviously only needed if the remote processor is accessing memory
+ * via an iommu.
+ *
+ * @da should specify the required device address, @pa should specify
+ * the physical address we want to map, @len should specify the size of
+ * the mapping and @flags is the IOMMU protection flags. As always, @name may
+ * (optionally) contain a human readable name of this mapping (mainly for
+ * debugging purposes).
+ *
+ * Note: at this point we just "trust" those devmem entries to contain valid
+ * physical addresses, but this isn't safe and will be changed: eventually we
+ * want remoteproc implementations to provide us ranges of physical addresses
+ * the firmware is allowed to request, and not allow firmwares to request
+ * access to physical addresses that are outside those ranges.
+ */
+struct fw_rsc_devmem {
+ u32 da;
+ u32 pa;
+ u32 len;
+ u32 flags;
+ u32 reserved;
+ u8 name[32];
+} __packed;
+
+/**
+ * struct fw_rsc_trace - trace buffer declaration
+ * @da: device address
+ * @len: length (in bytes)
+ * @reserved: reserved (must be zero)
+ * @name: human-readable name of the trace buffer
+ *
+ * This resource entry provides the host information about a trace buffer
+ * into which the remote processor will write log messages.
+ *
+ * @da specifies the device address of the buffer, @len specifies
+ * its size, and @name may contain a human readable name of the trace buffer.
+ *
+ * After booting the remote processor, the trace buffers are exposed to the
+ * user via debugfs entries (called trace0, trace1, etc..).
+ */
+struct fw_rsc_trace {
+ u32 da;
+ u32 len;
+ u32 reserved;
+ u8 name[32];
+} __packed;
+
+/**
+ * struct fw_rsc_vdev_vring - vring descriptor entry
+ * @da: device address
+ * @align: the alignment between the consumer and producer parts of the vring
+ * @num: num of buffers supported by this vring (must be power of two)
+ * @notifyid: a unique rproc-wide notify index for this vring. This notify
+ * index is used when kicking a remote processor, to let it know that this
+ * vring is triggered.
+ * @pa: physical address
+ *
+ * This descriptor is not a resource entry by itself; it is part of the
+ * vdev resource type (see below).
+ *
+ * Note that @da should either contain the device address where
+ * the remote processor is expecting the vring, or indicate that
+ * dynamically allocation of the vring's device address is supported.
+ */
+struct fw_rsc_vdev_vring {
+ u32 da;
+ u32 align;
+ u32 num;
+ u32 notifyid;
+ u32 pa;
+} __packed;
+
+/**
+ * struct fw_rsc_vdev - virtio device header
+ * @id: virtio device id (as in virtio_ids.h)
+ * @notifyid: a unique rproc-wide notify index for this vdev. This notify
+ * index is used when kicking a remote processor, to let it know that the
+ * status/features of this vdev have changes.
+ * @dfeatures: specifies the virtio device features supported by the firmware
+ * @gfeatures: a place holder used by the host to write back the
+ * negotiated features that are supported by both sides.
+ * @config_len: the size of the virtio config space of this vdev. The config
+ * space lies in the resource table immediate after this vdev header.
+ * @status: a place holder where the host will indicate its virtio progress.
+ * @num_of_vrings: indicates how many vrings are described in this vdev header
+ * @reserved: reserved (must be zero)
+ * @vring: an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
+ *
+ * This resource is a virtio device header: it provides information about
+ * the vdev, and is then used by the host and its peer remote processors
+ * to negotiate and share certain virtio properties.
+ *
+ * By providing this resource entry, the firmware essentially asks remoteproc
+ * to statically allocate a vdev upon registration of the rproc (dynamic vdev
+ * allocation is not yet supported).
+ *
+ * Note:
+ * 1. unlike virtualization systems, the term 'host' here means
+ * the Linux side which is running remoteproc to control the remote
+ * processors. We use the name 'gfeatures' to comply with virtio's terms,
+ * though there isn't really any virtualized guest OS here: it's the host
+ * which is responsible for negotiating the final features.
+ * Yeah, it's a bit confusing.
+ *
+ * 2. immediately following this structure is the virtio config space for
+ * this vdev (which is specific to the vdev; for more info, read the virtio
+ * spec). The size of the config space is specified by @config_len.
+ */
+struct fw_rsc_vdev {
+ u32 id;
+ u32 notifyid;
+ u32 dfeatures;
+ u32 gfeatures;
+ u32 config_len;
+ u8 status;
+ u8 num_of_vrings;
+ u8 reserved[2];
+ struct fw_rsc_vdev_vring vring[];
+} __packed;
+
+/**
+ * rsc_table_for_each_entry() - iterate over all entries in a resource table
+ * @table: pointer to the resource table
+ * @table_sz: total size of the table buffer in bytes
+ * @dev: device used for error logging
+ * @cb: callback invoked for each entry:
+ * @type - value from enum fw_resource_type
+ * @rsc - pointer to the entry payload (past struct fw_rsc_hdr)
+ * @offset - byte offset of the payload within the table; callers
+ * that write back into the table (e.g. to record a
+ * dynamically allocated address) use this to locate the
+ * entry for later update
+ * @avail - bytes available in the payload
+ * @data - caller-supplied private pointer
+ * Return 0 to continue iteration, non-zero to stop.
+ * @data: private pointer forwarded to @cb on every call
+ *
+ * Iterates over every resource entry in @table, performing the standard
+ * truncation check, and invokes @cb for each one. Iteration stops on the
+ * first non-zero return from @cb or on a malformed table.
+ *
+ * Returns 0 after a complete iteration, -EINVAL if the table is truncated,
+ * or the first non-zero value returned by @cb.
+ */
+static inline int rsc_table_for_each_entry(struct resource_table *table,
+ size_t table_sz,
+ struct device *dev,
+ int (*cb)(u32 type, void *rsc,
+ int offset, int avail,
+ void *data),
+ void *data) {
+ int i, ret;
+
+ for (i = 0; i < table->num; i++) {
+ u32 offset = table->offset[i];
+ struct fw_rsc_hdr *hdr;
+ int avail, rsc_offset;
+ void *rsc;
+
+ if (offset < sizeof(*table) || offset >= table_sz ||
+ table_sz - offset < sizeof(*hdr)) {
+ dev_err(dev, "rsc table is truncated\n");
+ return -EINVAL;
+ }
+
+ hdr = (void *)table + offset;
+ avail = table_sz - offset - sizeof(*hdr);
+ rsc_offset = offset + sizeof(*hdr);
+ rsc = (void *)hdr + sizeof(*hdr);
+
+ ret = cb(hdr->type, rsc, rsc_offset, avail, data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#endif /* RSC_TABLE_H */
diff --git a/include/linux/rtsx_usb.h b/include/linux/rtsx_usb.h
index 3247ed8e9ff0..5933751cf721 100644
--- a/include/linux/rtsx_usb.h
+++ b/include/linux/rtsx_usb.h
@@ -57,6 +57,9 @@ struct rtsx_ucr {
struct timer_list sg_timer;
struct mutex dev_mutex;
+
+ u16 card_status_cache;
+ bool card_status_valid;
};
/* buffer size */
diff --git a/include/linux/sunrpc/svc_rdma_pcl.h b/include/linux/sunrpc/svc_rdma_pcl.h
index 7516ad0fae80..655681cf8fed 100644
--- a/include/linux/sunrpc/svc_rdma_pcl.h
+++ b/include/linux/sunrpc/svc_rdma_pcl.h
@@ -97,7 +97,7 @@ pcl_next_chunk(const struct svc_rdma_pcl *pcl, struct svc_rdma_chunk *chunk)
*/
#define pcl_for_each_segment(pos, chunk) \
for (pos = &(chunk)->ch_segments[0]; \
- pos <= &(chunk)->ch_segments[(chunk)->ch_segcount - 1]; \
+ pos < &(chunk)->ch_segments[(chunk)->ch_segcount]; \
pos++)
/**
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e49e5ab57813..899fc1e9e301 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1128,8 +1128,8 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size)
/* ----------------------------------------------------------------------- */
/* Stuff for dynamic usb ids */
+extern struct mutex usb_dynids_lock;
struct usb_dynids {
- spinlock_t lock;
struct list_head list;
};
@@ -1145,16 +1145,6 @@ extern ssize_t usb_store_new_id(struct usb_dynids *dynids,
extern ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf);
-/**
- * struct usbdrv_wrap - wrapper for driver-model structure
- * @driver: The driver-model core driver structure.
- * @for_devices: Non-zero for device drivers, 0 for interface drivers.
- */
-struct usbdrv_wrap {
- struct device_driver driver;
- int for_devices;
-};
-
/**
* struct usb_driver - identifies USB interface driver to usbcore
* @name: The driver name should be unique among USB drivers,
@@ -1165,7 +1155,8 @@ struct usbdrv_wrap {
* interface. It may also use usb_set_interface() to specify the
* appropriate altsetting. If unwilling to manage the interface,
* return -ENODEV, if genuine IO errors occurred, an appropriate
- * negative errno value.
+ * negative errno value. The usb_device_id parameter is only valid during
+ * probe.
* @disconnect: Called when the interface is no longer accessible, usually
* because its device has been (or is being) disconnected or the
* driver module is being unloaded.
@@ -1195,7 +1186,7 @@ struct usbdrv_wrap {
* is bound to the driver.
* @dynids: used internally to hold the list of dynamically added device
* ids for this driver.
- * @drvwrap: Driver-model core structure wrapper.
+ * @driver: The driver-model core driver structure.
* @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be
* added to this driver by preventing the sysfs file from being created.
* @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
@@ -1243,13 +1234,13 @@ struct usb_driver {
const struct attribute_group **dev_groups;
struct usb_dynids dynids;
- struct usbdrv_wrap drvwrap;
+ struct device_driver driver;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int disable_hub_initiated_lpm:1;
unsigned int soft_unbind:1;
};
-#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
+#define to_usb_driver(d) container_of_const(d, struct usb_driver, driver)
/**
* struct usb_device_driver - identifies USB device driver to usbcore
@@ -1267,7 +1258,7 @@ struct usb_driver {
* @resume: Called when the device is being resumed by the system.
* @dev_groups: Attributes attached to the device that will be created once it
* is bound to the driver.
- * @drvwrap: Driver-model core structure wrapper.
+ * @driver: The driver-model core driver structure.
* @id_table: used with @match() to select better matching driver at
* probe() time.
* @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
@@ -1276,7 +1267,7 @@ struct usb_driver {
* resume and suspend functions will be called in addition to the driver's
* own, so this part of the setup does not need to be replicated.
*
- * USB drivers must provide all the fields listed above except drvwrap,
+ * USB drivers must provide all the fields listed above except driver,
* match, and id_table.
*/
struct usb_device_driver {
@@ -1289,13 +1280,13 @@ struct usb_device_driver {
int (*suspend) (struct usb_device *udev, pm_message_t message);
int (*resume) (struct usb_device *udev, pm_message_t message);
const struct attribute_group **dev_groups;
- struct usbdrv_wrap drvwrap;
+ struct device_driver driver;
const struct usb_device_id *id_table;
unsigned int supports_autosuspend:1;
unsigned int generic_subclass:1;
};
#define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \
- drvwrap.driver)
+ driver)
/**
* struct usb_class_driver - identifies a USB driver that wants to use the USB major number
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 02a9f4dc594d..d2cab1056964 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -6,6 +6,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
+#include <net/tcp.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/virtio_net.h>
@@ -178,6 +179,9 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
if (skb->ip_summed == CHECKSUM_PARTIAL &&
skb->csum_offset != offsetof(struct tcphdr, check))
return -EINVAL;
+
+ BUILD_BUG_ON(TCP_MIN_GSO_SIZE * GSO_MAX_SEGS < GSO_MAX_SIZE);
+ gso_size = max(gso_size, TCP_MIN_GSO_SIZE);
break;
}
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 4676545ffd8f..b7805ecce62b 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -262,7 +262,6 @@ struct rc_map *rc_map_get(const char *name);
#define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce"
#define RC_MAP_GOTVIEW7135 "rc-gotview7135"
#define RC_MAP_HAUPPAUGE "rc-hauppauge"
-#define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge"
#define RC_MAP_HISI_POPLAR "rc-hisi-poplar"
#define RC_MAP_HISI_TV_DEMO "rc-hisi-tv-demo"
#define RC_MAP_IMON_MCE "rc-imon-mce"
@@ -308,7 +307,6 @@ struct rc_map *rc_map_get(const char *name);
#define RC_MAP_PROTEUS_2309 "rc-proteus-2309"
#define RC_MAP_PURPLETV "rc-purpletv"
#define RC_MAP_PV951 "rc-pv951"
-#define RC_MAP_RC5_TV "rc-rc5-tv"
#define RC_MAP_RC6_MCE "rc-rc6-mce"
#define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys"
#define RC_MAP_REDDO "rc-reddo"
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index bc18037e25ac..edd56d6fe76e 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -886,9 +886,9 @@ static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
hdev->discovery.result_filtering = false;
hdev->discovery.report_invalid_rssi = true;
hdev->discovery.rssi = HCI_RSSI_INVALID;
- hdev->discovery.uuid_count = 0;
spin_lock(&hdev->discovery.lock);
+ hdev->discovery.uuid_count = 0;
kfree(hdev->discovery.uuids);
hdev->discovery.uuids = NULL;
spin_unlock(&hdev->discovery.lock);
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 136036b48e6d..767df6d832c8 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -696,7 +696,12 @@ struct l2cap_rx_busy {
struct l2cap_pinfo {
struct bt_sock bt;
+
+ /* With owning sk_socket chan may be read without lock, other access
+ * should hold lock_sock.
+ */
struct l2cap_chan *chan;
+
struct list_head rx_busy;
};
diff --git a/include/net/dst.h b/include/net/dst.h
index 55d1be268d24..4b54b2f6730e 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -24,7 +24,10 @@
struct sk_buff;
struct dst_entry {
- struct net_device *dev;
+ union {
+ struct net_device *dev;
+ struct net_device __rcu *dev_rcu;
+ };
struct dst_ops *ops;
unsigned long _metrics;
unsigned long expires;
@@ -569,18 +572,41 @@ static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu)
dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}
-static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst)
+static inline struct net_device *dst_dev(const struct dst_entry *dst)
{
- /* In the future, use rcu_dereference(dst->dev) */
- WARN_ON_ONCE(!rcu_read_lock_held());
return READ_ONCE(dst->dev);
}
+static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst)
+{
+ return rcu_dereference(dst->dev_rcu);
+}
+
+static inline struct net *dst_dev_net_rcu(const struct dst_entry *dst)
+{
+ return dev_net_rcu(dst_dev_rcu(dst));
+}
+
+static inline struct net_device *skb_dst_dev(const struct sk_buff *skb)
+{
+ return dst_dev(skb_dst(skb));
+}
+
static inline struct net_device *skb_dst_dev_rcu(const struct sk_buff *skb)
{
return dst_dev_rcu(skb_dst(skb));
}
+static inline struct net *skb_dst_dev_net(const struct sk_buff *skb)
+{
+ return dev_net(skb_dst_dev(skb));
+}
+
+static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb)
+{
+ return dev_net_rcu(skb_dst_dev_rcu(skb));
+}
+
struct dst_entry *dst_blackhole_check(struct dst_entry *dst, u32 cookie);
void dst_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb, u32 mtu, bool confirm_neigh);
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 3eb715f66cbf..4ed883cad65a 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -42,7 +42,9 @@ struct inet_connection_sock_af_ops {
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req);
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk));
u16 net_header_len;
u16 net_frag_header_len;
u16 sockaddr_len;
@@ -296,14 +298,8 @@ reqsk_timeout(struct request_sock *req, unsigned long max_timeout)
return (unsigned long)min_t(u64, timeout, max_timeout);
}
-static inline void inet_csk_prepare_for_destroy_sock(struct sock *sk)
-{
- /* The below has to be done to allow calling inet_csk_destroy_sock */
- sock_set_flag(sk, SOCK_DEAD);
- this_cpu_inc(*sk->sk_prot->orphan_count);
-}
-
void inet_csk_destroy_sock(struct sock *sk);
+void inet_csk_prepare_for_destroy_sock(struct sock *sk);
void inet_csk_prepare_forced_close(struct sock *sk);
/*
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 8eeb3708f20b..21119e962f00 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -511,7 +511,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,
const int sdif,
bool *refcounted)
{
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = skb_dst_dev_net(skb);
const struct iphdr *iph = ip_hdr(skb);
struct sock *sk;
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
index f475757daafb..414e9adf4c51 100644
--- a/include/net/inetpeer.h
+++ b/include/net/inetpeer.h
@@ -35,6 +35,7 @@ struct inetpeer_addr {
struct inet_peer {
struct rb_node rb_node;
+ u64 hash;
struct inetpeer_addr daddr;
u32 metrics[RTAX_MAX];
@@ -125,6 +126,9 @@ static inline int inetpeer_addr_cmp(const struct inetpeer_addr *a,
{
int i, n;
+ if (a->family != b->family)
+ return a->family < b->family ? -1 : 1;
+
if (a->family == AF_INET)
n = sizeof(a->a4) / sizeof(u32);
else
diff --git a/include/net/ip.h b/include/net/ip.h
index bacdb4fecc89..f66dd581ba83 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -458,7 +458,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
bool forwarding)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net *net = dev_net(dst->dev);
+ struct net *net = dev_net(dst_dev(dst));
unsigned int mtu;
if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) ||
@@ -474,7 +474,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
if (mtu)
goto out;
- mtu = READ_ONCE(dst->dev->mtu);
+ mtu = READ_ONCE(dst_dev(dst)->mtu);
if (unlikely(ip_mtu_locked(dst))) {
if (rt->rt_uses_gateway && mtu > 576)
@@ -490,16 +490,17 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
const struct sk_buff *skb)
{
+ const struct dst_entry *dst = skb_dst(skb);
unsigned int mtu;
if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) {
bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
- return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
+ return ip_dst_mtu_maybe_forward(dst, forwarding);
}
- mtu = min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU);
- return mtu - lwtunnel_headroom(skb_dst(skb)->lwtstate, mtu);
+ mtu = min(READ_ONCE(dst_dev(dst)->mtu), IP_MAX_MTU);
+ return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
}
struct dst_metrics *ip_fib_metrics_init(struct nlattr *fc_mx, int fc_mx_len,
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 1051af74285c..1243ec414bcd 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -492,8 +492,7 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst,
int headroom, bool reply);
-static inline void ip_tunnel_adj_headroom(struct net_device *dev,
- unsigned int headroom)
+static inline unsigned int ip_tunnel_limit_headroom(unsigned int headroom)
{
/* we must cap headroom to some upperlimit, else pskb_expand_head
* will overflow header offsets in skb_headers_offset_update().
@@ -503,6 +502,14 @@ static inline void ip_tunnel_adj_headroom(struct net_device *dev,
if (headroom > max_allowed)
headroom = max_allowed;
+ return headroom;
+}
+
+static inline void ip_tunnel_adj_headroom(struct net_device *dev,
+ unsigned int headroom)
+{
+ headroom = ip_tunnel_limit_headroom(headroom);
+
if (headroom > READ_ONCE(dev->needed_headroom))
WRITE_ONCE(dev->needed_headroom, headroom);
}
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index 9b5698b86986..1cce0a3970b9 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -12,6 +12,7 @@
#define DEFAULT_TX_QUEUE_LEN 1000
#define STAB_SIZE_LOG_MAX 30
+#define QDISC_PKT_LEN_MAX (1 << 20) /* 1 MiB */
struct qdisc_walker {
int stop;
diff --git a/include/net/route.h b/include/net/route.h
index 1d5d975c1707..d443de683628 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -359,7 +359,7 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
const struct net *net;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dev_net_rcu(dst_dev(dst));
hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
rcu_read_unlock();
}
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5baf98eac3db..61b81510ac18 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -2088,7 +2088,7 @@ struct sctp_association {
force_delay:1;
__u8 strreset_enable;
- __u8 strreset_outstanding; /* request param count on the fly */
+ __u8 strreset_outstanding; /* request param bitmask on the fly */
__u32 strreset_outseq; /* Update after receiving response */
__u32 strreset_inseq; /* Update after receiving request */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 23d830a7a6c8..c75d12e5156d 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -461,7 +461,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req);
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk));
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
int tcp_connect(struct sock *sk);
@@ -2513,6 +2515,11 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2,
return tcp_call_bpf(sk, op, 3, args);
}
+static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk)
+{
+ tcp_sk(sk)->bpf_sock_ops_cb_flags = 0;
+}
+
#else
static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
{
@@ -2530,6 +2537,10 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2,
return -EPERM;
}
+static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk)
+{
+}
+
#endif
static inline u32 tcp_timeout_init(struct sock *sk)
diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h
index 97392fd7712c..d3a109a79a1d 100644
--- a/include/net/xsk_buff_pool.h
+++ b/include/net/xsk_buff_pool.h
@@ -27,7 +27,6 @@ struct xdp_buff_xsk {
dma_addr_t dma;
dma_addr_t frame_dma;
struct xsk_buff_pool *pool;
- u64 orig_addr;
struct list_head list_node;
};
@@ -119,7 +118,6 @@ void xp_free(struct xdp_buff_xsk *xskb);
static inline void xp_init_xskb_addr(struct xdp_buff_xsk *xskb, struct xsk_buff_pool *pool,
u64 addr)
{
- xskb->orig_addr = addr;
xskb->xdp.data_hard_start = pool->addrs + addr + pool->headroom;
}
@@ -224,14 +222,19 @@ static inline void xp_release(struct xdp_buff_xsk *xskb)
xskb->pool->free_heads[xskb->pool->free_heads_cnt++] = xskb;
}
-static inline u64 xp_get_handle(struct xdp_buff_xsk *xskb)
+static inline u64 xp_get_handle(struct xdp_buff_xsk *xskb,
+ struct xsk_buff_pool *pool)
{
- u64 offset = xskb->xdp.data - xskb->xdp.data_hard_start;
+ u64 orig_addr = xskb->xdp.data - pool->addrs;
+ u64 offset;
- offset += xskb->pool->headroom;
- if (!xskb->pool->unaligned)
- return xskb->orig_addr + offset;
- return xskb->orig_addr + (offset << XSK_UNALIGNED_BUF_OFFSET_SHIFT);
+ if (!pool->unaligned)
+ return orig_addr;
+
+ offset = xskb->xdp.data - xskb->xdp.data_hard_start;
+ offset += pool->headroom;
+ orig_addr -= offset;
+ return orig_addr + (offset << XSK_UNALIGNED_BUF_OFFSET_SHIFT);
}
#endif /* XSK_BUFF_POOL_H_ */
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index bea6f44e120e..c8787bd5be99 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1829,6 +1829,11 @@ struct ib_dm {
struct ib_mr {
struct ib_device *device;
+ /*
+ * Due to IB_MR_REREG_PD pd is not a fixed pointer and can change. For a
+ * user MR, this value should only be read from a system call that holds
+ * the uobject lock, or the driver should disable in-place REREG_PD.
+ */
struct ib_pd *pd;
u32 lkey;
u32 rkey;
@@ -2484,6 +2489,15 @@ struct ib_device_ops {
int (*modify_cq)(struct ib_cq *cq, u16 cq_count, u16 cq_period);
int (*destroy_cq)(struct ib_cq *cq, struct ib_udata *udata);
int (*resize_cq)(struct ib_cq *cq, int cqe, struct ib_udata *udata);
+ /**
+ * pre_destroy_cq - Prevent a cq from generating any new work
+ * completions, but not free any kernel resources
+ */
+ int (*pre_destroy_cq)(struct ib_cq *cq);
+ /**
+ * post_destroy_cq - Free all kernel resources
+ */
+ void (*post_destroy_cq)(struct ib_cq *cq);
struct ib_mr *(*get_dma_mr)(struct ib_pd *pd, int mr_access_flags);
struct ib_mr *(*reg_user_mr)(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int mr_access_flags,
diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h
index 8b7c46daeb07..0d69ded73bf2 100644
--- a/include/rdma/restrack.h
+++ b/include/rdma/restrack.h
@@ -14,6 +14,9 @@
#include <uapi/rdma/rdma_netlink.h>
#include <linux/xarray.h>
+/* Mark entry as containing driver specific details, it is used to provide QP subtype for now */
+#define RESTRACK_DD XA_MARK_1
+
struct ib_device;
struct sk_buff;
@@ -116,8 +119,8 @@ struct rdma_restrack_entry {
u32 id;
};
-int rdma_restrack_count(struct ib_device *dev,
- enum rdma_restrack_type type);
+int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
+ bool show_details);
/**
* rdma_is_kernel_res() - check the owner of resource
* @res: resource entry
diff --git a/include/sound/core.h b/include/sound/core.h
index dfef0c9d4b9f..fe955fb238e5 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -314,6 +314,8 @@ static inline void snd_card_unref(struct snd_card *card)
put_device(&card->card_dev);
}
+DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
+
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
/* device.c */
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index f95326fce6bb..a5b743a2f775 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -723,6 +723,24 @@ enum {
};
#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
+/* [MDBA_GET_ENTRY] = {
+ * struct br_mdb_entry
+ * [MDBA_GET_ENTRY_ATTRS] = {
+ * [MDBE_ATTR_SOURCE]
+ * struct in_addr / struct in6_addr
+ * [MDBE_ATTR_SRC_VNI]
+ * u32
+ * }
+ * }
+ */
+enum {
+ MDBA_GET_ENTRY_UNSPEC,
+ MDBA_GET_ENTRY,
+ MDBA_GET_ENTRY_ATTRS,
+ __MDBA_GET_ENTRY_MAX,
+};
+#define MDBA_GET_ENTRY_MAX (__MDBA_GET_ENTRY_MAX - 1)
+
/* [MDBA_SET_ENTRY_ATTRS] = {
* [MDBE_ATTR_xxx]
* ...
@@ -739,6 +757,7 @@ enum {
MDBE_ATTR_VNI,
MDBE_ATTR_IFINDEX,
MDBE_ATTR_SRC_VNI,
+ MDBE_ATTR_STATE_MASK,
__MDBE_ATTR_MAX,
};
#define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 7c1fb1419702..1e72860b150a 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1219,6 +1219,7 @@ enum {
IFLA_HSR_PROTOCOL, /* Indicate different protocol than
* HSR. For example PRP.
*/
+ IFLA_HSR_INTERLINK, /* HSR interlink network device */
__IFLA_HSR_MAX,
};
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index f82a66361a1e..4dc38ef34cbd 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -126,6 +126,7 @@ struct landlock_path_beneath_attr {
* device.
* - %LANDLOCK_ACCESS_FS_MAKE_DIR: Create (or rename) a directory.
* - %LANDLOCK_ACCESS_FS_MAKE_REG: Create (or rename or link) a regular file.
+ * This also guards the creation of whiteout objects as used in OverlayFS.
* - %LANDLOCK_ACCESS_FS_MAKE_SOCK: Create (or rename or link) a UNIX domain
* socket.
* - %LANDLOCK_ACCESS_FS_MAKE_FIFO: Create (or rename or link) a named pipe.
diff --git a/include/uapi/linux/time_types.h b/include/uapi/linux/time_types.h
index bcc0002115d3..03a0d8aaadca 100644
--- a/include/uapi/linux/time_types.h
+++ b/include/uapi/linux/time_types.h
@@ -30,7 +30,7 @@ struct __kernel_old_timeval {
struct __kernel_old_timespec {
__kernel_old_time_t tv_sec; /* seconds */
- long tv_nsec; /* nanoseconds */
+ __kernel_long_t tv_nsec; /* nanoseconds */
};
struct __kernel_old_itimerval {
diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h
index e50c357367db..327bfb5b83aa 100644
--- a/include/uapi/rdma/rdma_netlink.h
+++ b/include/uapi/rdma/rdma_netlink.h
@@ -554,6 +554,14 @@ enum rdma_nldev_attr {
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX, /* u32 */
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC, /* u8 */
+ RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE, /* u8 */
+
+ RDMA_NLDEV_ATTR_DRIVER_DETAILS, /* u8 */
+ /*
+ * QP subtype string, used for driver QPs
+ */
+ RDMA_NLDEV_ATTR_RES_SUBTYPE, /* string */
+
/*
* Always the end
*/
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 538f5c640f7c..6f123436d55c 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1025,7 +1025,6 @@ static inline int audit_add_rule(struct audit_entry *entry)
int audit_del_rule(struct audit_entry *entry)
{
struct audit_entry *e;
- struct audit_tree *tree = entry->rule.tree;
struct list_head *list;
int ret = 0;
#ifdef CONFIG_AUDITSYSCALL
@@ -1073,9 +1072,6 @@ int audit_del_rule(struct audit_entry *entry)
out:
mutex_unlock(&audit_filter_mutex);
- if (tree)
- audit_put_tree(tree); /* that's the temporary one */
-
return ret;
}
@@ -1160,6 +1156,8 @@ int audit_rule_change(int type, int seq, void *data, size_t datasz)
}
if (err || type == AUDIT_DEL_RULE) {
+ if (type == AUDIT_DEL_RULE && entry->rule.tree)
+ audit_put_tree(entry->rule.tree);
if (entry->rule.exe)
audit_remove_mark(entry->rule.exe);
audit_free_rule(entry);
diff --git a/kernel/bpf/bloom_filter.c b/kernel/bpf/bloom_filter.c
index 35e1ddca74d2..3f55f28fd589 100644
--- a/kernel/bpf/bloom_filter.c
+++ b/kernel/bpf/bloom_filter.c
@@ -41,7 +41,7 @@ static long bloom_map_peek_elem(struct bpf_map *map, void *value)
for (i = 0; i < bloom->nr_hash_funcs; i++) {
h = hash(bloom, value, map->value_size, i);
- if (!test_bit(h, bloom->bitset))
+ if (!test_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h)))
return -ENOENT;
}
@@ -57,9 +57,13 @@ static long bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags)
if (flags != BPF_ANY)
return -EINVAL;
+ /*
+ * On 32-bit architectures, hashes larger than INT_MAX would be
+ * treated as negative by set_bit().
+ */
for (i = 0; i < bloom->nr_hash_funcs; i++) {
h = hash(bloom, value, map->value_size, i);
- set_bit(h, bloom->bitset);
+ set_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h));
}
return 0;
@@ -94,9 +98,10 @@ static int bloom_map_alloc_check(union bpf_attr *attr)
static struct bpf_map *bloom_map_alloc(union bpf_attr *attr)
{
- u32 bitset_bytes, bitset_mask, nr_hash_funcs, nr_bits;
+ u32 bitset_mask, nr_hash_funcs, nr_bits;
int numa_node = bpf_map_attr_numa_node(attr);
struct bpf_bloom_filter *bloom;
+ u64 bitset_bytes;
if (attr->key_size != 0 || attr->value_size == 0 ||
attr->max_entries == 0 ||
@@ -127,22 +132,16 @@ static struct bpf_map *bloom_map_alloc(union bpf_attr *attr)
if (check_mul_overflow(attr->max_entries, nr_hash_funcs, &nr_bits) ||
check_mul_overflow(nr_bits / 5, (u32)7, &nr_bits) ||
nr_bits > (1UL << 31)) {
- /* The bit array size is 2^32 bits but to avoid overflowing the
- * u32, we use U32_MAX, which will round up to the equivalent
- * number of bytes
- */
- bitset_bytes = BITS_TO_BYTES(U32_MAX);
bitset_mask = U32_MAX;
} else {
if (nr_bits <= BITS_PER_LONG)
nr_bits = BITS_PER_LONG;
else
nr_bits = roundup_pow_of_two(nr_bits);
- bitset_bytes = BITS_TO_BYTES(nr_bits);
bitset_mask = nr_bits - 1;
}
- bitset_bytes = roundup(bitset_bytes, sizeof(unsigned long));
+ bitset_bytes = BITS_TO_LONGS((u64)bitset_mask + 1) * sizeof(unsigned long);
bloom = bpf_map_area_alloc(sizeof(*bloom) + bitset_bytes, numa_node);
if (!bloom)
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index b75b63e67506..0a422be3b039 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -646,8 +646,10 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *old_prog = NULL;
struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
struct bpf_cgroup_storage *new_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
+ struct bpf_cgroup_storage *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
struct bpf_prog *new_prog = prog ? : link->link.prog;
enum cgroup_bpf_attach_type atype;
+ u32 old_flags, old_pl_flags;
struct bpf_prog_list *pl;
struct hlist_head *progs;
int err;
@@ -693,6 +695,8 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
if (pl) {
old_prog = pl->prog;
+ old_pl_flags = pl->flags;
+ bpf_cgroup_storages_assign(old_storage, pl->storage);
} else {
struct hlist_node *last = NULL;
@@ -716,6 +720,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
pl->link = link;
pl->flags = flags;
bpf_cgroup_storages_assign(pl->storage, storage);
+ old_flags = cgrp->bpf.flags[atype];
cgrp->bpf.flags[atype] = saved_flags;
if (type == BPF_LSM_CGROUP) {
@@ -746,12 +751,15 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
if (old_prog) {
pl->prog = old_prog;
pl->link = NULL;
+ pl->flags = old_pl_flags;
+ bpf_cgroup_storages_assign(pl->storage, old_storage);
}
bpf_cgroup_storages_free(new_storage);
if (!old_prog) {
hlist_del(&pl->node);
kfree(pl);
}
+ cgrp->bpf.flags[atype] = old_flags;
return err;
}
@@ -2053,7 +2061,7 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level,
if (ret < 0)
return ret;
- if (ctx.optlen > *optlen)
+ if (ctx.optlen > *optlen || ctx.optlen < 0)
return -EFAULT;
/* BPF programs can shrink the buffer, export the modifications.
@@ -2436,22 +2444,22 @@ static bool cg_sockopt_is_valid_access(int off, int size,
}
switch (off) {
- case offsetof(struct bpf_sockopt, sk):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET;
break;
- case offsetof(struct bpf_sockopt, optval):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, optval):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET;
break;
- case offsetof(struct bpf_sockopt, optval_end):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, optval_end):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET_END;
break;
- case offsetof(struct bpf_sockopt, retval):
+ case bpf_ctx_range(struct bpf_sockopt, retval):
if (size != size_default)
return false;
return prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT;
diff --git a/kernel/bpf/net_namespace.c b/kernel/bpf/net_namespace.c
index 868cc2c43899..a1d14bcb67c5 100644
--- a/kernel/bpf/net_namespace.c
+++ b/kernel/bpf/net_namespace.c
@@ -172,33 +172,28 @@ static int bpf_netns_link_update_prog(struct bpf_link *link,
struct net *net;
int idx, ret;
+ guard(mutex)(&netns_bpf_mutex);
+
if (old_prog && old_prog != link->prog)
return -EPERM;
if (new_prog->type != link->prog->type)
return -EINVAL;
- mutex_lock(&netns_bpf_mutex);
-
net = net_link->net;
- if (!net || !check_net(net)) {
+ if (!net || !check_net(net))
/* Link auto-detached or netns dying */
- ret = -ENOLINK;
- goto out_unlock;
- }
+ return -ENOLINK;
run_array = rcu_dereference_protected(net->bpf.run_array[type],
lockdep_is_held(&netns_bpf_mutex));
idx = link_index(net, type, net_link);
ret = bpf_prog_array_update_at(run_array, idx, new_prog);
if (ret)
- goto out_unlock;
+ return ret;
old_prog = xchg(&link->prog, new_prog);
bpf_prog_put(old_prog);
-
-out_unlock:
- mutex_unlock(&netns_bpf_mutex);
- return ret;
+ return 0;
}
static int bpf_netns_link_fill_info(const struct bpf_link *link,
diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c
index c0c5e9b313e4..73a810a64a6e 100644
--- a/kernel/bpf/ringbuf.c
+++ b/kernel/bpf/ringbuf.c
@@ -429,7 +429,7 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
prod_pos = rb->producer_pos;
new_prod_pos = prod_pos + len;
- while (pend_pos < prod_pos) {
+ while (prod_pos - pend_pos > 0) {
hdr = (void *)rb->data + (pend_pos & rb->mask);
hdr_len = READ_ONCE(hdr->len);
if (hdr_len & BPF_RINGBUF_BUSY_BIT)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 5fa86a2ab14a..163700efb5b3 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2984,9 +2984,10 @@ static const char *bpf_link_type_strs[] = {
static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
{
const struct bpf_link *link = filp->private_data;
- const struct bpf_prog *prog = link->prog;
+ const struct bpf_prog *prog;
enum bpf_link_type type = link->type;
char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
+ u32 prog_id = 0;
if (type < ARRAY_SIZE(bpf_link_type_strs) && bpf_link_type_strs[type]) {
seq_printf(m, "link_type:\t%s\n", bpf_link_type_strs[type]);
@@ -2996,13 +2997,20 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
}
seq_printf(m, "link_id:\t%u\n", link->id);
+ rcu_read_lock();
+ prog = READ_ONCE(link->prog);
if (prog) {
bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
+ prog_id = prog->aux->id;
+ }
+ rcu_read_unlock();
+
+ if (prog) {
seq_printf(m,
"prog_tag:\t%s\n"
"prog_id:\t%u\n",
prog_tag,
- prog->aux->id);
+ prog_id);
}
if (link->ops->show_fdinfo)
link->ops->show_fdinfo(link, m);
@@ -4750,6 +4758,7 @@ static int bpf_link_get_info_by_fd(struct file *file,
{
struct bpf_link_info __user *uinfo = u64_to_user_ptr(attr->info.info);
struct bpf_link_info info;
+ const struct bpf_prog *prog;
u32 info_len = attr->info.info_len;
int err;
@@ -4764,8 +4773,12 @@ static int bpf_link_get_info_by_fd(struct file *file,
info.type = link->type;
info.id = link->id;
- if (link->prog)
- info.prog_id = link->prog->aux->id;
+
+ rcu_read_lock();
+ prog = READ_ONCE(link->prog);
+ if (prog)
+ info.prog_id = prog->aux->id;
+ rcu_read_unlock();
if (link->ops->fill_link_info) {
err = link->ops->fill_link_info(link, &info);
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index f7ef58090c7d..30dd2b31a38d 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -783,6 +783,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
struct mmap_unlock_irq_work *work = NULL;
struct vm_area_struct *vma;
bool irq_work_busy = false;
+ bool __maybe_unused mmput_needed = false;
struct mm_struct *mm;
int ret = -ENOENT;
@@ -792,14 +793,38 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
if (!task)
return -ENOENT;
- mm = task->mm;
+ if (task == current) {
+ mm = task->mm;
+ } else {
+ /*
+ * Foreign task: pin task->mm against a concurrent exit_mm().
+ * Use trylock on alloc_lock instead of get_task_mm()'s
+ * blocking task_lock() to avoid deadlocking the target task.
+ */
+ if (!IS_ENABLED(CONFIG_MMU))
+ return -EOPNOTSUPP;
+ if (irqs_disabled())
+ return -EBUSY;
+ if (!spin_trylock(&task->alloc_lock))
+ return -EBUSY;
+ mm = task->mm;
+ if (mm && !(task->flags & PF_KTHREAD)) {
+ mmget(mm);
+ mmput_needed = true;
+ } else {
+ mm = NULL;
+ }
+ spin_unlock(&task->alloc_lock);
+ }
if (!mm)
return -ENOENT;
irq_work_busy = bpf_mmap_unlock_get_irq_work(&work);
- if (irq_work_busy || !mmap_read_trylock(mm))
- return -EBUSY;
+ if (irq_work_busy || !mmap_read_trylock(mm)) {
+ ret = -EBUSY;
+ goto out;
+ }
vma = find_vma(mm, start);
@@ -809,6 +834,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
ret = 0;
}
bpf_mmap_unlock_mm(work, mm);
+out:
+#ifdef CONFIG_MMU
+ if (mmput_needed)
+ mmput_async(mm);
+#endif
return ret;
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0a67f285f4b0..f7f5f54dc448 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20511,13 +20511,25 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
struct btf *bpf_get_btf_vmlinux(void)
{
- if (!btf_vmlinux && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
+ /* Pairs with the smp_store_release() on the parse path below. */
+ struct btf *btf = smp_load_acquire(&btf_vmlinux);
+
+ if (!btf && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
mutex_lock(&bpf_verifier_lock);
- if (!btf_vmlinux)
- btf_vmlinux = btf_parse_vmlinux();
+ btf = btf_vmlinux;
+ if (!btf) {
+ btf = btf_parse_vmlinux();
+ /*
+ * Order the parsed BTF contents and the globals the
+ * parse populated (e.g. bpf_ctx_convert.t) before
+ * the pointer publication. Pairs with the acquire
+ * on the lockless fast path above.
+ */
+ smp_store_release(&btf_vmlinux, btf);
+ }
mutex_unlock(&bpf_verifier_lock);
}
- return btf_vmlinux;
+ return btf;
}
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index f9c7178f51ad..bb413045d174 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2630,6 +2630,8 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
*/
cs->attach_in_progress++;
out_unlock:
+ if (ret)
+ reset_migrate_dl_data(cs);
mutex_unlock(&cpuset_mutex);
return ret;
}
@@ -3462,8 +3464,6 @@ static int cpuset_can_fork(struct task_struct *task, struct css_set *cset)
cs->attach_in_progress++;
out_unlock:
- if (ret)
- reset_migrate_dl_data(cs);
mutex_unlock(&cpuset_mutex);
return ret;
}
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 2959309c4fb8..0968155a638e 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -268,9 +268,9 @@ void __init swiotlb_update_mem_attributes(void)
}
static void swiotlb_init_io_tlb_pool(struct io_tlb_pool *mem, phys_addr_t start,
- unsigned long nslabs, bool late_alloc, unsigned int nareas)
+ void *vaddr, unsigned long nslabs, bool late_alloc,
+ unsigned int nareas)
{
- void *vaddr = phys_to_virt(start);
unsigned long bytes = nslabs << IO_TLB_SHIFT, i;
mem->nslabs = nslabs;
@@ -411,7 +411,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
return;
}
- swiotlb_init_io_tlb_pool(mem, __pa(tlb), nslabs, false, nareas);
+ swiotlb_init_io_tlb_pool(mem, __pa(tlb), tlb, nslabs, false, nareas);
add_mem_pool(&io_tlb_default_mem, mem);
if (flags & SWIOTLB_VERBOSE)
@@ -509,7 +509,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
set_memory_decrypted((unsigned long)vstart,
(nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
- swiotlb_init_io_tlb_pool(mem, virt_to_phys(vstart), nslabs, true,
+ swiotlb_init_io_tlb_pool(mem, virt_to_phys(vstart), vstart, nslabs, true,
nareas);
add_mem_pool(&io_tlb_default_mem, mem);
@@ -607,25 +607,26 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
* @bytes: Size of the buffer.
* @phys_limit: Maximum allowed physical address of the buffer.
* @gfp: GFP flags for the allocation.
+ * @vaddr: Receives the virtual address for the allocated buffer.
*
* Return: Allocated pages, or %NULL on allocation failure.
*/
static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
- u64 phys_limit, gfp_t gfp)
+ u64 phys_limit, gfp_t gfp, void **vaddr)
{
struct page *page;
+ *vaddr = NULL;
+
/*
* Allocate from the atomic pools if memory is encrypted and
* the allocation is atomic, because decrypting may block.
*/
if (!gfpflags_allow_blocking(gfp) && dev && force_dma_unencrypted(dev)) {
- void *vaddr;
-
if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
return NULL;
- return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
+ return dma_alloc_from_pool(dev, bytes, vaddr, gfp,
dma_coherent_ok);
}
@@ -647,6 +648,8 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
return NULL;
}
+ if (page)
+ *vaddr = phys_to_virt(page_to_phys(page));
return page;
}
@@ -687,6 +690,7 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
{
struct io_tlb_pool *pool;
unsigned int slot_order;
+ void *tlb_vaddr;
struct page *tlb;
size_t pool_size;
size_t tlb_size;
@@ -703,7 +707,8 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
pool->areas = (void *)pool + sizeof(*pool);
tlb_size = nslabs << IO_TLB_SHIFT;
- while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, gfp))) {
+ while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, gfp,
+ &tlb_vaddr))) {
if (nslabs <= minslabs)
goto error_tlb;
nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
@@ -717,11 +722,12 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
if (!pool->slots)
goto error_slots;
- swiotlb_init_io_tlb_pool(pool, page_to_phys(tlb), nslabs, true, nareas);
+ swiotlb_init_io_tlb_pool(pool, page_to_phys(tlb), tlb_vaddr, nslabs,
+ true, nareas);
return pool;
error_slots:
- swiotlb_free_tlb(page_address(tlb), tlb_size);
+ swiotlb_free_tlb(tlb_vaddr, tlb_size);
error_tlb:
kfree(pool);
error:
@@ -1746,7 +1752,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
rmem->size >> PAGE_SHIFT);
- swiotlb_init_io_tlb_pool(pool, rmem->base, nslabs,
+ swiotlb_init_io_tlb_pool(pool, rmem->base, phys_to_virt(rmem->base),
+ nslabs,
false, nareas);
mem->force_bounce = true;
mem->for_alloc = true;
diff --git a/kernel/entry/common.c b/kernel/entry/common.c
index 5ff4f1cd3644..19923bc5ab77 100644
--- a/kernel/entry/common.c
+++ b/kernel/entry/common.c
@@ -65,6 +65,9 @@ static long syscall_trace_enter(struct pt_regs *regs, long syscall,
ret = ptrace_report_syscall_entry(regs);
if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
return -1L;
+
+ /* ptrace might have changed work flags */
+ work = READ_ONCE(current_thread_info()->syscall_work);
}
/* Do seccomp after ptrace, to catch any tracer changes. */
diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
index 60b08247b07d..dd1dfec2651e 100644
--- a/kernel/futex/requeue.c
+++ b/kernel/futex/requeue.c
@@ -147,8 +147,16 @@ static inline void futex_requeue_pi_complete(struct futex_q *q, int locked)
} while (!atomic_try_cmpxchg(&q->requeue_state, &old, new));
#ifdef CONFIG_PREEMPT_RT
- /* If the waiter interleaved with the requeue let it know */
- if (unlikely(old == Q_REQUEUE_PI_WAIT))
+ /*
+ * The waiter in futex_requeue_pi_wakeup_sync() can interleave with the
+ * wake below: It will assign Q_REQUEUE_PI_IN_PROGRESS and here it will
+ * be updated to Q_REQUEUE_PI_LOCKED (locked = 1). The rcuwait_wait_event()
+ * will already read Q_REQUEUE_PI_LOCKED and skip the schedule() invocation,
+ * leading to an access of futex_q::requeue_wait after the waiter returned.
+ * In this case only we skip the wake here and rely on following wake in
+ * requeue_pi_wake_futex() to perform the wake if needed.
+ */
+ if (unlikely(old == Q_REQUEUE_PI_WAIT) && new != Q_REQUEUE_PI_LOCKED)
rcuwait_wake_up(&q->requeue_wait);
#endif
}
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index 5a452b94b643..59f0685b4ec4 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -272,21 +272,14 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
}
/**
- * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain
- * @d: irq domain for which to allocate chips
- * @irqs_per_chip: Number of interrupts each chip handles (max 32)
- * @num_ct: Number of irq_chip_type instances associated with this
- * @name: Name of the irq chip
- * @handler: Default flow handler associated with these chips
- * @clr: IRQ_* bits to clear in the mapping function
- * @set: IRQ_* bits to set in the mapping function
- * @gcflags: Generic chip specific setup flags
+ * irq_domain_alloc_generic_chips - Allocate generic chips for an irq domain
+ * @d: irq domain for which to allocate chips
+ * @info: Generic chip information
+ *
+ * Return: 0 on success, negative error code on failure
*/
-int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
- int num_ct, const char *name,
- irq_flow_handler_t handler,
- unsigned int clr, unsigned int set,
- enum irq_gc_flags gcflags)
+int irq_domain_alloc_generic_chips(struct irq_domain *d,
+ const struct irq_domain_chip_generic_info *info)
{
struct irq_domain_chip_generic *dgc;
struct irq_chip_generic *gc;
@@ -300,23 +293,23 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
if (d->gc)
return -EBUSY;
- numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip);
+ numchips = DIV_ROUND_UP(d->revmap_size, info->irqs_per_chip);
if (!numchips)
return -EINVAL;
/* Allocate a pointer, generic chip and chiptypes for each chip */
- gc_sz = struct_size(gc, chip_types, num_ct);
+ gc_sz = struct_size(gc, chip_types, info->num_ct);
dgc_sz = struct_size(dgc, gc, numchips);
sz = dgc_sz + numchips * gc_sz;
tmp = dgc = kzalloc(sz, GFP_KERNEL);
if (!dgc)
return -ENOMEM;
- dgc->irqs_per_chip = irqs_per_chip;
+ dgc->irqs_per_chip = info->irqs_per_chip;
dgc->num_chips = numchips;
- dgc->irq_flags_to_set = set;
- dgc->irq_flags_to_clear = clr;
- dgc->gc_flags = gcflags;
+ dgc->irq_flags_to_set = info->irq_flags_to_set;
+ dgc->irq_flags_to_clear = info->irq_flags_to_clear;
+ dgc->gc_flags = info->gc_flags;
d->gc = dgc;
/* Calc pointer to the first generic chip */
@@ -324,11 +317,12 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
for (i = 0; i < numchips; i++) {
/* Store the pointer to the generic chip */
dgc->gc[i] = gc = tmp;
- irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
- NULL, handler);
+ irq_init_generic_chip(gc, info->name, info->num_ct,
+ i * dgc->irqs_per_chip, NULL,
+ info->handler);
gc->domain = d;
- if (gcflags & IRQ_GC_BE_IO) {
+ if (dgc->gc_flags & IRQ_GC_BE_IO) {
gc->reg_readl = &irq_readl_be;
gc->reg_writel = &irq_writel_be;
}
@@ -341,6 +335,57 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
}
return 0;
}
+EXPORT_SYMBOL_GPL(irq_domain_alloc_generic_chips);
+
+/**
+ * irq_domain_remove_generic_chips - Remove generic chips from an irq domain
+ * @d: irq domain for which generic chips are to be removed
+ */
+void irq_domain_remove_generic_chips(struct irq_domain *d)
+{
+ struct irq_domain_chip_generic *dgc = d->gc;
+ unsigned int i;
+
+ if (!dgc)
+ return;
+
+ for (i = 0; i < dgc->num_chips; i++)
+ irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0);
+
+ d->gc = NULL;
+ kfree(dgc);
+}
+EXPORT_SYMBOL_GPL(irq_domain_remove_generic_chips);
+
+/**
+ * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain
+ * @d: irq domain for which to allocate chips
+ * @irqs_per_chip: Number of interrupts each chip handles (max 32)
+ * @num_ct: Number of irq_chip_type instances associated with this
+ * @name: Name of the irq chip
+ * @handler: Default flow handler associated with these chips
+ * @clr: IRQ_* bits to clear in the mapping function
+ * @set: IRQ_* bits to set in the mapping function
+ * @gcflags: Generic chip specific setup flags
+ */
+int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
+ int num_ct, const char *name,
+ irq_flow_handler_t handler,
+ unsigned int clr, unsigned int set,
+ enum irq_gc_flags gcflags)
+{
+ struct irq_domain_chip_generic_info info = {
+ .irqs_per_chip = irqs_per_chip,
+ .num_ct = num_ct,
+ .name = name,
+ .handler = handler,
+ .irq_flags_to_clear = clr,
+ .irq_flags_to_set = set,
+ .gc_flags = gcflags,
+ };
+
+ return irq_domain_alloc_generic_chips(d, &info);
+}
EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips);
static struct irq_chip_generic *
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index ddaaccdc09fa..11af2877e752 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -236,6 +236,66 @@ static void __irq_domain_publish(struct irq_domain *domain)
pr_debug("Added domain %s\n", domain->name);
}
+static void irq_domain_free(struct irq_domain *domain)
+{
+ fwnode_dev_initialized(domain->fwnode, false);
+ fwnode_handle_put(domain->fwnode);
+ if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
+ kfree(domain->name);
+ kfree(domain);
+}
+
+/**
+ * irq_domain_instantiate() - Instantiate a new irq domain data structure
+ * @info: Domain information pointer pointing to the information for this domain
+ *
+ * Return: A pointer to the instantiated irq domain or an ERR_PTR value.
+ */
+struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
+{
+ struct irq_domain *domain;
+ int err;
+
+ domain = __irq_domain_create(info->fwnode, info->size, info->hwirq_max,
+ info->direct_max, info->ops, info->host_data);
+ if (!domain)
+ return ERR_PTR(-ENOMEM);
+
+ domain->flags |= info->domain_flags;
+ domain->exit = info->exit;
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ if (info->parent) {
+ domain->root = info->parent->root;
+ domain->parent = info->parent;
+ }
+#endif
+
+ if (info->dgc_info) {
+ err = irq_domain_alloc_generic_chips(domain, info->dgc_info);
+ if (err)
+ goto err_domain_free;
+ }
+
+ if (info->init) {
+ err = info->init(domain);
+ if (err)
+ goto err_domain_gc_remove;
+ }
+
+ __irq_domain_publish(domain);
+
+ return domain;
+
+err_domain_gc_remove:
+ if (info->dgc_info)
+ irq_domain_remove_generic_chips(domain);
+err_domain_free:
+ irq_domain_free(domain);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(irq_domain_instantiate);
+
/**
* __irq_domain_add() - Allocate a new irq_domain data structure
* @fwnode: firmware node for the interrupt controller
@@ -254,14 +314,18 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
const struct irq_domain_ops *ops,
void *host_data)
{
- struct irq_domain *domain;
-
- domain = __irq_domain_create(fwnode, size, hwirq_max, direct_max,
- ops, host_data);
- if (domain)
- __irq_domain_publish(domain);
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = hwirq_max,
+ .direct_max = direct_max,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
- return domain;
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}
EXPORT_SYMBOL_GPL(__irq_domain_add);
@@ -275,6 +339,9 @@ EXPORT_SYMBOL_GPL(__irq_domain_add);
*/
void irq_domain_remove(struct irq_domain *domain)
{
+ if (domain->exit)
+ domain->exit(domain);
+
mutex_lock(&irq_domain_mutex);
debugfs_remove_domain_dir(domain);
@@ -290,13 +357,11 @@ void irq_domain_remove(struct irq_domain *domain)
mutex_unlock(&irq_domain_mutex);
- pr_debug("Removed domain %s\n", domain->name);
+ if (domain->flags & IRQ_DOMAIN_FLAG_DESTROY_GC)
+ irq_domain_remove_generic_chips(domain);
- fwnode_dev_initialized(domain->fwnode, false);
- fwnode_handle_put(domain->fwnode);
- if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
- kfree(domain->name);
- kfree(domain);
+ pr_debug("Removed domain %s\n", domain->name);
+ irq_domain_free(domain);
}
EXPORT_SYMBOL_GPL(irq_domain_remove);
diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c
index 8a7baf4e332e..2db82661cd60 100644
--- a/kernel/kcsan/core.c
+++ b/kernel/kcsan/core.c
@@ -585,8 +585,14 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type, unsigned
* information is lost if dirtied by KCSAN.
*/
kcsan_save_irqtrace(current);
- if (!interrupt_watcher)
+ if (!interrupt_watcher) {
local_irq_save(irq_flags);
+ /*
+ * NMIs can still fire, disable checking for all interrupt
+ * contexts.
+ */
+ raw_cpu_ptr(&kcsan_cpu_ctx)->disable_count++;
+ }
watchpoint = insert_watchpoint((unsigned long)ptr, size, is_write);
if (watchpoint == NULL) {
@@ -699,8 +705,10 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type, unsigned
atomic_long_dec(&kcsan_counters[KCSAN_COUNTER_USED_WATCHPOINTS]);
out_unlock:
- if (!interrupt_watcher)
+ if (!interrupt_watcher) {
+ raw_cpu_ptr(&kcsan_cpu_ctx)->disable_count--;
local_irq_restore(irq_flags);
+ }
kcsan_restore_irqtrace(current);
ctx->disable_scoped--;
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index b687bd4ed932..fbe7c34fcd87 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1394,8 +1394,14 @@ static bool __within_kprobe_blacklist(unsigned long addr)
/*
* If 'kprobe_blacklist' is defined, check the address and
* reject any probe registration in the prohibited area.
+ * Note: this can return true during transition period where
+ * (start_addr, end_addr) in the black list is shrinking
+ * but old entry has not been removed yet. This is acceptable
+ * because the worst case is that we reject more probes than
+ * we should.
*/
- list_for_each_entry(ent, &kprobe_blacklist, list) {
+ guard(rcu)();
+ list_for_each_entry_rcu(ent, &kprobe_blacklist, list) {
if (addr >= ent->start_addr && addr < ent->end_addr)
return true;
}
@@ -2487,7 +2493,7 @@ int kprobe_add_ksym_blacklist(unsigned long entry)
ent->start_addr = entry;
ent->end_addr = entry + size;
INIT_LIST_HEAD(&ent->list);
- list_add_tail(&ent->list, &kprobe_blacklist);
+ list_add_tail_rcu(&ent->list, &kprobe_blacklist);
return (int)size;
}
@@ -2516,8 +2522,8 @@ static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end)
list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) {
if (ent->start_addr < start || ent->start_addr >= end)
continue;
- list_del(&ent->list);
- kfree(ent);
+ list_del_rcu(&ent->list);
+ kfree_rcu(ent, rcu);
}
}
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 9419a79e8833..b16e699cee9f 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -5331,6 +5331,8 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
lock->wait_type_outer,
lock->lock_type);
class = register_lock_class(lock, subclass, 0);
+ if (!class)
+ return 0;
hlock->class_idx = class - lock_classes;
curr->lockdep_depth = i;
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index c3ad93fc24e6..cd69e983870f 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -2808,9 +2808,10 @@ int snapshot_write_next(struct snapshot_handle *handle)
return error;
error = memory_bm_create(&zero_bm, GFP_ATOMIC, PG_ANY);
- if (error)
+ if (error) {
+ memory_bm_free(©_bm, PG_UNSAFE_CLEAR);
return error;
-
+ }
nr_zero_pages = 0;
hibernate_restore_protection_begin();
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index 4e941999a53b..5c2a248cf249 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -63,7 +63,7 @@ static unsigned int number_of_wakelocks;
static inline bool wakelocks_limit_exceeded(void)
{
- return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT;
+ return number_of_wakelocks >= CONFIG_PM_WAKELOCKS_LIMIT;
}
static inline void increment_wakelocks_number(void)
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 46612fb15fc6..6a42accb1463 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2641,7 +2641,7 @@ static unsigned long rcu_torture_fwd_prog_cbfree(struct rcu_fwd *rfp)
rcu_torture_fwd_prog_cond_resched(freed);
if (tick_nohz_full_enabled()) {
local_irq_save(flags);
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
local_irq_restore(flags);
}
}
@@ -2791,7 +2791,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp)
rcu_torture_fwd_prog_cond_resched(n_launders + n_max_cbs);
if (tick_nohz_full_enabled()) {
local_irq_save(flags);
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
local_irq_restore(flags);
}
}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 81f0a730c54b..849d9daf45f7 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -323,7 +323,7 @@ bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)
*
* The caller must have disabled interrupts and must not be idle.
*/
-notrace void rcu_momentary_dyntick_idle(void)
+notrace void rcu_momentary_eqs(void)
{
int seq;
@@ -333,7 +333,7 @@ notrace void rcu_momentary_dyntick_idle(void)
WARN_ON_ONCE(!(seq & RCU_DYNTICKS_IDX));
rcu_preempt_deferred_qs(current);
}
-EXPORT_SYMBOL_GPL(rcu_momentary_dyntick_idle);
+EXPORT_SYMBOL_GPL(rcu_momentary_eqs);
/**
* rcu_is_cpu_rrupt_from_idle - see if 'interrupted' from idle
diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h
index a3d613f063a8..42b7f0d4d4d8 100644
--- a/kernel/rcu/tree_nocb.h
+++ b/kernel/rcu/tree_nocb.h
@@ -930,7 +930,7 @@ static void nocb_cb_wait(struct rcu_data *rdp)
local_irq_save(flags);
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
local_irq_restore(flags);
/*
* Disable BH to provide the expected environment. Also, when
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 8cf1adcd259b..db59f4a0d26e 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -953,7 +953,7 @@ static void rcu_qs(void)
/*
* Register an urgently needed quiescent state. If there is an
- * emergency, invoke rcu_momentary_dyntick_idle() to do a heavy-weight
+ * emergency, invoke rcu_momentary_eqs() to do a heavy-weight
* dyntick-idle quiescent state visible to other CPUs, which will in
* some cases serve for expedited as well as normal grace periods.
* Either way, register a lightweight quiescent state.
@@ -962,7 +962,7 @@ void rcu_all_qs(void)
{
unsigned long flags;
- if (!raw_cpu_read(rcu_data.rcu_urgent_qs))
+ if (!READ_ONCE(*raw_cpu_ptr(&rcu_data.rcu_urgent_qs)))
return;
preempt_disable(); // For CONFIG_PREEMPT_COUNT=y kernels
/* Load rcu_urgent_qs before other flags. */
@@ -970,10 +970,10 @@ void rcu_all_qs(void)
preempt_enable();
return;
}
- this_cpu_write(rcu_data.rcu_urgent_qs, false);
- if (unlikely(raw_cpu_read(rcu_data.rcu_need_heavy_qs))) {
+ WRITE_ONCE(*this_cpu_ptr(&rcu_data.rcu_urgent_qs), false);
+ if (unlikely(READ_ONCE(*this_cpu_ptr(&rcu_data.rcu_need_heavy_qs)))) {
local_irq_save(flags);
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
local_irq_restore(flags);
}
rcu_qs();
@@ -991,9 +991,9 @@ void rcu_note_context_switch(bool preempt)
/* Load rcu_urgent_qs before other flags. */
if (!smp_load_acquire(this_cpu_ptr(&rcu_data.rcu_urgent_qs)))
goto out;
- this_cpu_write(rcu_data.rcu_urgent_qs, false);
- if (unlikely(raw_cpu_read(rcu_data.rcu_need_heavy_qs)))
- rcu_momentary_dyntick_idle();
+ WRITE_ONCE(*this_cpu_ptr(&rcu_data.rcu_urgent_qs), false);
+ if (unlikely(READ_ONCE(*this_cpu_ptr(&rcu_data.rcu_need_heavy_qs))))
+ rcu_momentary_eqs();
out:
rcu_tasks_qs(current, preempt);
trace_rcu_utilization(TPS("End context switch"));
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 819ec1ccc08c..809452968eb4 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -59,6 +59,17 @@ static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
/************************ Governor internals ***********************/
+static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
+{
+ /*
+ * Cast rate_limit_us before multiplication to force 64-bit arithmetic.
+ * Otherwise, on 32-bit platforms, both operands are converted to
+ * 32-bit unsigned long and the multiplication may overflow.
+ */
+ sg_policy->freq_update_delay_ns =
+ (s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
+}
+
static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
{
s64 delta_ns;
@@ -306,7 +317,7 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
* A CPU running a task which woken up after an IO operation can have its
* utilization boosted to speed up the completion of those IO operations.
* The IO boost value is increased each time a task wakes up from IO, in
- * sugov_iowait_apply(), and it's instead decreased by this function,
+ * sugov_iowait_boost(), and it's instead decreased by this function,
* each time an increase has not been requested (!iowait_boost_pending).
*
* A CPU which also appears to have been idle for at least one tick has also
@@ -592,7 +603,7 @@ rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count
tunables->rate_limit_us = rate_limit_us;
list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook)
- sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC;
+ sugov_update_rate_limit_us(sg_policy);
return count;
}
@@ -823,7 +834,7 @@ static int sugov_start(struct cpufreq_policy *policy)
void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
unsigned int cpu;
- sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
+ sugov_update_rate_limit_us(sg_policy);
sg_policy->last_freq_update_time = 0;
sg_policy->next_freq = 0;
sg_policy->work_in_progress = false;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c3a6b66b836c..43bc2f03e0c9 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4312,7 +4312,8 @@ static inline void
update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq)
{
long delta_sum, delta_avg = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg;
- u32 new_sum, divider;
+ u64 new_sum;
+ u32 divider;
/* Nothing to update */
if (!delta_avg)
@@ -4326,7 +4327,7 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf
/* Set new sched_entity's runnable */
se->avg.runnable_avg = gcfs_rq->avg.runnable_avg;
- new_sum = se->avg.runnable_avg * divider;
+ new_sum = (u64)se->avg.runnable_avg * divider;
delta_sum = (long)new_sum - (long)se->avg.runnable_sum;
se->avg.runnable_sum = new_sum;
@@ -10055,6 +10056,17 @@ static bool update_sd_pick_busiest(struct lb_env *env,
sds->local_stat.group_type != group_has_spare))
return false;
+ /*
+ * Candidate sg has no more than one task per CPU and has higher
+ * per-CPU capacity. Migrating tasks to less capable CPUs may harm
+ * throughput. Maximize throughput, power/energy consequences are not
+ * considered.
+ */
+ if ((env->sd->flags & SD_ASYM_CPUCAPACITY) &&
+ (sgs->group_type <= group_fully_busy) &&
+ (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu))))
+ return false;
+
if (sgs->group_type > busiest->group_type)
return true;
@@ -10162,17 +10174,6 @@ static bool update_sd_pick_busiest(struct lb_env *env,
break;
}
- /*
- * Candidate sg has no more than one task per CPU and has higher
- * per-CPU capacity. Migrating tasks to less capable CPUs may harm
- * throughput. Maximize throughput, power/energy consequences are not
- * considered.
- */
- if ((env->sd->flags & SD_ASYM_CPUCAPACITY) &&
- (sgs->group_type <= group_fully_busy) &&
- (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu))))
- return false;
-
return true;
}
diff --git a/kernel/signal.c b/kernel/signal.c
index 9fe386e6badf..6a51bd690092 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1226,6 +1226,7 @@ static inline bool has_si_pid_and_uid(struct kernel_siginfo *info)
int send_signal_locked(int sig, struct kernel_siginfo *info,
struct task_struct *t, enum pid_type type)
{
+ struct kernel_siginfo rewritten;
/* Should SIGKILL or SIGSTOP be received by a pid namespace init? */
bool force = false;
@@ -1239,6 +1240,9 @@ int send_signal_locked(int sig, struct kernel_siginfo *info,
/* SIGKILL and SIGSTOP is special or has ids */
struct user_namespace *t_user_ns;
+ rewritten = *info;
+ info = &rewritten;
+
rcu_read_lock();
t_user_ns = task_cred_xxx(t, user_ns);
if (current_user_ns() != t_user_ns) {
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index cedb17ba158a..da821ce258ea 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -251,7 +251,7 @@ static int multi_cpu_stop(void *data)
*/
touch_nmi_watchdog();
}
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
} while (curstate != MULTI_STOP_EXIT);
local_irq_restore(flags);
diff --git a/kernel/sys.c b/kernel/sys.c
index 47cb10a16b00..7465bb7c8919 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2136,7 +2136,7 @@ static int prctl_set_auxv(struct mm_struct *mm, unsigned long addr,
BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));
task_lock(current);
- memcpy(mm->saved_auxv, user_auxv, len);
+ memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv));
task_unlock(current);
return 0;
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index e47e13a5a579..5896b553de24 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -368,10 +368,9 @@ static int parse(struct nlattr *na, struct cpumask *mask)
return -E2BIG;
if (len < 1)
return -EINVAL;
- data = kmalloc(len, GFP_KERNEL);
+ data = nla_strdup(na, GFP_KERNEL);
if (!data)
return -ENOMEM;
- nla_strscpy(data, na, len);
ret = cpulist_parse(data, mask);
kfree(data);
return ret;
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index 353829883e66..7006f2d8eefe 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -1482,8 +1482,12 @@ static int __init init_clocksource_sysfs(void)
{
int error = subsys_system_register(&clocksource_subsys, NULL);
- if (!error)
- error = device_register(&device_clocksource);
+ if (error)
+ return error;
+
+ error = device_register(&device_clocksource);
+ if (error)
+ bus_unregister(&clocksource_subsys);
return error;
}
diff --git a/kernel/time/itimer.c b/kernel/time/itimer.c
index 00629e658ca1..dc5b1d92e246 100644
--- a/kernel/time/itimer.c
+++ b/kernel/time/itimer.c
@@ -100,7 +100,7 @@ static int do_getitimer(int which, struct itimerspec64 *value)
static int put_itimerval(struct __kernel_old_itimerval __user *o,
const struct itimerspec64 *i)
{
- struct __kernel_old_itimerval v;
+ struct __kernel_old_itimerval v = {};
v.it_interval.tv_sec = i->it_interval.tv_sec;
v.it_interval.tv_usec = i->it_interval.tv_nsec / NSEC_PER_USEC;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 11b7000d5e1d..344b158bcbd7 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1983,6 +1983,11 @@ static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk,
* xtime_nsec_2 = xtime_nsec_1 - offset
* Which simplifies to:
* xtime_nsec -= offset
+ *
+ * When subtracting offset from xtime_nsec, the same amount
+ * (in appropriate units) has to be added to ntp_error, in
+ * order to correctly track the delta between the time
+ * reported in xtime_nsec, and the intended time.
*/
if ((mult_adj > 0) && (tk->tkr_mono.mult + mult_adj < mult_adj)) {
/* NTP adjustment caused clocksource mult overflow */
@@ -1993,6 +1998,7 @@ static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk,
tk->tkr_mono.mult += mult_adj;
tk->xtime_interval += interval;
tk->tkr_mono.xtime_nsec -= offset;
+ tk->ntp_error += offset << tk->ntp_error_shift;
}
/*
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 4a2aeaa85af3..cf86c1d9d68e 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -2224,6 +2224,7 @@ static void migrate_timer_list(struct timer_base *new_base, struct hlist_head *h
timer = hlist_entry(head->first, struct timer_list, entry);
detach_timer(timer, false);
timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
+ debug_timer_activate(timer);
internal_add_timer(new_base, timer);
}
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index f57baf67726d..47f1e8f47d47 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6174,17 +6174,27 @@ static inline void trace_insert_eval_map_file(struct module *mod,
struct trace_eval_map **start, int len) { }
#endif /* !CONFIG_TRACE_EVAL_MAP_FILE */
-static void trace_insert_eval_map(struct module *mod,
- struct trace_eval_map **start, int len)
+static void
+trace_event_update_with_eval_map(struct module *mod,
+ struct trace_eval_map **start,
+ int len)
{
struct trace_eval_map **map;
- if (len <= 0)
- return;
+ /* Always run sanitizer only if btf_type_tag attr exists. */
+ if (len <= 0) {
+ if (!(IS_ENABLED(CONFIG_DEBUG_INFO_BTF) &&
+ IS_ENABLED(CONFIG_PAHOLE_HAS_BTF_TAG) &&
+ __has_attribute(btf_type_tag)))
+ return;
+ }
map = start;
- trace_event_eval_update(map, len);
+ trace_event_update_all(map, len, mod);
+
+ if (len <= 0)
+ return;
trace_insert_eval_map_file(mod, start, len);
}
@@ -9820,7 +9830,7 @@ static void __init eval_map_work_func(struct work_struct *work)
int len;
len = __stop_ftrace_eval_maps - __start_ftrace_eval_maps;
- trace_insert_eval_map(NULL, __start_ftrace_eval_maps, len);
+ trace_event_update_with_eval_map(NULL, __start_ftrace_eval_maps, len);
}
static int __init trace_eval_init(void)
@@ -9855,9 +9865,6 @@ late_initcall_sync(trace_eval_sync);
#ifdef CONFIG_MODULES
static void trace_module_add_evals(struct module *mod)
{
- if (!mod->num_trace_evals)
- return;
-
/*
* Modules with bad taint do not have events created, do
* not bother with enums either.
@@ -9865,7 +9872,8 @@ static void trace_module_add_evals(struct module *mod)
if (trace_module_has_bad_taint(mod))
return;
- trace_insert_eval_map(mod, mod->trace_evals, mod->num_trace_evals);
+ /* Even if no trace_evals, this need to sanitize field types. */
+ trace_event_update_with_eval_map(mod, mod->trace_evals, mod->num_trace_evals);
}
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 7ba372c21fee..05ac2b5c8e20 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1987,13 +1987,13 @@ static inline const char *get_syscall_name(int syscall)
#ifdef CONFIG_EVENT_TRACING
void trace_event_init(void);
-void trace_event_eval_update(struct trace_eval_map **map, int len);
+void trace_event_update_all(struct trace_eval_map **map, int len, struct module *mod);
/* Used from boot time tracer */
extern int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
extern int trigger_process_regex(struct trace_event_file *file, char *buff);
#else
static inline void __init trace_event_init(void) { }
-static inline void trace_event_eval_update(struct trace_eval_map **map, int len) { }
+static inline void trace_event_update_all(struct trace_eval_map **map, int len, struct module *mod) { }
#endif
#ifdef CONFIG_TRACER_SNAPSHOT
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index bd8167d006f9..5e33b0c34f48 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2981,43 +2981,120 @@ static void add_str_to_module(struct module *module, char *str)
list_add(&modstr->next, &module_strings);
}
+#define ATTRIBUTE_STR "__attribute__("
+#define ATTRIBUTE_STR_LEN (sizeof(ATTRIBUTE_STR) - 1)
+
+/* Remove all __attribute__() from @type. Return allocated string or @type. */
+static char *sanitize_field_type(const char *type)
+{
+ char *attr, *tmp, *next, *ret = (char *)type;
+ int depth;
+
+ next = (char *)type;
+ while ((attr = strstr(next, ATTRIBUTE_STR))) {
+ /* Retry if "__attribute__(" is a part of another word. */
+ if (attr != next && !isspace(attr[-1])) {
+ next = attr + ATTRIBUTE_STR_LEN;
+ continue;
+ }
+
+ if (ret == type) {
+ ret = kstrdup(type, GFP_KERNEL);
+ if (WARN_ON_ONCE(!ret))
+ return NULL;
+ attr = ret + (attr - type);
+ }
+
+ /* the ATTRIBUTE_STR already has the first '(' */
+ depth = 1;
+ next = attr + ATTRIBUTE_STR_LEN;
+ do {
+ tmp = strpbrk(next, "()");
+ /* There is unbalanced parentheses */
+ if (WARN_ON_ONCE(!tmp)) {
+ kfree(ret);
+ return (char *)type;
+ }
+
+ if (*tmp == '(')
+ depth++;
+ else
+ depth--;
+ next = tmp + 1;
+ } while (depth > 0);
+ next = skip_spaces(next);
+ strcpy(attr, next);
+ next = attr;
+ }
+ return ret;
+}
+
+static char *find_replacable_eval(const char *type, const char *eval_string,
+ int len)
+{
+ char *ptr;
+
+ if (!eval_string)
+ return NULL;
+
+ ptr = strchr(type, '[');
+ if (!ptr)
+ return NULL;
+ ptr++;
+
+ if (!isalpha(*ptr) && *ptr != '_')
+ return NULL;
+
+ if (strncmp(eval_string, ptr, len) != 0)
+ return NULL;
+
+ return ptr;
+}
+
static void update_event_fields(struct trace_event_call *call,
struct trace_eval_map *map)
{
struct ftrace_event_field *field;
+ const char *eval_string = NULL;
struct list_head *head;
+ int len = 0;
char *ptr;
char *str;
- int len = strlen(map->eval_string);
/* Dynamic events should never have field maps */
- if (WARN_ON_ONCE(call->flags & TRACE_EVENT_FL_DYNAMIC))
+ if (call->flags & TRACE_EVENT_FL_DYNAMIC)
return;
+ if (map) {
+ eval_string = map->eval_string;
+ len = strlen(map->eval_string);
+ }
+
head = trace_get_fields(call);
list_for_each_entry(field, head, link) {
- ptr = strchr(field->type, '[');
- if (!ptr)
- continue;
- ptr++;
-
- if (!isalpha(*ptr) && *ptr != '_')
- continue;
+ str = sanitize_field_type(field->type);
+ if (!str)
+ return;
- if (strncmp(map->eval_string, ptr, len) != 0)
- continue;
+ ptr = find_replacable_eval(str, eval_string, len);
+ if (ptr) {
+ if (str == field->type) {
+ str = kstrdup(field->type, GFP_KERNEL);
+ if (WARN_ON_ONCE(!str))
+ return;
+ ptr = str + (ptr - field->type);
+ }
- str = kstrdup(field->type, GFP_KERNEL);
- if (WARN_ON_ONCE(!str))
- return;
- ptr = str + (ptr - field->type);
- ptr = eval_replace(ptr, map, len);
- /* enum/sizeof string smaller than value */
- if (WARN_ON_ONCE(!ptr)) {
- kfree(str);
- continue;
+ ptr = eval_replace(ptr, map, len);
+ /* enum/sizeof string smaller than value */
+ if (WARN_ON_ONCE(!ptr)) {
+ kfree(str);
+ continue;
+ }
}
+ if (str == field->type)
+ continue;
/*
* If the event is part of a module, then we need to free the string
* when the module is removed. Otherwise, it will stay allocated
@@ -3027,20 +3104,28 @@ static void update_event_fields(struct trace_event_call *call,
add_str_to_module(call->module, str);
field->type = str;
+ if (field->filter_type == FILTER_OTHER)
+ field->filter_type = filter_assign_type(field->type);
}
}
-void trace_event_eval_update(struct trace_eval_map **map, int len)
+/* Update all events for replacing eval and sanitizing */
+void trace_event_update_all(struct trace_eval_map **map, int len, struct module *mod)
{
struct trace_event_call *call, *p;
const char *last_system = NULL;
bool first = false;
+ bool updated;
int last_i;
int i;
mutex_lock(&event_mutex);
down_write(&trace_event_sem);
list_for_each_entry_safe(call, p, &ftrace_events, list) {
+
+ if (mod && call->module != mod)
+ continue;
+
/* events are usually grouped together with systems */
if (!last_system || call->class->system != last_system) {
first = true;
@@ -3048,6 +3133,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
last_system = call->class->system;
}
+ updated = false;
/*
* Since calls are grouped by systems, the likelihood that the
* next call in the iteration belongs to the same system as the
@@ -3067,8 +3153,12 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
}
update_event_printk(call, map[i]);
update_event_fields(call, map[i]);
+ updated = true;
}
}
+ /* If not updated yet, update field for sanitizing. */
+ if (!updated)
+ update_event_fields(call, NULL);
cond_resched();
}
up_write(&trace_event_sem);
@@ -4350,6 +4440,8 @@ static __init void event_test_stuff(void)
struct task_struct *test_thread;
test_thread = kthread_run(event_test_thread, NULL, "test-events");
+ if (WARN_ON(IS_ERR(test_thread)))
+ return;
msleep(1);
kthread_stop(test_thread);
}
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index f54e3855516f..d7e801855993 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6593,8 +6593,10 @@ static int hist_register_trigger(char *glob,
tracing_set_filter_buffering(file->tr, true);
}
- if (named_data)
+ if (named_data) {
+ remove_hist_vars(hist_data);
destroy_hist_data(hist_data);
+ }
out:
return ret;
}
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index 9dabeb86acd1..2d8bf456e6b8 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -850,6 +850,9 @@ void user_event_mm_dup(struct task_struct *t, struct user_event_mm *old_mm)
struct user_event_mm *mm = user_event_mm_alloc(t);
struct user_event_enabler *enabler;
+ /* On failure, do not free parent's copy */
+ t->user_event_mm = NULL;
+
if (!mm)
return;
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index c523ce5aa495..f4ebf7d9cc25 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -292,15 +292,18 @@ __init static int init_mmio_trace(void)
device_initcall(init_mmio_trace);
static void __trace_mmiotrace_rw(struct trace_array *tr,
- struct trace_array_cpu *data,
struct mmiotrace_rw *rw)
{
struct trace_event_call *call = &event_mmiotrace_rw;
- struct trace_buffer *buffer = tr->array_buffer.buffer;
+ struct trace_buffer *buffer;
struct ring_buffer_event *event;
struct trace_mmiotrace_rw *entry;
unsigned int trace_ctx;
+ if (!tr)
+ return;
+
+ buffer = tr->array_buffer.buffer;
trace_ctx = tracing_gen_ctx_flags(0);
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
sizeof(*entry), trace_ctx);
@@ -318,20 +321,22 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
void mmio_trace_rw(struct mmiotrace_rw *rw)
{
struct trace_array *tr = mmio_trace_array;
- struct trace_array_cpu *data = per_cpu_ptr(tr->array_buffer.data, smp_processor_id());
- __trace_mmiotrace_rw(tr, data, rw);
+ __trace_mmiotrace_rw(tr, rw);
}
static void __trace_mmiotrace_map(struct trace_array *tr,
- struct trace_array_cpu *data,
struct mmiotrace_map *map)
{
struct trace_event_call *call = &event_mmiotrace_map;
- struct trace_buffer *buffer = tr->array_buffer.buffer;
+ struct trace_buffer *buffer;
struct ring_buffer_event *event;
struct trace_mmiotrace_map *entry;
unsigned int trace_ctx;
+ if (!tr)
+ return;
+
+ buffer = tr->array_buffer.buffer;
trace_ctx = tracing_gen_ctx_flags(0);
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
sizeof(*entry), trace_ctx);
@@ -349,12 +354,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
void mmio_trace_mapping(struct mmiotrace_map *map)
{
struct trace_array *tr = mmio_trace_array;
- struct trace_array_cpu *data;
-
- preempt_disable();
- data = per_cpu_ptr(tr->array_buffer.data, smp_processor_id());
- __trace_mmiotrace_map(tr, data, map);
- preempt_enable();
+ __trace_mmiotrace_map(tr, map);
}
int mmio_trace_printk(const char *fmt, va_list args)
diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 8f4b59bedea6..f18e9bd0818d 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -1554,7 +1554,7 @@ static int run_osnoise(void)
* This will eventually cause unwarranted noise as PREEMPT_RCU
* will force preemption as the means of ending the current
* grace period. We avoid this problem by calling
- * rcu_momentary_dyntick_idle(), which performs a zero duration
+ * rcu_momentary_eqs(), which performs a zero duration
* EQS allowing PREEMPT_RCU to end the current grace period.
* This call shouldn't be wrapped inside an RCU critical
* section.
@@ -1566,7 +1566,7 @@ static int run_osnoise(void)
if (!disable_irq)
local_irq_disable();
- rcu_momentary_dyntick_idle();
+ rcu_momentary_eqs();
if (!disable_irq)
local_irq_enable();
diff --git a/lib/string.c b/lib/string.c
index d49243af5bf2..b05d9e5283d3 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -844,7 +844,8 @@ void *memchr_inv(const void *start, int c, size_t bytes)
{
u8 value = c;
u64 value64;
- unsigned int words, prefix;
+ size_t words;
+ unsigned int prefix;
if (bytes <= 16)
return check_bytes8(start, value, bytes);
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index cb50065e37a3..943cb521429d 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -391,7 +391,7 @@ static int dmirror_fault(struct dmirror *dmirror, unsigned long start,
/* Since the mm is for the mirrored process, get a reference first. */
if (!mmget_not_zero(mm))
- return 0;
+ return -EFAULT;
for (addr = start; addr < end; addr = range.end) {
range.start = addr;
diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c
index 0a559a42359b..19b9abaae18a 100644
--- a/lib/ucs2_string.c
+++ b/lib/ucs2_string.c
@@ -8,7 +8,7 @@ ucs2_strnlen(const ucs2_char_t *s, size_t maxlength)
{
unsigned long length = 0;
- while (*s++ != 0 && length < maxlength)
+ while (length < maxlength && *s++ != 0)
length++;
return length;
}
diff --git a/lib/xarray.c b/lib/xarray.c
index da79128ad754..26a455af6275 100644
--- a/lib/xarray.c
+++ b/lib/xarray.c
@@ -1012,6 +1012,9 @@ void xas_split_alloc(struct xa_state *xas, void *entry, unsigned int order,
if (xas->xa_shift + XA_CHUNK_SHIFT > order)
return;
+ if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
+ gfp |= __GFP_ACCOUNT;
+
do {
unsigned int i;
void *sibling = NULL;
diff --git a/mm/damon/core-test.h b/mm/damon/core-test.h
index a2aa410d6d0d..dd91b0eea61f 100644
--- a/mm/damon/core-test.h
+++ b/mm/damon/core-test.h
@@ -162,6 +162,10 @@ static void damon_test_split_at(struct kunit *test)
}
damon_add_region(r, t);
damon_split_region_at(t, r, 25);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+ if (damon_nr_regions(t) != 2)
+ goto out;
+
KUNIT_EXPECT_EQ(test, r->ar.start, 0ul);
KUNIT_EXPECT_EQ(test, r->ar.end, 25ul);
@@ -169,6 +173,7 @@ static void damon_test_split_at(struct kunit *test)
KUNIT_EXPECT_EQ(test, r->ar.start, 25ul);
KUNIT_EXPECT_EQ(test, r->ar.end, 100ul);
+out:
damon_free_target(t);
damon_destroy_ctx(c);
}
@@ -250,14 +255,17 @@ static void damon_test_merge_regions_of(struct kunit *test)
damon_add_region(r, t);
}
- damon_merge_regions_of(t, 9, 9999);
+ damon_merge_regions_of(t, 9, 9999, true);
/* 0-112, 114-130, 130-156, 156-170 */
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 5u);
+ if (damon_nr_regions(t) != 5)
+ goto out;
for (i = 0; i < 5; i++) {
r = __nth_region_of(t, i);
KUNIT_EXPECT_EQ(test, r->ar.start, saddrs[i]);
KUNIT_EXPECT_EQ(test, r->ar.end, eaddrs[i]);
}
+out:
damon_free_target(t);
}
@@ -497,6 +505,8 @@ static void damos_test_filter_out(struct kunit *test)
KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 1);
KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 2);
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+ if (damon_nr_regions(t) != 2)
+ goto out;
r2 = damon_next_region(r);
KUNIT_EXPECT_EQ(test, r2->ar.start, DAMON_MIN_REGION * 2);
KUNIT_EXPECT_EQ(test, r2->ar.end, DAMON_MIN_REGION * 4);
@@ -510,11 +520,14 @@ static void damos_test_filter_out(struct kunit *test)
KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 2);
KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 6);
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+ if (damon_nr_regions(t) != 2)
+ goto out;
r2 = damon_next_region(r);
KUNIT_EXPECT_EQ(test, r2->ar.start, DAMON_MIN_REGION * 6);
KUNIT_EXPECT_EQ(test, r2->ar.end, DAMON_MIN_REGION * 8);
damon_destroy_region(r2, t);
+out:
damon_free_target(t);
damos_free_filter(f);
}
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 734ab958ec57..0713fda22682 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -1192,15 +1192,17 @@ static void damon_merge_two_regions(struct damon_target *t,
* sz_limit size upper limit of each region
*/
static void damon_merge_regions_of(struct damon_target *t, unsigned int thres,
- unsigned long sz_limit)
+ unsigned long sz_limit, bool count_age)
{
struct damon_region *r, *prev = NULL, *next;
damon_for_each_region_safe(r, next, t) {
- if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
- r->age = 0;
- else
- r->age++;
+ if (count_age) {
+ if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
+ r->age = 0;
+ else
+ r->age++;
+ }
if (prev && prev->ar.end == r->ar.start &&
abs(prev->nr_accesses - r->nr_accesses) <= thres &&
@@ -1234,15 +1236,18 @@ static void kdamond_merge_regions(struct damon_ctx *c, unsigned int threshold,
struct damon_target *t;
unsigned int nr_regions;
unsigned int max_thres;
+ bool count_age = true;
max_thres = c->attrs.aggr_interval /
(c->attrs.sample_interval ? c->attrs.sample_interval : 1);
do {
nr_regions = 0;
damon_for_each_target(t, c) {
- damon_merge_regions_of(t, threshold, sz_limit);
+ damon_merge_regions_of(t, threshold, sz_limit,
+ count_age);
nr_regions += damon_nr_regions(t);
}
+ count_age = false;
threshold = max(1, threshold * 2);
} while (nr_regions > c->attrs.max_nr_regions &&
threshold / 2 < max_thres);
diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index 909db25efb35..fe481ae64f37 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -111,7 +111,7 @@ static bool __damon_pa_young(struct folio *folio, struct vm_area_struct *vma,
return *accessed == false;
}
-static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
+static bool damon_pa_young(unsigned long paddr)
{
struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
bool accessed = false;
@@ -143,30 +143,17 @@ static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
folio_unlock(folio);
out:
- *folio_sz = folio_size(folio);
folio_put(folio);
return accessed;
}
static void __damon_pa_check_access(struct damon_region *r)
{
- static unsigned long last_addr;
- static unsigned long last_folio_sz = PAGE_SIZE;
- static bool last_accessed;
-
- /* If the region is in the last checked page, reuse the result */
- if (ALIGN_DOWN(last_addr, last_folio_sz) ==
- ALIGN_DOWN(r->sampling_addr, last_folio_sz)) {
- if (last_accessed)
- r->nr_accesses++;
- return;
- }
+ bool accessed;
- last_accessed = damon_pa_young(r->sampling_addr, &last_folio_sz);
- if (last_accessed)
+ accessed = damon_pa_young(r->sampling_addr);
+ if (accessed)
r->nr_accesses++;
-
- last_addr = r->sampling_addr;
}
static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx)
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 3cde2137dba8..546decf6b777 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -151,6 +151,7 @@ static void damon_sysfs_scheme_regions_rm_dirs(
list_for_each_entry_safe(r, next, ®ions->regions_list, list) {
list_del(&r->list);
+ kobject_del(&r->kobj);
kobject_put(&r->kobj);
regions->nr_regions--;
}
@@ -513,8 +514,10 @@ static void damon_sysfs_scheme_filters_rm_dirs(
struct damon_sysfs_scheme_filter **filters_arr = filters->filters_arr;
int i;
- for (i = 0; i < filters->nr; i++)
+ for (i = 0; i < filters->nr; i++) {
+ kobject_del(&filters_arr[i]->kobj);
kobject_put(&filters_arr[i]->kobj);
+ }
filters->nr = 0;
kfree(filters_arr);
filters->filters_arr = NULL;
@@ -1402,6 +1405,7 @@ void damon_sysfs_schemes_rm_dirs(struct damon_sysfs_schemes *schemes)
for (i = 0; i < schemes->nr; i++) {
damon_sysfs_scheme_rm_dirs(schemes_arr[i]);
+ kobject_del(&schemes_arr[i]->kobj);
kobject_put(&schemes_arr[i]->kobj);
}
schemes->nr = 0;
@@ -1439,13 +1443,15 @@ static int damon_sysfs_schemes_add_dirs(struct damon_sysfs_schemes *schemes,
goto out;
err = damon_sysfs_scheme_add_dirs(scheme);
if (err)
- goto out;
+ goto del_out;
schemes_arr[i] = scheme;
schemes->nr++;
}
return 0;
+del_out:
+ kobject_del(&scheme->kobj);
out:
damon_sysfs_schemes_rm_dirs(schemes);
kobject_put(&scheme->kobj);
diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 7a410640a0ef..46980a4bef23 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -107,8 +107,10 @@ static void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions)
struct damon_sysfs_region **regions_arr = regions->regions_arr;
int i;
- for (i = 0; i < regions->nr; i++)
+ for (i = 0; i < regions->nr; i++) {
+ kobject_del(®ions_arr[i]->kobj);
kobject_put(®ions_arr[i]->kobj);
+ }
regions->nr = 0;
kfree(regions_arr);
regions->regions_arr = NULL;
@@ -305,6 +307,7 @@ static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
for (i = 0; i < targets->nr; i++) {
damon_sysfs_target_rm_dirs(targets_arr[i]);
+ kobject_del(&targets_arr[i]->kobj);
kobject_put(&targets_arr[i]->kobj);
}
targets->nr = 0;
@@ -343,13 +346,15 @@ static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
err = damon_sysfs_target_add_dirs(target);
if (err)
- goto out;
+ goto del_out;
targets_arr[i] = target;
targets->nr++;
}
return 0;
+del_out:
+ kobject_del(&target->kobj);
out:
damon_sysfs_targets_rm_dirs(targets);
kobject_put(&target->kobj);
@@ -829,6 +834,7 @@ static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
for (i = 0; i < contexts->nr; i++) {
damon_sysfs_context_rm_dirs(contexts_arr[i]);
+ kobject_del(&contexts_arr[i]->kobj);
kobject_put(&contexts_arr[i]->kobj);
}
contexts->nr = 0;
@@ -867,13 +873,15 @@ static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
err = damon_sysfs_context_add_dirs(context);
if (err)
- goto out;
+ goto del_out;
contexts_arr[i] = context;
contexts->nr++;
}
return 0;
+del_out:
+ kobject_del(&context->kobj);
out:
damon_sysfs_contexts_rm_dirs(contexts);
kobject_put(&context->kobj);
@@ -1634,6 +1642,7 @@ static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
for (i = 0; i < kdamonds->nr; i++) {
damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
+ kobject_del(&kdamonds_arr[i]->kobj);
kobject_put(&kdamonds_arr[i]->kobj);
}
kdamonds->nr = 0;
@@ -1689,13 +1698,15 @@ static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
err = damon_sysfs_kdamond_add_dirs(kdamond);
if (err)
- goto out;
+ goto del_out;
kdamonds_arr[i] = kdamond;
kdamonds->nr++;
}
return 0;
+del_out:
+ kobject_del(&kdamond->kobj);
out:
damon_sysfs_kdamonds_rm_dirs(kdamonds);
kobject_put(&kdamond->kobj);
diff --git a/mm/damon/vaddr-test.h b/mm/damon/vaddr-test.h
index 292736d3f395..c0546447b21e 100644
--- a/mm/damon/vaddr-test.h
+++ b/mm/damon/vaddr-test.h
@@ -149,12 +149,17 @@ static void damon_do_test_apply_three_regions(struct kunit *test,
damon_set_regions(t, three_regions, 3);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), nr_expected / 2);
+ if (damon_nr_regions(t) != nr_expected / 2)
+ goto out;
+
for (i = 0; i < nr_expected / 2; i++) {
r = __nth_region_of(t, i);
KUNIT_EXPECT_EQ(test, r->ar.start, expected[i * 2]);
KUNIT_EXPECT_EQ(test, r->ar.end, expected[i * 2 + 1]);
}
+out:
damon_destroy_target(t);
}
diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index 4d7dc9f65f68..c74b3fd1d227 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -429,8 +429,6 @@ static void damon_va_prepare_access_checks(struct damon_ctx *ctx)
}
struct damon_young_walk_private {
- /* size of the folio for the access checked virtual memory address */
- unsigned long *folio_sz;
bool young;
};
@@ -466,7 +464,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr,
mmu_notifier_test_young(walk->mm,
addr))
priv->young = true;
- *priv->folio_sz = HPAGE_PMD_SIZE;
folio_put(folio);
huge_out:
spin_unlock(ptl);
@@ -488,7 +485,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr,
if (pte_young(ptent) || !folio_test_idle(folio) ||
mmu_notifier_test_young(walk->mm, addr))
priv->young = true;
- *priv->folio_sz = folio_size(folio);
folio_put(folio);
out:
pte_unmap_unlock(pte, ptl);
@@ -517,7 +513,6 @@ static int damon_young_hugetlb_entry(pte_t *pte, unsigned long hmask,
if (pte_young(entry) || !folio_test_idle(folio) ||
mmu_notifier_test_young(walk->mm, addr))
priv->young = true;
- *priv->folio_sz = huge_page_size(h);
folio_put(folio);
@@ -535,11 +530,9 @@ static const struct mm_walk_ops damon_young_ops = {
.walk_lock = PGWALK_RDLOCK,
};
-static bool damon_va_young(struct mm_struct *mm, unsigned long addr,
- unsigned long *folio_sz)
+static bool damon_va_young(struct mm_struct *mm, unsigned long addr)
{
struct damon_young_walk_private arg = {
- .folio_sz = folio_sz,
.young = false,
};
@@ -556,25 +549,13 @@ static bool damon_va_young(struct mm_struct *mm, unsigned long addr,
* r the region to be checked
*/
static void __damon_va_check_access(struct mm_struct *mm,
- struct damon_region *r, bool same_target)
+ struct damon_region *r)
{
- static unsigned long last_addr;
- static unsigned long last_folio_sz = PAGE_SIZE;
- static bool last_accessed;
-
- /* If the region is in the last checked page, reuse the result */
- if (same_target && (ALIGN_DOWN(last_addr, last_folio_sz) ==
- ALIGN_DOWN(r->sampling_addr, last_folio_sz))) {
- if (last_accessed)
- r->nr_accesses++;
- return;
- }
+ bool accessed;
- last_accessed = damon_va_young(mm, r->sampling_addr, &last_folio_sz);
- if (last_accessed)
+ accessed = damon_va_young(mm, r->sampling_addr);
+ if (accessed)
r->nr_accesses++;
-
- last_addr = r->sampling_addr;
}
static unsigned int damon_va_check_accesses(struct damon_ctx *ctx)
@@ -583,17 +564,14 @@ static unsigned int damon_va_check_accesses(struct damon_ctx *ctx)
struct mm_struct *mm;
struct damon_region *r;
unsigned int max_nr_accesses = 0;
- bool same_target;
damon_for_each_target(t, ctx) {
mm = damon_get_mm(t);
if (!mm)
continue;
- same_target = false;
damon_for_each_region(r, t) {
- __damon_va_check_access(mm, r, same_target);
+ __damon_va_check_access(mm, r);
max_nr_accesses = max(r->nr_accesses, max_nr_accesses);
- same_target = true;
}
mmput(mm);
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index edae641407b9..bd8752a5356b 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -7353,14 +7353,14 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int re
* There is no need to transfer the per-node surplus state
* when we do not cross the node.
*/
- if (new_nid == old_nid)
- return;
- spin_lock_irq(&hugetlb_lock);
- if (h->surplus_huge_pages_node[old_nid]) {
- h->surplus_huge_pages_node[old_nid]--;
- h->surplus_huge_pages_node[new_nid]++;
+ if (new_nid != old_nid) {
+ spin_lock_irq(&hugetlb_lock);
+ if (h->surplus_huge_pages_node[old_nid]) {
+ h->surplus_huge_pages_node[old_nid]--;
+ h->surplus_huge_pages_node[new_nid]++;
+ }
+ spin_unlock_irq(&hugetlb_lock);
}
- spin_unlock_irq(&hugetlb_lock);
}
/*
diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
index 152dca73f398..11bd18e851a0 100644
--- a/mm/kasan/quarantine.c
+++ b/mm/kasan/quarantine.c
@@ -361,7 +361,12 @@ void kasan_quarantine_remove_cache(struct kmem_cache *cache)
*/
on_each_cpu(per_cpu_remove_cache, cache, 1);
- for_each_online_cpu(cpu) {
+ /*
+ * A CPU can go offline after on_each_cpu() returns, leaving cache
+ * objects on that CPU's shrink list. Scan all possible CPUs to
+ * drain those lists.
+ */
+ for_each_possible_cpu(cpu) {
sq = per_cpu_ptr(&shrink_qlist, cpu);
raw_spin_lock_irqsave(&sq->lock, flags);
qlist_move_cache(&sq->qlist, &to_free, cache);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 41207e680e7d..5c654a395fff 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -6593,6 +6593,9 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
unsigned long nr_pages = page_counter_read(&memcg->memory);
unsigned long reclaimed;
+ if (high != READ_ONCE(memcg->memory.high))
+ break;
+
if (nr_pages <= high)
break;
@@ -6641,6 +6644,9 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
for (;;) {
unsigned long nr_pages = page_counter_read(&memcg->memory);
+ if (max != READ_ONCE(memcg->memory.max))
+ break;
+
if (nr_pages <= max)
break;
diff --git a/mm/migrate.c b/mm/migrate.c
index 620871d193c7..f93e76594e4e 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1644,7 +1644,7 @@ static int migrate_pages_batch(struct list_head *from,
is_thp = is_large && folio_test_pmd_mappable(folio);
nr_pages = folio_nr_pages(folio);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
/*
* Large folio migration might be unsupported or
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 1aba3e8737d8..45c5a7ca9609 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1279,7 +1279,7 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat,
unsigned long node_start_pfn,
unsigned long node_end_pfn)
{
- unsigned long realtotalpages = 0, totalpages = 0;
+ unsigned long realtotalpages = 0;
enum zone_type i;
for (i = 0; i < MAX_NR_ZONES; i++) {
@@ -1309,11 +1309,10 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat,
zone->present_early_pages = real_size;
#endif
- totalpages += spanned;
realtotalpages += real_size;
}
- pgdat->node_spanned_pages = totalpages;
+ pgdat->node_spanned_pages = node_end_pfn - node_start_pfn;
pgdat->node_present_pages = realtotalpages;
pr_debug("On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages);
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 8eb498351d9b..b6ec592c3f14 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -6342,7 +6342,7 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
}
}
- cond_resched();
+ cond_resched_tasks_rcu_qs();
if (nr_reclaimed < nr_to_reclaim || proportional_reclaim)
continue;
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index ac12c06f11b8..f84638421437 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -323,11 +323,6 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
}
spin_unlock_bh(list_lock);
}
-
- /* all claims gone, initialize CRC */
- spin_lock_bh(&backbone_gw->crc_lock);
- backbone_gw->crc = BATADV_BLA_CRC_INIT;
- spin_unlock_bh(&backbone_gw->crc_lock);
}
/**
@@ -698,12 +693,14 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
struct batadv_bla_backbone_gw *old_backbone_gw;
struct batadv_bla_claim *claim;
struct batadv_bla_claim search_claim;
- bool remove_crc = false;
int hash_added;
+ u16 claim_crc;
+ bool changed;
ether_addr_copy(search_claim.addr, mac);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
+ claim_crc = crc16(0, mac, ETH_ALEN);
/* create a new claim entry if it does not exist yet. */
if (!claim) {
@@ -735,43 +732,56 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
kfree(claim);
return;
}
+
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_gw->crc ^= claim_crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
+ WRITE_ONCE(backbone_gw->lasttime, jiffies);
+
+ batadv_claim_put(claim);
+ return;
+ }
+
+ WRITE_ONCE(claim->lasttime, jiffies);
+
+ /* replace backbone_gw atomically and adjust reference counters */
+ spin_lock_bh(&claim->backbone_lock);
+ if (claim->backbone_gw != backbone_gw) {
+ changed = true;
+
+ old_backbone_gw = claim->backbone_gw;
+ kref_get(&backbone_gw->refcount);
+ claim->backbone_gw = backbone_gw;
} else {
- WRITE_ONCE(claim->lasttime, jiffies);
- if (claim->backbone_gw == backbone_gw)
- /* no need to register a new backbone */
- goto claim_free_ref;
+ old_backbone_gw = NULL;
+ changed = false;
+ }
+ spin_unlock_bh(&claim->backbone_lock);
+ if (changed) {
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): changing ownership for %pM, vid %d to gw %pM\n",
__func__, mac, batadv_print_vid(vid),
backbone_gw->orig);
- remove_crc = true;
- }
+ /* add claim address to new backbone_gw */
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_gw->crc ^= claim_crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
- /* replace backbone_gw atomically and adjust reference counters */
- spin_lock_bh(&claim->backbone_lock);
- old_backbone_gw = claim->backbone_gw;
- kref_get(&backbone_gw->refcount);
- claim->backbone_gw = backbone_gw;
- spin_unlock_bh(&claim->backbone_lock);
+ WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ }
- if (remove_crc) {
+ if (old_backbone_gw) {
/* remove claim address from old backbone_gw */
spin_lock_bh(&old_backbone_gw->crc_lock);
- old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ old_backbone_gw->crc ^= claim_crc;
spin_unlock_bh(&old_backbone_gw->crc_lock);
- }
- batadv_backbone_gw_put(old_backbone_gw);
-
- /* add claim address to new backbone_gw */
- spin_lock_bh(&backbone_gw->crc_lock);
- backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
- spin_unlock_bh(&backbone_gw->crc_lock);
- WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ batadv_backbone_gw_put(old_backbone_gw);
+ }
-claim_free_ref:
batadv_claim_put(claim);
}
@@ -941,26 +951,18 @@ static bool batadv_handle_unclaim(struct batadv_priv *bat_priv,
const u8 *backbone_addr, const u8 *claim_addr,
unsigned short vid)
{
- struct batadv_bla_backbone_gw *backbone_gw;
-
/* unclaim in any case if it is our own */
if (primary_if && batadv_compare_eth(backbone_addr,
primary_if->net_dev->dev_addr))
batadv_bla_send_claim(bat_priv, claim_addr, vid,
BATADV_CLAIM_TYPE_UNCLAIM);
- backbone_gw = batadv_backbone_hash_find(bat_priv, backbone_addr, vid);
-
- if (!backbone_gw)
- return true;
-
/* this must be an UNCLAIM frame */
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): UNCLAIM %pM on vid %d (sent by %pM)...\n", __func__,
- claim_addr, batadv_print_vid(vid), backbone_gw->orig);
+ claim_addr, batadv_print_vid(vid), backbone_addr);
batadv_bla_del_claim(bat_priv, claim_addr, vid);
- batadv_backbone_gw_put(backbone_gw);
return true;
}
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 1ddce66bb047..c6b7b3bece81 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -250,7 +250,10 @@ static u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
*/
static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
{
- return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
+ u8 *src = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN;
+ __be32 *ip = (__force __be32 *)src;
+
+ return get_unaligned(ip);
}
/**
@@ -275,8 +278,9 @@ static u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
{
u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4;
+ __be32 *ip = (__force __be32 *)dst;
- return *(__force __be32 *)dst;
+ return get_unaligned(ip);
}
/**
@@ -372,18 +376,19 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
u8 *mac_addr, unsigned short vid)
{
+ u64 u64_mac = ether_addr_to_u64(mac_addr);
struct batadv_dat_entry *dat_entry;
int hash_added;
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
/* if this entry is already known, just update it */
if (dat_entry) {
- if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
- ether_addr_copy(dat_entry->mac_addr, mac_addr);
+ atomic64_set(&dat_entry->mac_addr, u64_mac);
+
dat_entry->last_update = jiffies;
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Entry updated: %pI4 %pM (vid: %d)\n",
- &dat_entry->ip, dat_entry->mac_addr,
+ &dat_entry->ip, mac_addr,
batadv_print_vid(vid));
goto out;
}
@@ -394,7 +399,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
dat_entry->ip = ip;
dat_entry->vid = vid;
- ether_addr_copy(dat_entry->mac_addr, mac_addr);
+ atomic64_set(&dat_entry->mac_addr, u64_mac);
dat_entry->last_update = jiffies;
kref_init(&dat_entry->refcount);
@@ -410,7 +415,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
}
batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
- &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
+ &dat_entry->ip, mac_addr, batadv_print_vid(vid));
out:
batadv_dat_entry_put(dat_entry);
@@ -866,6 +871,8 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
struct netlink_callback *cb,
struct batadv_dat_entry *dat_entry)
{
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
int msecs;
void *hdr;
@@ -878,11 +885,12 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
genl_dump_check_consistent(cb, hdr);
msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
dat_entry->ip) ||
- nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN,
- dat_entry->mac_addr) ||
+ nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN, mac) ||
nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
genlmsg_cancel(msg, hdr);
@@ -1158,6 +1166,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct net_device *soft_iface = bat_priv->soft_iface;
int hdr_size = 0;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
@@ -1185,6 +1195,9 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
if (dat_entry) {
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
/* If the ARP request is destined for a local client the local
* client will answer itself. DAT would only generate a
* duplicate packet.
@@ -1193,7 +1206,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
* additional DAT answer may trigger kernel warnings about
* a packet coming from the wrong port.
*/
- if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
+ if (batadv_is_my_client(bat_priv, mac, vid)) {
ret = true;
goto out;
}
@@ -1203,18 +1216,16 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
* the backbone gws belonging to our backbone has claimed the
* destination.
*/
- if (!batadv_bla_check_claim(bat_priv,
- dat_entry->mac_addr, vid)) {
+ if (!batadv_bla_check_claim(bat_priv, mac, vid)) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Device %pM claimed by another backbone gw. Don't send ARP reply!",
- dat_entry->mac_addr);
+ mac);
ret = true;
goto out;
}
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
- dat_entry->mac_addr,
- hw_src, vid);
+ mac, hw_src, vid);
if (!skb_new)
goto out;
@@ -1256,6 +1267,8 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct batadv_dat_entry *dat_entry = NULL;
bool ret = false;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
int err;
if (!atomic_read(&bat_priv->distributed_arp_table))
@@ -1283,8 +1296,11 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
if (!dat_entry)
goto out;
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
- dat_entry->mac_addr, hw_src, vid);
+ mac, hw_src, vid);
if (!skb_new)
goto out;
@@ -1375,6 +1391,8 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
u8 *hw_src, *hw_dst;
bool dropped = false;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
@@ -1403,11 +1421,17 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
* this frame would lead to doubled receive of an ARP reply.
*/
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid);
- if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) {
- batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
- hw_src, &ip_src, hw_dst, &ip_dst,
- dat_entry->mac_addr, &dat_entry->ip);
- dropped = true;
+ if (dat_entry) {
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
+ if (batadv_compare_eth(hw_src, mac)) {
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
+ hw_src, &ip_src, hw_dst, &ip_dst,
+ mac, &dat_entry->ip);
+ dropped = true;
+ }
}
/* Update our internal cache with both the IP addresses the node got
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 417de98c39a7..650e9bacbb79 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -457,6 +457,10 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (!skb)
goto err_put;
+ /* Merged fragments re-enter here with reused skb metadata. */
+ skb->dev = dev;
+ skb->skb_iif = dev->ifindex;
+
/* packet should hold at least type and version */
if (unlikely(!pskb_may_pull(skb, 2)))
goto err_free;
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 4856cc98fc3c..2ebc718bc324 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -2295,7 +2295,7 @@ struct batadv_dat_entry {
__be32 ip;
/** @mac_addr: the MAC address associated to the stored IPv4 */
- u8 mac_addr[ETH_ALEN];
+ atomic64_t mac_addr;
/** @vid: the vlan ID associated to this entry */
unsigned short vid;
diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c
index 3e1713673ecc..773201672a27 100644
--- a/net/bluetooth/eir.c
+++ b/net/bluetooth/eir.c
@@ -366,6 +366,7 @@ u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr)
void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
{
+ const u8 *eir_end = eir + eir_len;
size_t dlen;
while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) {
@@ -378,7 +379,7 @@ void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
}
eir += dlen;
- eir_len -= dlen;
+ eir_len = eir_end - eir;
}
return NULL;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e4364a64ffd5..c13c50367408 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -288,8 +288,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
struct hci_cp_enhanced_setup_sync_conn cp;
const struct sco_param *param;
- kfree(conn_handle);
-
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
@@ -458,6 +456,15 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
return true;
}
+static void hci_enhanced_setup_sync_destroy(struct hci_dev *hdev, void *data,
+ int err)
+{
+ struct conn_handle_t *conn_handle = data;
+
+ hci_conn_put(conn_handle->conn);
+ kfree(conn_handle);
+}
+
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
int result;
@@ -469,12 +476,15 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
if (!conn_handle)
return false;
- conn_handle->conn = conn;
+ conn_handle->conn = hci_conn_get(conn);
conn_handle->handle = handle;
result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync,
- conn_handle, NULL);
- if (result < 0)
+ conn_handle,
+ hci_enhanced_setup_sync_destroy);
+ if (result < 0) {
+ hci_conn_put(conn);
kfree(conn_handle);
+ }
return result == 0;
}
@@ -1229,7 +1239,8 @@ static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
/* Enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
- hci_enable_advertising(hdev);
+ if (conn->role == HCI_ROLE_SLAVE)
+ hci_enable_advertising(hdev);
}
/* This function requires the caller holds hdev->lock */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d9bb05f81f50..cf9121f01fc6 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -4063,7 +4063,7 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND &&
!hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
kfree_skb(hdev->req_skb);
- hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
+ hdev->req_skb = skb_get(hdev->sent_cmd);
}
atomic_dec(&hdev->cmd_cnt);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 083b7db898fe..1a6fb75f2c1e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5646,10 +5646,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
- /* All controllers implicitly stop advertising in the event of a
- * connection, so ensure that the state bit is cleared.
+ /* Advertising stops when a connection is created. On a failed
+ * connection it keeps running, so leave the state bit alone.
*/
- hci_dev_clear_flag(hdev, HCI_LE_ADV);
+ if (!status)
+ hci_dev_clear_flag(hdev, HCI_LE_ADV);
/* Check for existing connection:
*
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index eff4dd8aae2a..7e9a45350596 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -611,8 +611,6 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
{
u8 instance = *(u8 *)data;
- kfree(data);
-
hci_clear_adv_instance_sync(hdev, NULL, instance, false);
if (list_empty(&hdev->adv_instances))
@@ -621,6 +619,12 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
return 0;
}
+static void adv_timeout_expire_destroy(struct hci_dev *hdev, void *data,
+ int err)
+{
+ kfree(data);
+}
+
static void adv_timeout_expire(struct work_struct *work)
{
u8 *inst_ptr;
@@ -641,7 +645,9 @@ static void adv_timeout_expire(struct work_struct *work)
goto unlock;
*inst_ptr = hdev->cur_adv_instance;
- hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, NULL);
+ if (hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr,
+ adv_timeout_expire_destroy) < 0)
+ kfree(inst_ptr);
unlock:
hci_dev_unlock(hdev);
@@ -5274,6 +5280,7 @@ int hci_dev_open_sync(struct hci_dev *hdev)
if (hdev->req_skb) {
kfree_skb(hdev->req_skb);
hdev->req_skb = NULL;
+ hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
}
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -5442,6 +5449,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
if (hdev->req_skb) {
kfree_skb(hdev->req_skb);
hdev->req_skb = NULL;
+ hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
}
clear_bit(HCI_RUNNING, &hdev->flags);
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index a67bce151aaf..ea5d8171fde7 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -1760,6 +1760,14 @@ static void iso_conn_ready(struct iso_conn *conn)
lock_sock(parent);
+ /* The listener may have been closed concurrently. */
+ if (parent->sk_state != BT_LISTEN ||
+ sock_flag(parent, SOCK_ZAPPED)) {
+ release_sock(parent);
+ sock_put(parent);
+ return;
+ }
+
sk = iso_sock_alloc(sock_net(parent), NULL,
BTPROTO_ISO, GFP_ATOMIC, 0);
if (!sk) {
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 89f881be9712..d09a0b0ffd0c 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1268,7 +1268,12 @@ static void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
+ /* Take lock to synchronize against access without owning sk->sk_socket,
+ * eg. in l2cap_sock_cleanup_listen(). proto_ops etc. don't need lock.
+ */
+ lock_sock(sk);
l2cap_sock_put_chan(sk);
+ release_sock(sk);
/* Kill poor orphan */
sock_set_flag(sk, SOCK_DEAD);
@@ -1471,14 +1476,10 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
* establish sk_lock -> conn->lock and invert the established
* conn->lock -> chan->lock -> sk_lock order (lockdep deadlock).
*
- * Instead, briefly take the child sk lock to fetch and pin its chan.
- * l2cap_conn_del() reaches the chan free only via
- * l2cap_chan_del() -> l2cap_sock_teardown_cb(), which itself takes
- * the child sk lock; holding it across l2cap_chan_hold_unless_zero()
- * therefore guarantees the chan cannot be freed while we read and
- * pin it (hold_unless_zero() additionally skips a chan already past
- * its last reference). We then drop the sk lock before taking
- * chan->lock, so sk and chan locks are never held together.
+ * Instead, briefly take the child sk lock to synchronize vs.
+ * l2cap_sock_kill that puts l2cap_pi(sk)->chan. We then drop the sk
+ * lock before taking chan->lock, so sk and chan locks are never held
+ * together.
*
* Since we cannot call l2cap_chan_close() without conn->lock,
* schedule l2cap_chan_timeout to close the channel; it already
@@ -1488,10 +1489,12 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
struct l2cap_chan *chan;
lock_sock_nested(sk, L2CAP_NESTING_NORMAL);
- chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
+ chan = l2cap_pi(sk)->chan;
+ if (chan)
+ l2cap_chan_hold(chan);
release_sock(sk);
if (!chan) {
- /* l2cap_conn_del() already tearing this child down */
+ /* Already torn down */
sock_put(sk);
continue;
}
@@ -1523,6 +1526,11 @@ static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
lock_sock(parent);
+ if (parent->sk_state != BT_LISTEN) {
+ release_sock(parent);
+ return -EINVAL;
+ }
+
/* Check for backlog size */
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -1687,10 +1695,14 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
if (!sk)
return;
+ lock_sock(sk);
+
sk->sk_state = state;
if (err)
sk->sk_err = err;
+
+ release_sock(sk);
}
static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
@@ -1766,6 +1778,8 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
if (!sk)
return;
+ lock_sock(sk);
+
if (test_and_clear_bit(FLAG_PENDING_SECURITY, &chan->flags)) {
sk->sk_state = BT_CONNECTED;
chan->state = BT_CONNECTED;
@@ -1773,6 +1787,8 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
sk->sk_state_change(sk);
+
+ release_sock(sk);
}
static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0422c88ca9ef..b4712ca23d85 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2404,11 +2404,15 @@ static int send_cancel(struct hci_dev *hdev, void *data)
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
0, NULL, 0);
- mgmt_pending_free(cmd);
return 0;
}
+static void send_cancel_destroy(struct hci_dev *hdev, void *data, int err)
+{
+ mgmt_pending_free(data);
+}
+
static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
@@ -2429,7 +2433,8 @@ static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev,
if (!cmd)
err = -ENOMEM;
else
- err = hci_cmd_sync_queue(hdev, send_cancel, cmd, NULL);
+ err = hci_cmd_sync_queue(hdev, send_cancel, cmd,
+ send_cancel_destroy);
if (err < 0) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
@@ -6143,6 +6148,7 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
struct mgmt_pending_cmd *cmd;
const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
u16 uuid_count, expected_len;
+ u8 (*uuids)[16] = NULL;
u8 status;
int err;
@@ -6219,12 +6225,10 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
hdev->discovery.result_filtering = true;
hdev->discovery.type = cp->type;
hdev->discovery.rssi = cp->rssi;
- hdev->discovery.uuid_count = uuid_count;
if (uuid_count > 0) {
- hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
- GFP_KERNEL);
- if (!hdev->discovery.uuids) {
+ uuids = kmemdup(cp->uuids, uuid_count * sizeof(*uuids), GFP_KERNEL);
+ if (!uuids) {
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_START_SERVICE_DISCOVERY,
MGMT_STATUS_FAILED,
@@ -6234,6 +6238,11 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
}
}
+ spin_lock(&hdev->discovery.lock);
+ hdev->discovery.uuids = uuids;
+ hdev->discovery.uuid_count = uuid_count;
+ spin_unlock(&hdev->discovery.lock);
+
err = hci_cmd_sync_queue(hdev, start_discovery_sync, cmd,
start_discovery_complete);
if (err < 0) {
@@ -10364,6 +10373,7 @@ static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir,
!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks))))
return false;
+ spin_lock(&hdev->discovery.lock);
if (hdev->discovery.uuid_count != 0) {
/* If a list of UUIDs is provided in filter, results with no
* matching UUID should be dropped.
@@ -10372,9 +10382,12 @@ static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir,
hdev->discovery.uuids) &&
!eir_has_uuids(scan_rsp, scan_rsp_len,
hdev->discovery.uuid_count,
- hdev->discovery.uuids))
+ hdev->discovery.uuids)) {
+ spin_unlock(&hdev->discovery.lock);
return false;
+ }
}
+ spin_unlock(&hdev->discovery.lock);
/* If duplicate filtering does not report RSSI changes, then restart
* scanning to ensure updated result with updated RSSI values.
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
index 9aee9bffaa7e..01592eb4704f 100644
--- a/net/bluetooth/msft.c
+++ b/net/bluetooth/msft.c
@@ -166,6 +166,11 @@ static bool read_supported_features(struct hci_dev *hdev,
if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
goto failed;
+ if (skb->len < sizeof(*rp) + rp->evt_prefix_len) {
+ bt_dev_err(hdev, "MSFT event prefix length mismatch");
+ goto failed;
+ }
+
if (rp->evt_prefix_len > 0) {
msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
GFP_KERNEL);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index fbc165aff369..390107643a76 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1458,6 +1458,10 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
d->mtu = __le16_to_cpu(pn->mtu);
+ /* MTU 0 causes an infinite loop when fragmenting in sendmsg */
+ if (!d->mtu)
+ d->mtu = RFCOMM_DEFAULT_MTU;
+
if (cr && d->mtu > s->mtu)
d->mtu = s->mtu;
@@ -2207,9 +2211,13 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
+ rfcomm_lock();
+
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
- if (!s)
+ if (!s) {
+ rfcomm_unlock();
return;
+ }
list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
@@ -2241,6 +2249,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
}
+ rfcomm_unlock();
+
rfcomm_schedule();
}
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index ce6f63c77cc0..86f0e75d6e34 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -196,7 +196,7 @@ static const struct nla_policy
br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
[IFLA_BRIDGE_MRP_START_TEST_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NLA_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1),
[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 },
@@ -316,7 +316,7 @@ static const struct nla_policy
br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = {
[IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NLA_U32 },
- [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1),
[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NLA_U32 },
};
diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c
index b006d1127c54..3ee0e14be727 100644
--- a/net/bridge/br_netlink_tunnel.c
+++ b/net/bridge/br_netlink_tunnel.c
@@ -302,7 +302,8 @@ int br_process_vlan_tunnel_info(const struct net_bridge *br,
if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
return -EINVAL;
- if ((tinfo_curr->vid - tinfo_last->vid) !=
+ if (tinfo_curr->vid < tinfo_last->vid ||
+ (tinfo_curr->vid - tinfo_last->vid) !=
(tinfo_curr->tunid - tinfo_last->tunid))
return -EINVAL;
t = tinfo_last->tunid;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 171d1dddb26a..67db91022207 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1129,7 +1129,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
if (err)
goto out;
- if (br_vlan_delete(br, old_pvid))
+ if (!br_vlan_delete(br, old_pvid))
br_vlan_notify(br, NULL, old_pvid, 0, RTM_DELVLAN);
br_vlan_notify(br, NULL, pvid, 0, RTM_NEWVLAN);
__set_bit(0, changed);
@@ -1151,7 +1151,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
&vlchange, extack);
if (err)
goto err_port;
- if (nbp_vlan_delete(p, old_pvid))
+ if (!nbp_vlan_delete(p, old_pvid))
br_vlan_notify(br, p, old_pvid, 0, RTM_DELVLAN);
br_vlan_notify(p->br, p, pvid, 0, RTM_NEWVLAN);
__set_bit(p->port_no, changed);
diff --git a/net/ceph/messenger_v2.c b/net/ceph/messenger_v2.c
index 89f2ab4b8e3a..f9ac4c4af2be 100644
--- a/net/ceph/messenger_v2.c
+++ b/net/ceph/messenger_v2.c
@@ -2176,6 +2176,11 @@ static int process_banner_prefix(struct ceph_connection *con)
payload_len = ceph_decode_16(&p);
dout("%s con %p payload_len %d\n", __func__, con, payload_len);
+ if (payload_len < sizeof(u64) + sizeof(u64)) {
+ con->error_msg = "protocol error, bad banner payload len";
+ return -EINVAL;
+ }
+
return prepare_read_banner_payload(con, payload_len);
}
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 8c1678f10440..2b51f951dd67 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -6,6 +6,7 @@
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/mm.h>
+#include <linux/overflow.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -5864,6 +5865,31 @@ static inline void convert_extent_map(struct ceph_sparse_read *sr)
#define MAX_EXTENTS 4096
+static bool sparse_extent_map_valid(struct ceph_sparse_read *sr)
+{
+ u64 req_end, pos;
+ int i;
+
+ if (check_add_overflow(sr->sr_req_off, sr->sr_req_len, &req_end))
+ return false;
+
+ pos = sr->sr_req_off;
+ for (i = 0; i < sr->sr_count; i++) {
+ struct ceph_sparse_extent *ext = &sr->sr_extent[i];
+ u64 end;
+
+ if (ext->off < pos)
+ return false;
+ if (check_add_overflow(ext->off, ext->len, &end))
+ return false;
+ if (end > req_end)
+ return false;
+ pos = end;
+ }
+
+ return true;
+}
+
static int osd_sparse_read(struct ceph_connection *con,
struct ceph_msg_data_cursor *cursor,
char **pbuf)
@@ -5922,6 +5948,10 @@ static int osd_sparse_read(struct ceph_connection *con,
fallthrough;
case CEPH_SPARSE_READ_DATA_LEN:
convert_extent_map(sr);
+ if (!sparse_extent_map_valid(sr)) {
+ pr_warn_ratelimited("invalid sparse extent map\n");
+ return -EREMOTEIO;
+ }
ret = sizeof(sr->sr_datalen);
*pbuf = (char *)&sr->sr_datalen;
sr->sr_state = CEPH_SPARSE_READ_DATA_PRE;
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index ff06ba2965cf..68bd45852fc3 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -519,6 +519,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
ceph_decode_need(p, end, 4*sizeof(u32), bad);
b->id = ceph_decode_32(p);
+ if (b->id != -1 - i)
+ goto bad;
b->type = ceph_decode_16(p);
if (b->type == 0)
goto bad;
diff --git a/net/core/dst.c b/net/core/dst.c
index 5ed8cb10748f..46cafba29991 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -153,7 +153,7 @@ void dst_dev_put(struct dst_entry *dst)
dst->ops->ifdown(dst, dev);
WRITE_ONCE(dst->input, dst_discard);
WRITE_ONCE(dst->output, dst_discard_out);
- dst->dev = blackhole_netdev;
+ rcu_assign_pointer(dst->dev_rcu, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev, &dst->dev_tracker,
GFP_ATOMIC);
}
@@ -266,7 +266,7 @@ unsigned int dst_blackhole_mtu(const struct dst_entry *dst)
{
unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
- return mtu ? : dst->dev->mtu;
+ return mtu ? : dst_dev(dst)->mtu;
}
EXPORT_SYMBOL_GPL(dst_blackhole_mtu);
diff --git a/net/core/filter.c b/net/core/filter.c
index 23e9deba098c..f568006baafa 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -8651,7 +8651,7 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct __sk_buff, sk):
+ case bpf_ctx_range_ptr(struct __sk_buff, sk):
if (type == BPF_WRITE || size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCK_COMMON_OR_NULL;
@@ -9220,7 +9220,7 @@ static bool sock_addr_is_valid_access(int off, int size,
return false;
}
break;
- case offsetof(struct bpf_sock_addr, sk):
+ case bpf_ctx_range_ptr(struct bpf_sock_addr, sk):
if (type != BPF_READ)
return false;
if (size != sizeof(__u64))
@@ -9274,17 +9274,17 @@ static bool sock_ops_is_valid_access(int off, int size,
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct bpf_sock_ops, sk):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET_OR_NULL;
break;
- case offsetof(struct bpf_sock_ops, skb_data):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET;
break;
- case offsetof(struct bpf_sock_ops, skb_data_end):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data_end):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET_END;
@@ -9293,7 +9293,7 @@ static bool sock_ops_is_valid_access(int off, int size,
bpf_ctx_record_field_size(info, size_default);
return bpf_ctx_narrow_access_ok(off, size,
size_default);
- case offsetof(struct bpf_sock_ops, skb_hwtstamp):
+ case bpf_ctx_range(struct bpf_sock_ops, skb_hwtstamp):
if (size != sizeof(__u64))
return false;
break;
@@ -9363,17 +9363,17 @@ static bool sk_msg_is_valid_access(int off, int size,
return false;
switch (off) {
- case offsetof(struct sk_msg_md, data):
+ case bpf_ctx_range_ptr(struct sk_msg_md, data):
info->reg_type = PTR_TO_PACKET;
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct sk_msg_md, data_end):
+ case bpf_ctx_range_ptr(struct sk_msg_md, data_end):
info->reg_type = PTR_TO_PACKET_END;
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct sk_msg_md, sk):
+ case bpf_ctx_range_ptr(struct sk_msg_md, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET;
@@ -10404,10 +10404,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
si->dst_reg, si->dst_reg, \
offsetof(OBJ, OBJ_FIELD)); \
if (si->dst_reg == si->src_reg) { \
- *insn++ = BPF_JMP_A(1); \
+ *insn++ = BPF_JMP_A(2); \
*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, \
temp)); \
+ *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \
} \
} while (0)
@@ -10441,10 +10442,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
si->dst_reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, sk));\
if (si->dst_reg == si->src_reg) { \
- *insn++ = BPF_JMP_A(1); \
+ *insn++ = BPF_JMP_A(2); \
*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \
offsetof(struct bpf_sock_ops_kern, \
temp)); \
+ *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \
} \
} while (0)
@@ -11584,7 +11586,7 @@ static bool sk_lookup_is_valid_access(int off, int size,
return false;
switch (off) {
- case offsetof(struct bpf_sk_lookup, sk):
+ case bpf_ctx_range_ptr(struct bpf_sk_lookup, sk):
info->reg_type = PTR_TO_SOCKET_OR_NULL;
return size == sizeof(__u64);
diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
index c5b90a309685..8c83b34ed58a 100644
--- a/net/core/lwt_bpf.c
+++ b/net/core/lwt_bpf.c
@@ -158,10 +158,10 @@ static int bpf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
return dst->lwtstate->orig_output(net, sk, skb);
}
-static int xmit_check_hhlen(struct sk_buff *skb, int hh_len)
+static int xmit_check_headroom(struct sk_buff *skb, int hroom)
{
- if (skb_headroom(skb) < hh_len) {
- int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb));
+ if (skb_headroom(skb) < hroom) {
+ int nhead = hroom - skb_headroom(skb);
if (pskb_expand_head(skb, nhead, 0, GFP_ATOMIC))
return -ENOMEM;
@@ -273,7 +273,7 @@ static int bpf_xmit(struct sk_buff *skb)
bpf = bpf_lwt_lwtunnel(dst->lwtstate);
if (bpf->xmit.prog) {
- int hh_len = dst->dev->hard_header_len;
+ int hroom = LL_RESERVED_SPACE(dst->dev);
__be16 proto = skb->protocol;
int ret;
@@ -289,9 +289,12 @@ static int bpf_xmit(struct sk_buff *skb)
return -EINVAL;
}
/* If the header was expanded, headroom might be too
- * small for L2 header to come, expand as needed.
+ * small for the L2 header to come, expand as needed.
+ * neigh_hh_output() copies the cached header in
+ * HH_DATA_MOD aligned chunks, so match the reservation
+ * made before LWT xmit.
*/
- ret = xmit_check_hhlen(skb, hh_len);
+ ret = xmit_check_headroom(skb, hroom);
if (unlikely(ret))
return ret;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index b99fdca441f3..99b54f469edf 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -472,8 +472,12 @@ void net_passive_dec(struct net *net)
if (refcount_dec_and_test(&net->passive)) {
kfree(rcu_access_pointer(net->gen));
+#ifdef CONFIG_REF_TRACKER
/* There should not be any trackers left there. */
ref_tracker_dir_exit(&net->notrefcnt_tracker);
+ if (!net->refcnt_tracker.dead)
+ ref_tracker_dir_exit(&net->refcnt_tracker);
+#endif
/* Wait for an extra rcu_barrier() before final free. */
llist_add(&net->defer_free_list, &defer_free_list);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 732204d99822..cfac251b003f 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1256,10 +1256,13 @@ EXPORT_SYMBOL(skb_dump);
*
* Report xmit error if a device callback is tracking this skb.
* skb must be freed afterwards.
+ *
+ * Does nothing for a cloned skb: the zerocopy state lives in
+ * skb_shinfo(), which the clones share.
*/
void skb_tx_error(struct sk_buff *skb)
{
- if (skb) {
+ if (skb && !skb_cloned(skb)) {
skb_zcopy_downgrade_managed(skb);
skb_zcopy_clear(skb, true);
}
@@ -3626,10 +3629,9 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen)
skb_len_add(to, len + plen);
- if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) {
- skb_tx_error(from);
+ if (unlikely(skb_orphan_frags(from, GFP_ATOMIC)))
return -ENOMEM;
- }
+
skb_zerocopy_clone(to, from, GFP_ATOMIC);
for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
diff --git a/net/core/sock.c b/net/core/sock.c
index 846e95805c19..2f82dc45f93e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2454,8 +2454,8 @@ static u32 sk_dst_gso_max_size(struct sock *sk, struct dst_entry *dst)
!ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr));
#endif
/* pairs with the WRITE_ONCE() in netif_set_gso(_ipv4)_max_size() */
- max_size = is_ipv6 ? READ_ONCE(dst->dev->gso_max_size) :
- READ_ONCE(dst->dev->gso_ipv4_max_size);
+ max_size = is_ipv6 ? READ_ONCE(dst_dev(dst)->gso_max_size) :
+ READ_ONCE(dst_dev(dst)->gso_ipv4_max_size);
if (max_size > GSO_LEGACY_MAX_SIZE && !sk_is_tcp(sk))
max_size = GSO_LEGACY_MAX_SIZE;
@@ -2466,7 +2466,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
{
u32 max_segs = 1;
- sk->sk_route_caps = dst->dev->features;
+ sk->sk_route_caps = dst_dev(dst)->features;
if (sk_is_tcp(sk))
sk->sk_route_caps |= NETIF_F_GSO;
if (sk->sk_route_caps & NETIF_F_GSO)
@@ -2480,7 +2480,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
sk->sk_gso_max_size = sk_dst_gso_max_size(sk, dst);
/* pairs with the WRITE_ONCE() in netif_set_gso_max_segs() */
- max_segs = max_t(u32, READ_ONCE(dst->dev->gso_max_segs), 1);
+ max_segs = max_t(u32, READ_ONCE(dst_dev(dst)->gso_max_segs), 1);
}
}
sk->sk_gso_max_segs = max_segs;
diff --git a/net/core/xdp.c b/net/core/xdp.c
index f640576090f2..672d971ce4ea 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -549,7 +549,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp)
xdp->data - xdp->data_meta;
totsize = xdp->data_end - xdp->data + metasize;
- if (sizeof(*xdpf) + totsize > PAGE_SIZE)
+ if (sizeof(*xdpf) + totsize > SKB_WITH_OVERHEAD(PAGE_SIZE))
return NULL;
page = dev_alloc_page();
@@ -566,7 +566,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp)
xdpf->data = addr + metasize;
xdpf->len = totsize - metasize;
- xdpf->headroom = 0;
+ xdpf->headroom = metasize;
xdpf->metasize = metasize;
xdpf->frame_sz = PAGE_SIZE;
xdpf->mem.type = MEM_TYPE_PAGE_ORDER0;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 1f748ed1279d..e4e055493203 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -267,7 +267,9 @@ struct sock *dccp_v4_request_recv_sock(const struct sock *sk, struct sk_buff *sk
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req);
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk));
struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index ca31c3b096bb..41667dcb6602 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -409,7 +409,9 @@ struct sock *dccp_v4_request_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct inet_request_sock *ireq;
struct inet_sock *newinet;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 2839ca8053ba..ae617bd8afab 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -417,7 +417,9 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct inet_request_sock *ireq = inet_rsk(req);
struct ipv6_pinfo *newnp;
@@ -432,7 +434,8 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
* v6 mapped
*/
newsk = dccp_v4_request_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
+ req_unhash, own_req,
+ opt_child_init);
if (newsk == NULL)
return NULL;
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 64d805b27add..0210d38442ec 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -193,7 +193,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
goto drop;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
- req, &own_req);
+ req, &own_req, NULL);
if (child) {
child = inet_csk_complete_hashdance(sk, child, req, own_req);
goto out;
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index b34a9b680aab..6dbb83b756df 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -139,30 +139,32 @@ static int hsr_dev_open(struct net_device *dev)
{
struct hsr_priv *hsr;
struct hsr_port *port;
- char designation;
+ const char *designation = NULL;
hsr = netdev_priv(dev);
- designation = '\0';
hsr_for_each_port_rtnl(hsr, port) {
if (port->type == HSR_PT_MASTER)
continue;
switch (port->type) {
case HSR_PT_SLAVE_A:
- designation = 'A';
+ designation = "Slave A";
break;
case HSR_PT_SLAVE_B:
- designation = 'B';
+ designation = "Slave B";
+ break;
+ case HSR_PT_INTERLINK:
+ designation = "Interlink";
break;
default:
- designation = '?';
+ designation = "Unknown";
}
if (!is_slave_up(port->dev))
- netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a fully working HSR network\n",
+ netdev_warn(dev, "%s (%s) is not up; please bring it up to get a fully working HSR network\n",
designation, port->dev->name);
}
- if (designation == '\0')
+ if (!designation)
netdev_warn(dev, "No slave devices configured\n");
return 0;
@@ -251,20 +253,22 @@ static const struct header_ops hsr_header_ops = {
.parse = eth_header_parse,
};
-static struct sk_buff *hsr_init_skb(struct hsr_port *master)
+static struct sk_buff *hsr_init_skb(struct hsr_port *master, int extra)
{
struct hsr_priv *hsr = master->hsr;
struct sk_buff *skb;
int hlen, tlen;
+ int len;
hlen = LL_RESERVED_SPACE(master->dev);
tlen = master->dev->needed_tailroom;
+ len = sizeof(struct hsr_sup_tag) + sizeof(struct hsr_sup_payload);
/* skb size is same for PRP/HSR frames, only difference
* being, for PRP it is a trailer and for HSR it is a
- * header
+ * header.
+ * RedBox might use @extra more bytes.
*/
- skb = dev_alloc_skb(sizeof(struct hsr_sup_tag) +
- sizeof(struct hsr_sup_payload) + hlen + tlen);
+ skb = dev_alloc_skb(len + extra + hlen + tlen);
if (!skb)
return skb;
@@ -296,8 +300,10 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
struct hsr_priv *hsr = master->hsr;
__u8 type = HSR_TLV_LIFE_CHECK;
struct hsr_sup_payload *hsr_sp;
+ struct hsr_sup_tlv *hsr_stlv;
struct hsr_sup_tag *hsr_stag;
struct sk_buff *skb;
+ int extra = 0;
*interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
if (hsr->announce_count < 3 && hsr->prot_version == 0) {
@@ -306,7 +312,11 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr->announce_count++;
}
- skb = hsr_init_skb(master);
+ if (hsr->redbox)
+ extra = sizeof(struct hsr_sup_tlv) +
+ sizeof(struct hsr_sup_payload);
+
+ skb = hsr_init_skb(master, extra);
if (!skb) {
netdev_warn_once(master->dev, "HSR: Could not send supervision frame\n");
return;
@@ -338,6 +348,16 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
+ if (hsr->redbox) {
+ hsr_stlv = skb_put(skb, sizeof(struct hsr_sup_tlv));
+ hsr_stlv->HSR_TLV_type = PRP_TLV_REDBOX_MAC;
+ hsr_stlv->HSR_TLV_length = sizeof(struct hsr_sup_payload);
+
+ /* Payload: MacAddressRedBox */
+ hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
+ ether_addr_copy(hsr_sp->macaddress_A, hsr->macaddress_redbox);
+ }
+
if (skb_put_padto(skb, ETH_ZLEN)) {
spin_unlock_bh(&hsr->seqnr_lock);
return;
@@ -356,7 +376,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
struct hsr_sup_tag *hsr_stag;
struct sk_buff *skb;
- skb = hsr_init_skb(master);
+ skb = hsr_init_skb(master, 0);
if (!skb) {
netdev_warn_once(master->dev, "PRP: Could not send supervision frame\n");
return;
@@ -419,6 +439,10 @@ void hsr_del_ports(struct hsr_priv *hsr)
if (port)
hsr_del_port(port);
+ port = hsr_port_get_hsr(hsr, HSR_PT_INTERLINK);
+ if (port)
+ hsr_del_port(port);
+
port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (port)
hsr_del_port(port);
@@ -624,8 +648,8 @@ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
};
int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
- unsigned char multicast_spec, u8 protocol_version,
- struct netlink_ext_ack *extack)
+ struct net_device *interlink, unsigned char multicast_spec,
+ u8 protocol_version, struct netlink_ext_ack *extack)
{
bool unregister = false;
struct hsr_priv *hsr;
@@ -634,6 +658,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr = netdev_priv(hsr_dev);
INIT_LIST_HEAD(&hsr->ports);
INIT_LIST_HEAD(&hsr->node_db);
+ INIT_LIST_HEAD(&hsr->proxy_node_db);
spin_lock_init(&hsr->list_lock);
eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
@@ -659,9 +684,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
/* Overflow soon to find bugs easier: */
hsr->sequence_nr = HSR_SEQNR_START;
hsr->sup_sequence_nr = HSR_SUP_SEQNR_START;
+ hsr->interlink_sequence_nr = HSR_SEQNR_START;
timer_setup(&hsr->announce_timer, hsr_announce, 0);
timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0);
+ timer_setup(&hsr->prune_proxy_timer, hsr_prune_proxy_nodes, 0);
ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr);
hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
@@ -698,6 +725,17 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
if (res)
goto err_unregister;
+ if (interlink) {
+ res = hsr_add_port(hsr, interlink, HSR_PT_INTERLINK, extack);
+ if (res)
+ goto err_unregister;
+
+ hsr->redbox = true;
+ ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr);
+ mod_timer(&hsr->prune_proxy_timer,
+ jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD));
+ }
+
hsr_debugfs_init(hsr, hsr_dev);
mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD));
@@ -707,6 +745,8 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr_del_ports(hsr);
err_add_master:
hsr_del_self_node(hsr);
+ hsr_del_nodes(&hsr->node_db);
+ hsr_del_nodes(&hsr->proxy_node_db);
if (unregister)
unregister_netdevice(hsr_dev);
diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h
index 9060c92168f9..655284095b78 100644
--- a/net/hsr/hsr_device.h
+++ b/net/hsr/hsr_device.h
@@ -16,8 +16,8 @@
void hsr_del_ports(struct hsr_priv *hsr);
void hsr_dev_setup(struct net_device *dev);
int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
- unsigned char multicast_spec, u8 protocol_version,
- struct netlink_ext_ack *extack);
+ struct net_device *interlink, unsigned char multicast_spec,
+ u8 protocol_version, struct netlink_ext_ack *extack);
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr);
int hsr_get_max_mtu(struct hsr_priv *hsr);
#endif /* __HSR_DEVICE_H */
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 7a596c4f603e..2a6df958292c 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -379,6 +379,15 @@ static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,
*/
ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr);
}
+
+ /* When HSR node is used as RedBox - the frame received from HSR ring
+ * requires source MAC address (SA) replacement to one which can be
+ * recognized by SAN devices (otherwise, frames are dropped by switch)
+ */
+ if (port->type == HSR_PT_INTERLINK)
+ ether_addr_copy(eth_hdr(skb)->h_source,
+ port->hsr->macaddress_redbox);
+
return dev_queue_xmit(skb);
}
@@ -392,9 +401,57 @@ bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
{
+ struct sk_buff *skb;
+
if (port->dev->features & NETIF_F_HW_HSR_FWD)
return prp_drop_frame(frame, port);
+ /* RedBox specific frames dropping policies
+ *
+ * Do not send HSR supervisory frames to SAN devices
+ */
+ if (frame->is_supervision && port->type == HSR_PT_INTERLINK)
+ return true;
+
+ /* Do not forward to other HSR port (A or B) unicast frames which
+ * are addressed to interlink port (and are in the ProxyNodeTable).
+ */
+ skb = frame->skb_hsr;
+ if (skb && prp_drop_frame(frame, port) &&
+ is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
+ hsr_is_node_in_db(&port->hsr->proxy_node_db,
+ eth_hdr(skb)->h_dest)) {
+ return true;
+ }
+
+ /* Do not forward to port C (Interlink) frames from nodes A and B
+ * if DA is in NodeTable.
+ */
+ if ((frame->port_rcv->type == HSR_PT_SLAVE_A ||
+ frame->port_rcv->type == HSR_PT_SLAVE_B) &&
+ port->type == HSR_PT_INTERLINK) {
+ skb = frame->skb_hsr;
+ if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
+ hsr_is_node_in_db(&port->hsr->node_db,
+ eth_hdr(skb)->h_dest)) {
+ return true;
+ }
+ }
+
+ /* Do not forward to port A and B unicast frames received on the
+ * interlink port if it is addressed to one of nodes registered in
+ * the ProxyNodeTable.
+ */
+ if ((port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) &&
+ frame->port_rcv->type == HSR_PT_INTERLINK) {
+ skb = frame->skb_std;
+ if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
+ hsr_is_node_in_db(&port->hsr->proxy_node_db,
+ eth_hdr(skb)->h_dest)) {
+ return true;
+ }
+ }
+
return false;
}
@@ -450,13 +507,14 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
}
/* Check if frame is to be dropped. Eg. for PRP no forward
- * between ports.
+ * between ports, or sending HSR supervision to RedBox.
*/
if (hsr->proto_ops->drop_frame &&
hsr->proto_ops->drop_frame(frame, port))
continue;
- if (port->type != HSR_PT_MASTER)
+ if (port->type == HSR_PT_SLAVE_A ||
+ port->type == HSR_PT_SLAVE_B)
skb = hsr->proto_ops->create_tagged_frame(frame, port);
else
skb = hsr->proto_ops->get_untagged_frame(frame, port);
@@ -471,7 +529,9 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
hsr_deliver_master(skb, port->dev, frame->node_src);
} else {
if (!hsr_xmit(skb, port, frame))
- sent = true;
+ if (port->type == HSR_PT_SLAVE_A ||
+ port->type == HSR_PT_SLAVE_B)
+ sent = true;
}
}
}
@@ -505,10 +565,12 @@ static void handle_std_frame(struct sk_buff *skb,
frame->skb_prp = NULL;
frame->skb_std = skb;
- if (port->type != HSR_PT_MASTER) {
+ if (port->type != HSR_PT_MASTER)
frame->is_from_san = true;
- } else {
- /* Sequence nr for the master node */
+
+ if (port->type == HSR_PT_MASTER ||
+ port->type == HSR_PT_INTERLINK) {
+ /* Sequence nr for the master/interlink node */
lockdep_assert_held(&hsr->seqnr_lock);
frame->sequence_nr = hsr->sequence_nr;
hsr->sequence_nr++;
@@ -566,6 +628,7 @@ static int fill_frame_info(struct hsr_frame_info *frame,
{
struct hsr_priv *hsr = port->hsr;
struct hsr_vlan_ethhdr *vlan_hdr;
+ struct list_head *n_db;
struct ethhdr *ethhdr;
__be16 proto;
int ret;
@@ -576,9 +639,13 @@ static int fill_frame_info(struct hsr_frame_info *frame,
memset(frame, 0, sizeof(*frame));
frame->is_supervision = is_supervision_frame(port->hsr, skb);
- frame->node_src = hsr_get_node(port, &hsr->node_db, skb,
- frame->is_supervision,
- port->type);
+
+ n_db = &hsr->node_db;
+ if (port->type == HSR_PT_INTERLINK)
+ n_db = &hsr->proxy_node_db;
+
+ frame->node_src = hsr_get_node(port, n_db, skb,
+ frame->is_supervision, port->type);
if (!frame->node_src)
return -1; /* Unknown node and !is_supervision, or no mem */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 9348fc0b3961..35c2fae8d1f7 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -69,6 +69,14 @@ static struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
return NULL;
}
+/* Check if node for a given MAC address is already present in data base
+ */
+bool hsr_is_node_in_db(struct list_head *node_db,
+ const unsigned char addr[ETH_ALEN])
+{
+ return !!find_node_by_addr_A(node_db, addr);
+}
+
/* Helper for device init; the self_node is used in hsr_rcv() to recognize
* frames from self that's been looped over the HSR ring.
*/
@@ -223,6 +231,15 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
}
}
+ /* Check if required node is not in proxy nodes table */
+ list_for_each_entry_rcu(node, &hsr->proxy_node_db, mac_list) {
+ if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
+ if (hsr->proto_ops->update_san_info)
+ hsr->proto_ops->update_san_info(node, is_sup);
+ return node;
+ }
+ }
+
/* Everyone may create a node entry, connected node to a HSR/PRP
* device.
*/
@@ -418,6 +435,10 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
node_dst = find_node_by_addr_A(&port->hsr->node_db,
eth_hdr(skb)->h_dest);
+ if (!node_dst && port->hsr->redbox)
+ node_dst = find_node_by_addr_A(&port->hsr->proxy_node_db,
+ eth_hdr(skb)->h_dest);
+
if (!node_dst) {
if (port->hsr->prot_version != PRP_V1 && net_ratelimit())
netdev_err(skb->dev, "%s: Unknown node\n", __func__);
@@ -561,6 +582,37 @@ void hsr_prune_nodes(struct timer_list *t)
jiffies + msecs_to_jiffies(PRUNE_PERIOD));
}
+void hsr_prune_proxy_nodes(struct timer_list *t)
+{
+ struct hsr_priv *hsr = from_timer(hsr, t, prune_proxy_timer);
+ unsigned long timestamp;
+ struct hsr_node *node;
+ struct hsr_node *tmp;
+
+ spin_lock_bh(&hsr->list_lock);
+ list_for_each_entry_safe(node, tmp, &hsr->proxy_node_db, mac_list) {
+ timestamp = node->time_in[HSR_PT_INTERLINK];
+
+ /* Prune old entries */
+ if (time_is_before_jiffies(timestamp +
+ msecs_to_jiffies(HSR_PROXY_NODE_FORGET_TIME))) {
+ hsr_nl_nodedown(hsr, node->macaddress_A);
+ if (!node->removed) {
+ list_del_rcu(&node->mac_list);
+ node->removed = true;
+ /* Note that we need to free this entry later: */
+ kfree_rcu(node, rcu_head);
+ }
+ }
+ }
+
+ spin_unlock_bh(&hsr->list_lock);
+
+ /* Restart timer */
+ mod_timer(&hsr->prune_proxy_timer,
+ jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD));
+}
+
void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
unsigned char addr[ETH_ALEN])
{
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index b23556251d62..7619e31c1d2d 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -46,6 +46,7 @@ int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr);
void hsr_prune_nodes(struct timer_list *t);
+void hsr_prune_proxy_nodes(struct timer_list *t);
int hsr_create_self_node(struct hsr_priv *hsr,
const unsigned char addr_a[ETH_ALEN],
@@ -67,6 +68,9 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
struct hsr_node *node);
void prp_update_san_info(struct hsr_node *node, bool is_sup);
+bool hsr_is_node_in_db(struct list_head *node_db,
+ const unsigned char addr[ETH_ALEN]);
+
struct hsr_node {
struct list_head mac_list;
/* Protect R/W access to seq_out */
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
index 2fcabe39e61f..56ee014f5440 100644
--- a/net/hsr/hsr_main.h
+++ b/net/hsr/hsr_main.h
@@ -21,6 +21,7 @@
*/
#define HSR_LIFE_CHECK_INTERVAL 2000 /* ms */
#define HSR_NODE_FORGET_TIME 60000 /* ms */
+#define HSR_PROXY_NODE_FORGET_TIME 60000 /* ms */
#define HSR_ANNOUNCE_INTERVAL 100 /* ms */
#define HSR_ENTRY_FORGET_TIME 400 /* ms */
@@ -35,6 +36,7 @@
* HSR_NODE_FORGET_TIME?
*/
#define PRUNE_PERIOD 3000 /* ms */
+#define PRUNE_PROXY_PERIOD 3000 /* ms */
#define HSR_TLV_EOT 0 /* End of TLVs */
#define HSR_TLV_ANNOUNCE 22
#define HSR_TLV_LIFE_CHECK 23
@@ -192,11 +194,14 @@ struct hsr_priv {
struct rcu_head rcu_head;
struct list_head ports;
struct list_head node_db; /* Known HSR nodes */
+ struct list_head proxy_node_db; /* RedBox HSR proxy nodes */
struct hsr_self_node __rcu *self_node; /* MACs of slaves */
struct timer_list announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer;
+ struct timer_list prune_proxy_timer;
int announce_count;
u16 sequence_nr;
+ u16 interlink_sequence_nr; /* Interlink port seq_nr */
u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */
enum hsr_version prot_version; /* Indicate if HSRv0, HSRv1 or PRPv1 */
spinlock_t seqnr_lock; /* locking for sequence_nr */
@@ -209,6 +214,8 @@ struct hsr_priv {
* of lan_id
*/
bool fwd_offloaded; /* Forwarding offloaded to HW */
+ bool redbox; /* Device supports HSR RedBox */
+ unsigned char macaddress_redbox[ETH_ALEN];
unsigned char sup_multicast_addr[ETH_ALEN] __aligned(sizeof(u16));
/* Align to u16 boundary to avoid unaligned access
* in ether_addr_equal
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index 78fe40eb9f01..898f18c6da53 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -23,6 +23,7 @@ static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
[IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN },
[IFLA_HSR_SEQ_NR] = { .type = NLA_U16 },
[IFLA_HSR_PROTOCOL] = { .type = NLA_U8 },
+ [IFLA_HSR_INTERLINK] = { .type = NLA_U32 },
};
/* Here, it seems a netdevice has already been allocated for us, and the
@@ -35,8 +36,8 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
enum hsr_version proto_version;
unsigned char multicast_spec;
u8 proto = HSR_PROTOCOL_HSR;
- struct net_device *link[2];
+ struct net_device *link[2], *interlink = NULL;
if (!data) {
NL_SET_ERR_MSG_MOD(extack, "No slave devices specified");
return -EINVAL;
@@ -67,6 +68,20 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
return -EINVAL;
}
+ if (data[IFLA_HSR_INTERLINK])
+ interlink = __dev_get_by_index(src_net,
+ nla_get_u32(data[IFLA_HSR_INTERLINK]));
+
+ if (interlink && interlink == link[0]) {
+ NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave1 are the same");
+ return -EINVAL;
+ }
+
+ if (interlink && interlink == link[1]) {
+ NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave2 are the same");
+ return -EINVAL;
+ }
+
if (!data[IFLA_HSR_MULTICAST_SPEC])
multicast_spec = 0;
else
@@ -96,10 +111,17 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
}
}
- if (proto == HSR_PROTOCOL_PRP)
+ if (proto == HSR_PROTOCOL_PRP) {
proto_version = PRP_V1;
+ if (interlink) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Interlink only works with HSR");
+ return -EINVAL;
+ }
+ }
- return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack);
+ return hsr_dev_finalize(dev, link, interlink, multicast_spec,
+ proto_version, extack);
}
static void hsr_dellink(struct net_device *dev, struct list_head *head)
@@ -107,6 +129,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head)
struct hsr_priv *hsr = netdev_priv(dev);
del_timer_sync(&hsr->prune_timer);
+ del_timer_sync(&hsr->prune_proxy_timer);
del_timer_sync(&hsr->announce_timer);
hsr_debugfs_term(hsr);
@@ -114,6 +137,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head)
hsr_del_self_node(hsr);
hsr_del_nodes(&hsr->node_db);
+ hsr_del_nodes(&hsr->proxy_node_db);
unregister_netdevice_queue(dev, head);
}
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index 58e757dc5554..75566a1fbd76 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -55,6 +55,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
protocol = eth_hdr(skb)->h_proto;
if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) &&
+ port->type != HSR_PT_INTERLINK &&
hsr->proto_ops->invalid_dan_ingress_frame &&
hsr->proto_ops->invalid_dan_ingress_frame(protocol))
goto finish_pass;
@@ -72,7 +73,16 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
}
skb_reset_mac_len(skb);
- hsr_forward_skb(skb, port);
+ /* Only the frames received over the interlink port will assign a
+ * sequence number and require synchronisation vs other sender.
+ */
+ if (port->type == HSR_PT_INTERLINK) {
+ spin_lock_bh(&hsr->seqnr_lock);
+ hsr_forward_skb(skb, port);
+ spin_unlock_bh(&hsr->seqnr_lock);
+ } else {
+ hsr_forward_skb(skb, port);
+ }
finish_consume:
return RX_HANDLER_CONSUMED;
@@ -138,9 +148,12 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
int res;
/* Don't use promiscuous mode for offload since L2 frame forward
- * happens at the offloaded hardware.
+ * happens at the offloaded hardware. The interlink port never
+ * gets forwarding offload (RedBox forwarding to/from it is done
+ * by this driver), so it still needs promiscuous mode to receive
+ * frames addressed to hsr_dev's MAC rather than its own.
*/
- if (!port->hsr->fwd_offloaded) {
+ if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK) {
res = dev_set_promiscuity(dev, 1);
if (res)
return res;
@@ -163,7 +176,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
fail_rx_handler:
netdev_upper_dev_unlink(dev, hsr_dev);
fail_upper_dev_link:
- if (!port->hsr->fwd_offloaded)
+ if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK)
dev_set_promiscuity(dev, -1);
return res;
@@ -226,7 +239,7 @@ void hsr_del_port(struct hsr_port *port)
netdev_update_features(master->dev);
dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
netdev_rx_handler_unregister(port->dev);
- if (!port->hsr->fwd_offloaded)
+ if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK)
dev_set_promiscuity(port->dev, -1);
if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
vlan_vids_del_by_dev(port->dev, master->dev);
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 7c4c7b7fe384..5c38af80bc14 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -313,22 +313,24 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
{
struct dst_entry *dst = &rt->dst;
struct inet_peer *peer;
+ struct net_device *dev;
bool rc = true;
if (!apply_ratelimit)
return true;
/* No rate limit on loopback */
- if (dst->dev && (dst->dev->flags&IFF_LOOPBACK))
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
+ if (dev && (dev->flags & IFF_LOOPBACK))
goto out;
- rcu_read_lock();
peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr,
- l3mdev_master_ifindex_rcu(dst->dev));
+ l3mdev_master_ifindex_rcu(dev));
rc = inet_peer_xrlim_allow(peer,
READ_ONCE(net->ipv4.sysctl_icmp_ratelimit));
- rcu_read_unlock();
out:
+ rcu_read_unlock();
if (!rc)
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
else
@@ -472,13 +474,13 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
*/
static struct net_device *icmp_get_route_lookup_dev(struct sk_buff *skb)
{
- struct net_device *route_lookup_dev = NULL;
+ struct net_device *dev = skb->dev;
+ const struct dst_entry *dst;
- if (skb->dev)
- route_lookup_dev = skb->dev;
- else if (skb_dst(skb))
- route_lookup_dev = skb_dst(skb)->dev;
- return route_lookup_dev;
+ if (dev)
+ return dev;
+ dst = skb_dst(skb);
+ return dst ? dst_dev(dst) : NULL;
}
static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
@@ -911,7 +913,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
struct net *net;
u32 info = 0;
- net = dev_net_rcu(skb_dst(skb)->dev);
+ net = skb_dst_dev_net_rcu(skb);
/*
* Incomplete header ?
@@ -1054,7 +1056,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
struct icmp_bxm icmp_param;
struct net *net;
- net = dev_net_rcu(skb_dst(skb)->dev);
+ net = skb_dst_dev_net_rcu(skb);
/* should there be an ICMP stat for ignored echos? */
if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
return SKB_NOT_DROPPED_YET;
@@ -1231,7 +1233,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
return SKB_NOT_DROPPED_YET;
out_err:
- __ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
+ __ICMP_INC_STATS(skb_dst_dev_net_rcu(skb), ICMP_MIB_INERRORS);
return SKB_DROP_REASON_PKT_TOO_SMALL;
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 732804f3a0aa..8509e9473834 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -428,7 +428,7 @@ static int igmpv3_sendpack(struct sk_buff *skb)
pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
- return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
+ return ip_local_out(skb_dst_dev_net(skb), skb->sk, skb);
}
static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 208f8b173a05..530d495ff7d2 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1252,6 +1252,15 @@ void inet_csk_destroy_sock(struct sock *sk)
}
EXPORT_SYMBOL(inet_csk_destroy_sock);
+void inet_csk_prepare_for_destroy_sock(struct sock *sk)
+{
+ /* The below has to be done to allow calling inet_csk_destroy_sock */
+ if (sk->sk_protocol == IPPROTO_TCP)
+ tcp_clear_sock_ops_cb_flags(sk);
+ sock_set_flag(sk, SOCK_DEAD);
+ this_cpu_inc(*sk->sk_prot->orphan_count);
+}
+
/* This function allows to force a closure of a socket after the call to
* tcp/dccp_create_openreq_child().
*/
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index 23896b6b8417..21a9c6eff1d7 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -22,6 +22,7 @@
#include <net/ip.h>
#include <net/inetpeer.h>
#include <net/secure_seq.h>
+#include <linux/siphash.h>
/*
* Theory of operations.
@@ -53,6 +54,34 @@
*/
static struct kmem_cache *peer_cachep __ro_after_init;
+static siphash_aligned_key_t inetpeer_hash_key __read_mostly;
+
+static u64 inetpeer_addr_hash(const struct inetpeer_addr *a)
+{
+ net_get_random_once(&inetpeer_hash_key, sizeof(inetpeer_hash_key));
+
+ if (a->family == AF_INET)
+ return siphash_2u32((__force u32)a->a4.addr, a->a4.vif,
+ &inetpeer_hash_key);
+
+ return siphash_4u32((__force u32)a->a6.s6_addr32[0],
+ (__force u32)a->a6.s6_addr32[1],
+ (__force u32)a->a6.s6_addr32[2],
+ (__force u32)a->a6.s6_addr32[3],
+ &inetpeer_hash_key);
+}
+
+static int inetpeer_entry_cmp(u64 dhash,
+ const struct inetpeer_addr *daddr,
+ const struct inet_peer *p)
+{
+ if (dhash < p->hash)
+ return -1;
+ if (dhash > p->hash)
+ return 1;
+
+ return inetpeer_addr_cmp(daddr, &p->daddr);
+}
void inet_peer_base_init(struct inet_peer_base *bp)
{
@@ -89,6 +118,7 @@ void __init inet_initpeers(void)
/* Called with rcu_read_lock() or base->lock held */
static struct inet_peer *lookup(const struct inetpeer_addr *daddr,
+ u64 dhash,
struct inet_peer_base *base,
unsigned int seq,
struct inet_peer *gc_stack[],
@@ -110,7 +140,7 @@ static struct inet_peer *lookup(const struct inetpeer_addr *daddr,
break;
parent = next;
p = rb_entry(parent, struct inet_peer, rb_node);
- cmp = inetpeer_addr_cmp(daddr, &p->daddr);
+ cmp = inetpeer_entry_cmp(dhash, daddr, p);
if (cmp == 0) {
now = jiffies;
if (READ_ONCE(p->dtime) != now)
@@ -180,6 +210,7 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
const struct inetpeer_addr *daddr)
{
struct inet_peer *p, *gc_stack[PEER_MAX_GC];
+ u64 dhash = inetpeer_addr_hash(daddr);
struct rb_node **pp, *parent;
unsigned int gc_cnt, seq;
@@ -187,7 +218,7 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
* Because of a concurrent writer, we might not find an existing entry.
*/
seq = read_seqbegin(&base->lock);
- p = lookup(daddr, base, seq, NULL, &gc_cnt, &parent, &pp);
+ p = lookup(daddr, dhash, base, seq, NULL, &gc_cnt, &parent, &pp);
if (p)
return p;
@@ -199,11 +230,12 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
write_seqlock_bh(&base->lock);
gc_cnt = 0;
- p = lookup(daddr, base, seq, gc_stack, &gc_cnt, &parent, &pp);
+ p = lookup(daddr, dhash, base, seq, gc_stack, &gc_cnt, &parent, &pp);
if (!p) {
p = kmem_cache_alloc(peer_cachep, GFP_ATOMIC);
if (p) {
p->daddr = *daddr;
+ p->hash = dhash;
p->dtime = (__u32)jiffies;
refcount_set(&p->refcnt, 1);
atomic_set(&p->rid, 0);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 8524f2ac2cf3..e79ebe911548 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -470,14 +470,16 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
/* Process an incoming IP datagram fragment. */
int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
{
- struct net_device *dev = skb->dev ? : skb_dst(skb)->dev;
- int vif = l3mdev_master_ifindex_rcu(dev);
+ struct net_device *dev;
struct ipq *qp;
+ int vif;
__IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);
/* Lookup (or create) queue header */
rcu_read_lock();
+ dev = skb->dev ? : skb_dst_dev_rcu(skb);
+ vif = l3mdev_master_ifindex_rcu(dev);
qp = ip_find(net, ip_hdr(skb), user, vif);
if (qp) {
int ret, refs = 0;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 150cdea8aacb..3bb198b2c101 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -116,7 +116,7 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
skb->protocol = htons(ETH_P_IP);
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
- net, sk, skb, NULL, skb_dst(skb)->dev,
+ net, sk, skb, NULL, skb_dst_dev(skb),
dst_output);
}
@@ -199,7 +199,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
- struct net_device *dev = dst->dev;
+ struct net_device *dev = dst_dev(dst);
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
bool is_v6gw = false;
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 090403c8cc6c..d250ed84f32b 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -316,7 +316,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
mtu = min(tdev->mtu, IP_MAX_MTU);
}
- dev->needed_headroom = t_hlen + hlen;
+ dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
mtu -= t_hlen + (dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0);
if (mtu < IPV4_MIN_MTU)
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 02744447e435..cd61a417c44d 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -226,7 +226,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
goto tx_error_icmp;
}
- tdev = dst->dev;
+ tdev = dst_dev(dst);
if (tdev == dev) {
dst_release(dst);
@@ -256,7 +256,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
xmit:
skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
skb_dst_set(skb, dst);
- skb->dev = skb_dst(skb)->dev;
+ skb->dev = skb_dst_dev(skb);
err = dst_output(tunnel->net, skb->sk, skb);
if (net_xmit_eval(err) == 0)
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index b7e935299153..4768904b1e62 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -240,7 +240,7 @@ static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto)
if (tunnel->collect_md) {
tun_dst = ip_tun_rx_dst(skb, 0, 0, 0);
if (!tun_dst)
- return 0;
+ goto drop;
ip_tunnel_md_udp_encap(skb, &tun_dst->u.tun_info);
}
skb_reset_mac_header(skb);
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index af9412a507cf..f39cd565569a 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1893,7 +1893,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
goto out_free;
}
- dev = rt->dst.dev;
+ dev = dst_dev_rcu(&rt->dst);
if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
/* Do not fragment multicasts. Alas, IPv4 does not
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index bd135165482a..c450509d6efd 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -19,12 +19,12 @@
/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type)
{
+ struct net_device *dev = skb_dst_dev(skb);
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
struct flowi4 fl4 = {};
__be32 saddr = iph->saddr;
__u8 flags;
- struct net_device *dev = skb_dst(skb)->dev;
struct flow_keys flkeys;
unsigned int hh_len;
@@ -73,7 +73,7 @@ int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, un
#endif
/* Change in oif may mean change in hh_len. */
- hh_len = skb_dst(skb)->dev->hard_header_len;
+ hh_len = skb_dst_dev(skb)->hard_header_len;
if (skb_headroom(skb) < hh_len &&
pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
0, GFP_ATOMIC))
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 789953096c80..b175a8292eb3 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -416,11 +416,11 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
const void *daddr)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net_device *dev = dst->dev;
+ struct net_device *dev;
struct neighbour *n;
rcu_read_lock();
-
+ dev = dst_dev_rcu(dst);
if (likely(rt->rt_gw_family == AF_INET)) {
n = ip_neigh_gw4(dev, rt->rt_gw4);
} else if (rt->rt_gw_family == AF_INET6) {
@@ -443,7 +443,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net_device *dev = dst->dev;
+ struct net_device *dev = dst_dev(dst);
const __be32 *pkey = daddr;
if (rt->rt_gw_family == AF_INET) {
@@ -1069,7 +1069,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
return;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dst_dev_net_rcu(dst);
if (mtu < net->ipv4.ip_rt_min_pmtu) {
lock = true;
mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
@@ -1367,7 +1367,7 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
struct net *net;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dst_dev_net_rcu(dst);
advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
net->ipv4.ip_rt_min_advmss);
rcu_read_unlock();
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index e14356207795..9deeb3f8215e 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -199,7 +199,7 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
bool own_req;
child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
- NULL, &own_req);
+ NULL, &own_req, NULL);
if (child) {
refcount_set(&req->rsk_refcnt, 1);
tcp_sk(child)->tsoffset = tsoff;
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 408985eb74ee..e874ec597014 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -247,7 +247,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
bool own_req;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
- NULL, &own_req);
+ NULL, &own_req, NULL);
if (!child)
return NULL;
@@ -558,6 +558,7 @@ bool tcp_fastopen_active_should_disable(struct sock *sk)
void tcp_fastopen_active_disable_ofo_check(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct net_device *dev;
struct dst_entry *dst;
struct sk_buff *skb;
@@ -575,7 +576,8 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk)
} else if (tp->syn_fastopen_ch &&
atomic_read(&sock_net(sk)->ipv4.tfo_active_disable_times)) {
dst = sk_dst_get(sk);
- if (!(dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK)))
+ dev = dst ? dst_dev(dst) : NULL;
+ if (!(dev && (dev->flags & IFF_LOOPBACK)))
atomic_set(&sock_net(sk)->ipv4.tfo_active_disable_times, 0);
dst_release(dst);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 3f9e1cfde008..638303d6691e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -732,7 +732,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_base = (unsigned char *)&rep;
arg.iov[0].iov_len = sizeof(rep.th);
- net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
+ net = sk ? sock_net(sk) : skb_dst_dev_net(skb);
+
#ifdef CONFIG_TCP_MD5SIG
rcu_read_lock();
hash_location = tcp_parse_md5sig_option(th);
@@ -1566,7 +1567,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct inet_request_sock *ireq;
bool found_dup_sk = false;
@@ -1622,6 +1625,10 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
}
sk_setup_caps(newsk, dst);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (opt_child_init)
+ opt_child_init(newsk, sk);
+#endif
tcp_ca_openreq_child(newsk, dst);
tcp_sync_mss(newsk, dst_mtu(dst));
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index e0883ba709b0..49983d864e4f 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -166,11 +166,11 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
unsigned int hash)
{
struct tcp_metrics_block *tm;
- struct net *net;
bool reclaim = false;
+ struct net *net;
spin_lock_bh(&tcp_metrics_lock);
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
/* While waiting for the spin-lock the cache might have been populated
* with this entry and so we have to check again.
@@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
return NULL;
}
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
hash ^= net_hash_mix(net);
hash = hash_32(hash, tcp_metrics_hash_log);
@@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,
else
return NULL;
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
hash ^= net_hash_mix(net);
hash = hash_32(hash, tcp_metrics_hash_log);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 86f0feb2497f..d6d64697ce89 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -817,7 +817,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* socket is created, wait for troubles.
*/
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
- req, &own_req);
+ req, &own_req, NULL);
if (!child)
goto listen_overflow;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5abad93d3c74..93817ce0dca9 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3317,7 +3317,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
avail_wnd = cur_mss;
}
- len = cur_mss * segs;
+ len = cur_mss * (tcp_urg_mode(tp) ? 1 : segs);
if (len > avail_wnd) {
len = rounddown(avail_wnd, cur_mss);
if (!len)
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 3cff51ba72bb..0ae67d537499 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -31,7 +31,7 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
- net, sk, skb, skb->dev, skb_dst(skb)->dev,
+ net, sk, skb, skb->dev, skb_dst_dev(skb),
__xfrm4_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 74442c9fb4ab..ef94bce1d2d5 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1150,13 +1150,11 @@ static void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
return;
if (rt->dst.dev) {
- unsigned short dst_len = rt->dst.dev->hard_header_len +
- t_hlen;
+ unsigned int headroom;
- if (t->dev->header_ops)
- dev->hard_header_len = dst_len;
- else
- dev->needed_headroom = dst_len;
+ headroom = rt->dst.dev->hard_header_len + t_hlen;
+ headroom = ip_tunnel_limit_headroom(headroom);
+ dev->needed_headroom = headroom;
if (set_mtu) {
int mtu = rt->dst.dev->mtu - t_hlen;
@@ -1184,8 +1182,8 @@ static int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
- if (tunnel->dev->header_ops)
- tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+ if (tunnel->dev->header_ops && tunnel->dev->type == ARPHRD_IP6GRE)
+ tunnel->dev->hard_header_len = t_hlen;
else
tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index dfabd9969b3f..0deea4870057 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1226,19 +1226,8 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
*/
max_headroom += LL_RESERVED_SPACE(tdev);
- if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
- (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
- struct sk_buff *new_skb;
-
- new_skb = skb_realloc_headroom(skb, max_headroom);
- if (!new_skb)
- goto tx_err_dst_release;
-
- if (skb->sk)
- skb_set_owner_w(new_skb, skb->sk);
- consume_skb(skb);
- skb = new_skb;
- }
+ if (skb_cow_head(skb, max_headroom))
+ goto tx_err_dst_release;
if (t->parms.collect_md) {
if (t->encap.type != TUNNEL_ENCAP_NONE)
@@ -1515,8 +1504,11 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
tdev = __dev_get_by_index(t->net, p->link);
if (tdev) {
- dev->needed_headroom = tdev->hard_header_len +
- tdev->needed_headroom + t_hlen;
+ unsigned int headroom;
+
+ headroom = tdev->hard_header_len + tdev->needed_headroom;
+ headroom += t_hlen;
+ dev->needed_headroom = ip_tunnel_limit_headroom(headroom);
mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU);
mtu = mtu - t_hlen;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 24d097290dbc..5cdb084b55fb 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -5922,7 +5922,7 @@ static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
return 0;
for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
- hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
+ hlist_for_each_entry_rcu(rt6_ex, &bucket->chain, hlist) {
if (w->skip) {
w->skip--;
continue;
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
index 2aba3d7e9cec..129cb659bab2 100644
--- a/net/ipv6/seg6_local.c
+++ b/net/ipv6/seg6_local.c
@@ -261,6 +261,15 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
IPCB(skb)->iif = iif;
+ } else if (proto == IPPROTO_IPV6) {
+ bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
+ int iif = IP6CB(skb)->iif;
+
+ memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+ IP6CB(skb)->iif = iif;
+ IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+ if (l3slave)
+ IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
}
return true;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index ac38d4aa09dd..bc1f0d3de34e 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1133,7 +1133,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
WRITE_ONCE(dev->mtu, mtu);
hlen = tdev->hard_header_len + tdev->needed_headroom;
}
- dev->needed_headroom = t_hlen + hlen;
+ dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
}
static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 689c0b383ebf..f13fdd1214aa 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1180,11 +1180,48 @@ static void tcp_v6_restore_cb(struct sk_buff *skb)
sizeof(struct inet6_skb_parm));
}
+/* Called from tcp_v4_syn_recv_sock() for v6_mapped children. */
+static void tcp_v6_mapped_child_init(struct sock *newsk, const struct sock *sk)
+{
+ struct inet_sock *newinet = inet_sk(newsk);
+ struct ipv6_pinfo *newnp;
+
+ newinet->pinet6 = newnp = tcp_inet6_sk(newsk);
+
+ memcpy(newnp, tcp_inet6_sk(sk), sizeof(struct ipv6_pinfo));
+
+ newnp->saddr = newsk->sk_v6_rcv_saddr;
+
+ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
+ if (sk_is_mptcp(newsk))
+ mptcpv6_handle_mapped(newsk, true);
+ newsk->sk_backlog_rcv = tcp_v4_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+ tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
+ newnp->ipv6_mc_list = NULL;
+ newnp->ipv6_ac_list = NULL;
+ newnp->ipv6_fl_list = NULL;
+ newnp->pktoptions = NULL;
+ newnp->opt = NULL;
+
+ /* tcp_v4_syn_recv_sock() has initialized newinet->mc_{index,ttl} */
+ newnp->mcast_oif = newinet->mc_index;
+ newnp->mcast_hops = newinet->mc_ttl;
+
+ newnp->rcv_flowinfo = 0;
+ if (tcp_inet6_sk(sk)->repflow)
+ newnp->flow_label = 0;
+}
+
static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct inet_request_sock *ireq;
struct ipv6_pinfo *newnp;
@@ -1200,60 +1237,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
#endif
struct flowi6 fl6;
- if (skb->protocol == htons(ETH_P_IP)) {
- /*
- * v6 mapped
- */
-
- newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
-
- if (!newsk)
- return NULL;
-
- inet_sk(newsk)->pinet6 = tcp_inet6_sk(newsk);
-
- newnp = tcp_inet6_sk(newsk);
- newtp = tcp_sk(newsk);
-
- memcpy(newnp, np, sizeof(struct ipv6_pinfo));
-
- newnp->saddr = newsk->sk_v6_rcv_saddr;
-
- inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
- if (sk_is_mptcp(newsk))
- mptcpv6_handle_mapped(newsk, true);
- newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#ifdef CONFIG_TCP_MD5SIG
- newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
-#endif
-
- newnp->ipv6_mc_list = NULL;
- newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_list = NULL;
- newnp->pktoptions = NULL;
- newnp->opt = NULL;
- newnp->mcast_oif = inet_iif(skb);
- newnp->mcast_hops = ip_hdr(skb)->ttl;
- newnp->rcv_flowinfo = 0;
- if (np->repflow)
- newnp->flow_label = 0;
-
- /*
- * No need to charge this sock to the relevant IPv6 refcnt debug socks count
- * here, tcp_create_openreq_child now does this for us, see the comment in
- * that function for the gory details. -acme
- */
-
- /* It is tricky place. Until this moment IPv4 tcp
- worked with IPv6 icsk.icsk_af_ops.
- Sync it now.
- */
- tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
-
- return newsk;
- }
-
+ if (skb->protocol == htons(ETH_P_IP))
+ return tcp_v4_syn_recv_sock(sk, skb, req, dst,
+ req_unhash, own_req,
+ tcp_v6_mapped_child_init);
ireq = inet_rsk(req);
if (sk_acceptq_is_full(sk))
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 8432b50d9ce4..00d2a1b8b679 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -193,7 +193,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
goto drop;
}
- if (1 + sp->len == XFRM_MAX_DEPTH) {
+ if (sp->len >= XFRM_MAX_DEPTH) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 965167bf3abd..b9ce76ccd054 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2080,6 +2080,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
sk = NULL;
read_lock(&iucv_sk_list.lock);
sk_for_each(sk, &iucv_sk_list.head) {
+ if (iucv_sk(sk)->hs_dev != dev)
+ continue;
if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
if ((!memcmp(&iucv_sk(sk)->src_name,
trans_hdr->destAppName, 8)) &&
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index a47afaaea2a1..93b1c499d76d 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -5,6 +5,7 @@
* Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
*/
+#include <linux/rcupdate.h>
#include <linux/bpf.h>
#include <linux/errno.h>
#include <linux/errqueue.h>
@@ -389,7 +390,9 @@ static int kcm_parse_func_strparser(struct strparser *strp, struct sk_buff *skb)
struct bpf_prog *prog = psock->bpf_prog;
int res;
+ rcu_read_lock();
res = bpf_prog_run_pin_on_cpu(prog, skb);
+ rcu_read_unlock();
return res;
}
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index f3980ee81739..7e9ca63f5aab 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -255,7 +255,8 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
link = links[link_id];
ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
ieee80211_link_setup(&link->data);
- ieee80211_set_wmm_default(&link->data, true, non_sta);
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ ieee80211_set_wmm_default(&link->data, true, non_sta);
}
if (new_links == 0)
diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c
index 9914390ff31f..337d8d4ea5f5 100644
--- a/net/mac80211/s1g.c
+++ b/net/mac80211/s1g.c
@@ -146,7 +146,7 @@ ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
- ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
+ ieee80211_s1g_send_twt_teardown(sdata, mgmt->da, sdata->vif.addr,
flowid);
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e3841099bbda..5ea7572aae3d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -5035,7 +5035,8 @@ static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type == NL80211_IFTYPE_AP && resp) {
u16 *resp_offsets = resp->cntdwn_counter_offsets;
- resp->data[resp_offsets[i]] = count;
+ if (resp_offsets[i])
+ resp->data[resp_offsets[i]] = count;
}
}
}
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 73e44f9e58c3..9685405d744b 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -198,6 +198,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
const struct iphdr *v4hdr;
+ hdr = mpls_hdr(skb) + label_index;
v4hdr = (const struct iphdr *)(hdr + 1);
if (v4hdr->version == 4) {
hash = jhash_3words(ntohl(v4hdr->saddr),
@@ -208,6 +209,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
sizeof(struct ipv6hdr))) {
const struct ipv6hdr *v6hdr;
+ hdr = mpls_hdr(skb) + label_index;
v6hdr = (const struct ipv6hdr *)(hdr + 1);
hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index d3b5c2d48b53..db2e3a1ebb6e 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -788,7 +788,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk);
struct mptcp_subflow_request_sock *subflow_req;
@@ -834,7 +836,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
create_child:
child = listener->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
+ req_unhash, own_req, opt_child_init);
if (child && *own_req) {
struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(child);
diff --git a/net/mptcp/syncookies.c b/net/mptcp/syncookies.c
index 7f22526346a7..b5cac5701122 100644
--- a/net/mptcp/syncookies.c
+++ b/net/mptcp/syncookies.c
@@ -118,6 +118,7 @@ bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subfl
subflow_req->local_nonce = e->local_nonce;
subflow_req->backup = e->backup;
subflow_req->remote_id = e->join_id;
+ subflow_req->local_id = e->local_id;
subflow_req->token = e->token;
subflow_req->msk = msk;
spin_unlock_bh(&join_entry_locks[i]);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 441d1f134110..7226eccfd7b7 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1180,7 +1180,7 @@ config NETFILTER_XT_MATCH_CGROUP
tristate '"control group" match support'
depends on NETFILTER_ADVANCED
depends on CGROUPS
- select CGROUP_NET_CLASSID
+ select SOCK_CGROUP_DATA
help
Socket/process control group matching allows you to match locally
generated packets based on which net_cls control group processes
diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c
index 61d3797fb799..b3f0d2d0e1dd 100644
--- a/net/netfilter/ipvs/ip_vs_ftp.c
+++ b/net/netfilter/ipvs/ip_vs_ftp.c
@@ -103,7 +103,7 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
char *s, c;
unsigned char p[6];
char edelim;
- __u16 hport;
+ __u32 hport;
int i = 0;
if (data_limit - data < plen) {
@@ -145,7 +145,11 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
return -1;
c = *data;
if (isdigit(c)) {
- p[i] = p[i]*10 + c - '0';
+ unsigned int val = p[i] * 10 + c - '0';
+
+ if (val > 255)
+ return -1;
+ p[i] = val;
} else if (c == ',' && i < 5) {
i++;
p[i] = 0;
@@ -223,6 +227,8 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
if (!isdigit(*s))
break;
hport = hport * 10 + *s - '0';
+ if (hport > 65535)
+ return -1;
}
if (s == data_limit || !hport || *s != edelim)
return -1;
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index a1c41defaf22..6b00c81084fe 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -267,12 +267,17 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
SIP_HDR_CONTACT, &in_header,
&matchoff, &matchlen,
&addr, &port) > 0) {
+ int old_len = skb->len, delta;
+
if (!map_addr(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen,
&addr, port)) {
nf_ct_helper_log(skb, ct, "cannot mangle contact");
return NF_DROP;
}
+
+ delta = (int)skb->len - old_len;
+ coff += delta;
}
if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 2c894a7d8a40..a9053667e370 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -10214,10 +10214,6 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
return -EAGAIN;
}
- err = nft_flow_rule_offload_commit(net);
- if (err < 0)
- return err;
-
/* 1. Allocate space for next generation rules_gen_X[] */
list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
int ret;
@@ -10241,6 +10237,16 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
}
}
+ /* must be last, so audit and chain blob set up does not leave hardware
+ * in consistent state.
+ */
+ err = nft_flow_rule_offload_commit(net);
+ if (err < 0) {
+ nf_tables_commit_chain_prepare_cancel(net);
+ nf_tables_commit_audit_free(&adl);
+ return err;
+ }
+
/* step 2. Make rules_gen_X visible to packet path */
list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(chain, &table->chains, list)
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index 813c75cd2c25..3fa6369790f4 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -32,6 +32,9 @@ struct nft_counter_percpu_priv {
static DEFINE_PER_CPU(struct u64_stats_sync, nft_counter_sync);
+/* control plane only: sync fetch+reset */
+static DEFINE_SPINLOCK(nft_counter_lock);
+
static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -148,13 +151,25 @@ static void nft_counter_fetch(struct nft_counter_percpu_priv *priv,
}
}
+static void nft_counter_fetch_and_reset(struct nft_counter_percpu_priv *priv,
+ struct nft_counter_tot *total)
+{
+ spin_lock(&nft_counter_lock);
+ nft_counter_fetch(priv, total);
+ nft_counter_reset(priv, total);
+ spin_unlock(&nft_counter_lock);
+}
+
static int nft_counter_do_dump(struct sk_buff *skb,
struct nft_counter_percpu_priv *priv,
bool reset)
{
struct nft_counter_tot total;
- nft_counter_fetch(priv, &total);
+ if (unlikely(reset))
+ nft_counter_fetch_and_reset(priv, &total);
+ else
+ nft_counter_fetch(priv, &total);
if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes),
NFTA_COUNTER_PAD) ||
@@ -162,9 +177,6 @@ static int nft_counter_do_dump(struct sk_buff *skb,
NFTA_COUNTER_PAD))
goto nla_put_failure;
- if (reset)
- nft_counter_reset(priv, &total);
-
return 0;
nla_put_failure:
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
index bfc98719684e..d9943c4f5ee4 100644
--- a/net/netfilter/xt_cgroup.c
+++ b/net/netfilter/xt_cgroup.c
@@ -23,6 +23,8 @@ MODULE_DESCRIPTION("Xtables: process control group matching");
MODULE_ALIAS("ipt_cgroup");
MODULE_ALIAS("ip6t_cgroup");
+#define NET_CLS_CLASSID_INVALID_MSG "classid invalid without net_cls cgroups\n"
+
static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
{
struct xt_cgroup_info_v0 *info = par->matchinfo;
@@ -30,6 +32,11 @@ static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
if (info->invert & ~1)
return -EINVAL;
+ if (!IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
+ pr_info_ratelimited(NET_CLS_CLASSID_INVALID_MSG);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -42,7 +49,7 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
return -EINVAL;
if (!info->has_path && !info->has_classid) {
- pr_info("xt_cgroup: no path or classid specified\n");
+ pr_info_ratelimited("no path or classid specified\n");
return -EINVAL;
}
@@ -51,6 +58,11 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
return -EINVAL;
}
+ if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
+ pr_info_ratelimited(NET_CLS_CLASSID_INVALID_MSG);
+ return -EINVAL;
+ }
+
info->priv = NULL;
if (info->has_path) {
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
@@ -77,7 +89,7 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
return -EINVAL;
if (!info->has_path && !info->has_classid) {
- pr_info("xt_cgroup: no path or classid specified\n");
+ pr_info_ratelimited("no path or classid specified\n");
return -EINVAL;
}
@@ -86,6 +98,11 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
return -EINVAL;
}
+ if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
+ pr_info_ratelimited(NET_CLS_CLASSID_INVALID_MSG);
+ return -EINVAL;
+ }
+
info->priv = NULL;
if (info->has_path) {
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c
index c1a70f8f0441..59e93d97b507 100644
--- a/net/netfilter/xt_hl.c
+++ b/net/netfilter/xt_hl.c
@@ -6,6 +6,7 @@
* Hop Limit matching module
* (C) 2001-2002 Maciej Soltysiak <solt@dns.toxicfilms.tv>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/ip.h>
#include <linux/ipv6.h>
@@ -22,6 +23,18 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_ttl");
MODULE_ALIAS("ip6t_hl");
+static int ttl_mt_check(const struct xt_mtchk_param *par)
+{
+ const struct ipt_ttl_info *info = par->matchinfo;
+
+ if (info->mode > IPT_TTL_GT) {
+ pr_info_ratelimited("Unknown TTL match mode: %d\n", info->mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct ipt_ttl_info *info = par->matchinfo;
@@ -41,6 +54,18 @@ static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par)
return false;
}
+static int hl_mt6_check(const struct xt_mtchk_param *par)
+{
+ const struct ip6t_hl_info *info = par->matchinfo;
+
+ if (info->mode > IP6T_HL_GT) {
+ pr_info_ratelimited("Unknown Hop Limit match mode: %d\n", info->mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static bool hl_mt6(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct ip6t_hl_info *info = par->matchinfo;
@@ -65,6 +90,7 @@ static struct xt_match hl_mt_reg[] __read_mostly = {
.name = "ttl",
.revision = 0,
.family = NFPROTO_IPV4,
+ .checkentry = ttl_mt_check,
.match = ttl_mt,
.matchsize = sizeof(struct ipt_ttl_info),
.me = THIS_MODULE,
@@ -73,6 +99,7 @@ static struct xt_match hl_mt_reg[] __read_mostly = {
.name = "hl",
.revision = 0,
.family = NFPROTO_IPV6,
+ .checkentry = hl_mt6_check,
.match = hl_mt6,
.matchsize = sizeof(struct ip6t_hl_info),
.me = THIS_MODULE,
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index d63d2e5dc60c..a6cf04cedd50 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -127,7 +127,7 @@ static void digital_wq_cmd_complete(struct work_struct *work)
mutex_unlock(&ddev->cmd_lock);
- if (!IS_ERR(cmd->resp))
+ if (!IS_ERR_OR_NULL(cmd->resp))
print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1,
cmd->resp->data, cmd->resp->len, false);
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index db050f4faa5b..7b86faf3f4ce 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1286,10 +1286,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
{
struct nfc_llcp_sock *llcp_sock;
u8 dsap, ssap, type, length, tid, sap;
- const u8 *tlv;
- u16 tlv_len, offset;
+ const u8 *tlv, *tlv_end;
const char *service_name;
- size_t service_name_len;
+ int service_name_len;
struct nfc_llcp_sdp_tlv *sdp;
HLIST_HEAD(llc_sdres_list);
size_t sdres_tlvs_len;
@@ -1305,22 +1304,34 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
return;
}
+ /*
+ * Walk the SNL TLV list in the linear part of the skb only,
+ * bounded by skb_tail_pointer(). Each TLV needs a two-byte
+ * header (type, length) and its declared length must fit before
+ * the end; this also keeps the walk safe for very short frames.
+ */
tlv = &skb->data[LLCP_HEADER_SIZE];
- tlv_len = skb->len - LLCP_HEADER_SIZE;
- offset = 0;
+ tlv_end = skb_tail_pointer(skb);
sdres_tlvs_len = 0;
- while (offset < tlv_len) {
+ while (tlv + 2 < tlv_end) {
type = tlv[0];
length = tlv[1];
+ if (tlv + 2 + length > tlv_end)
+ break;
+
switch (type) {
case LLCP_TLV_SDREQ:
+ if (length < 1)
+ break;
+
tid = tlv[2];
service_name = (char *) &tlv[3];
service_name_len = length - 1;
- pr_debug("Looking for %.16s\n", service_name);
+ pr_debug("Looking for %.*s\n", service_name_len,
+ service_name);
if (service_name_len == strlen("urn:nfc:sn:sdp") &&
!strncmp(service_name, "urn:nfc:sn:sdp",
@@ -1380,6 +1391,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
break;
case LLCP_TLV_SDRES:
+ if (length != 2)
+ break;
+
mutex_lock(&local->sdreq_lock);
pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
@@ -1408,7 +1422,6 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
break;
}
- offset += length + 2;
tlv += length + 2;
}
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index cd1fdf0beefb..5e895a45a1d3 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -319,14 +319,22 @@ static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,
if (get_user(len, optlen))
return -EFAULT;
- local = llcp_sock->local;
- if (!local)
- return -ENODEV;
+ if (len < 0)
+ return -EINVAL;
+
+ if (len < sizeof(u32))
+ return -EINVAL;
len = min_t(u32, len, sizeof(u32));
lock_sock(sk);
+ local = llcp_sock->local;
+ if (!local) {
+ release_sock(sk);
+ return -ENODEV;
+ }
+
switch (optname) {
case NFC_LLCP_RW:
rw = llcp_sock->rw > LLCP_MAX_RW ? local->rw : llcp_sock->rw;
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 7a3fb2a397a1..4db36cddf670 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -46,11 +46,11 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
del_timer_sync(&ndev->data_timer);
clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
- /* Mark the exchange as done before calling the callback.
- * The callback (e.g. rawsock_data_exchange_complete) may
- * want to immediately queue another data exchange.
- */
- clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
+ /* Claim completion atomically -- both close and rx_work may race here */
+ if (!test_and_clear_bit(NCI_DATA_EXCHANGE, &ndev->flags)) {
+ kfree_skb(skb);
+ return;
+ }
if (cb) {
/* forward skb to nfc core */
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index fccd7ce69bb7..e5d6ce71ca33 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -50,11 +50,27 @@ static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,
const struct nci_core_init_rsp_1 *rsp_1 = (void *)skb->data;
const struct nci_core_init_rsp_2 *rsp_2;
+ /* Ensure that the status field can be accessed. */
+ if (skb_headlen(skb) < 1)
+ return NCI_STATUS_SYNTAX_ERROR;
+
pr_debug("status 0x%x\n", rsp_1->status);
if (rsp_1->status != NCI_STATUS_OK)
return rsp_1->status;
+ /* Success response must contain the full fixed-size header */
+ if (skb_headlen(skb) < sizeof(*rsp_1))
+ return NCI_STATUS_SYNTAX_ERROR;
+
+ /* Ensure the variable-length rf_interfaces array and trailing
+ * rsp_2 structure are fully contained within the skb.
+ */
+ if (skb_headlen(skb) < sizeof(*rsp_1) +
+ rsp_1->num_supported_rf_interfaces +
+ sizeof(*rsp_2))
+ return NCI_STATUS_SYNTAX_ERROR;
+
ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features);
ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces;
@@ -87,15 +103,25 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
const struct sk_buff *skb)
{
const struct nci_core_init_rsp_nci_ver2 *rsp = (void *)skb->data;
- const u8 *supported_rf_interface = rsp->supported_rf_interfaces;
+ const u8 *supported_rf_interface;
u8 rf_interface_idx = 0;
u8 rf_extension_cnt = 0;
+ /* Ensure that the status field can be accessed. */
+ if (skb_headlen(skb) < 1)
+ return NCI_STATUS_SYNTAX_ERROR;
+
pr_debug("status %x\n", rsp->status);
if (rsp->status != NCI_STATUS_OK)
return rsp->status;
+ /* Success response must contain the full fixed-size header */
+ if (skb_headlen(skb) < sizeof(*rsp))
+ return NCI_STATUS_SYNTAX_ERROR;
+
+ supported_rf_interface = rsp->supported_rf_interfaces;
+
ndev->nfcc_features = __le32_to_cpu(rsp->nfcc_features);
ndev->num_supported_rf_interfaces = rsp->num_supported_rf_interfaces;
@@ -104,13 +130,22 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
NCI_MAX_SUPPORTED_RF_INTERFACES);
while (rf_interface_idx < ndev->num_supported_rf_interfaces) {
- ndev->supported_rf_interfaces[rf_interface_idx++] = *supported_rf_interface++;
+ /* Each entry: [rf_interface_type (1B)] [ext_count (1B)] [ext...] */
+ if (supported_rf_interface + 2 > skb_tail_pointer(skb))
+ break;
+ ndev->supported_rf_interfaces[rf_interface_idx] = *supported_rf_interface++;
- /* skip rf extension parameters */
rf_extension_cnt = *supported_rf_interface++;
+ if (supported_rf_interface + rf_extension_cnt > skb_tail_pointer(skb))
+ break;
+
+ /* Only count the entry after full validation */
+ rf_interface_idx++;
supported_rf_interface += rf_extension_cnt;
}
+ ndev->num_supported_rf_interfaces = rf_interface_idx;
+
ndev->max_logical_connections = rsp->max_logical_connections;
ndev->max_routing_table_size =
__le16_to_cpu(rsp->max_routing_table_size);
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 82ffb1ac86b0..6ceec146e694 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -908,10 +908,14 @@ static int ovs_ct_check_limit(struct net *net,
const struct ovs_conntrack_info *info)
{
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
- const struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;
+ const struct ovs_ct_limit_info *ct_limit_info;
u32 per_zone_limit, connections;
u32 conncount_key;
+ ct_limit_info = rcu_dereference(ovs_net->ct_limit_info);
+ if (!ct_limit_info)
+ return 0;
+
conncount_key = info->zone.id;
per_zone_limit = ct_limit_get(ct_limit_info, info->zone.id);
@@ -1557,43 +1561,56 @@ static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info)
#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
static int ovs_ct_limit_init(struct net *net, struct ovs_net *ovs_net)
{
+ struct ovs_ct_limit_info *info;
int i, err;
- ovs_net->ct_limit_info = kmalloc(sizeof(*ovs_net->ct_limit_info),
- GFP_KERNEL);
- if (!ovs_net->ct_limit_info)
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
return -ENOMEM;
- ovs_net->ct_limit_info->default_limit = OVS_CT_LIMIT_DEFAULT;
- ovs_net->ct_limit_info->limits =
+ info->default_limit = OVS_CT_LIMIT_DEFAULT;
+ info->limits =
kmalloc_array(CT_LIMIT_HASH_BUCKETS, sizeof(struct hlist_head),
GFP_KERNEL);
- if (!ovs_net->ct_limit_info->limits) {
- kfree(ovs_net->ct_limit_info);
+ if (!info->limits) {
+ kfree(info);
return -ENOMEM;
}
for (i = 0; i < CT_LIMIT_HASH_BUCKETS; i++)
- INIT_HLIST_HEAD(&ovs_net->ct_limit_info->limits[i]);
+ INIT_HLIST_HEAD(&info->limits[i]);
- ovs_net->ct_limit_info->data =
- nf_conncount_init(net, NFPROTO_INET, sizeof(u32));
+ info->data = nf_conncount_init(net, NFPROTO_INET, sizeof(u32));
- if (IS_ERR(ovs_net->ct_limit_info->data)) {
- err = PTR_ERR(ovs_net->ct_limit_info->data);
- kfree(ovs_net->ct_limit_info->limits);
- kfree(ovs_net->ct_limit_info);
+ if (IS_ERR(info->data)) {
+ err = PTR_ERR(info->data);
+ kfree(info->limits);
+ kfree(info);
pr_err("openvswitch: failed to init nf_conncount %d\n", err);
return err;
}
+ rcu_assign_pointer(ovs_net->ct_limit_info, info);
return 0;
}
-static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net)
+static void *ovs_ct_limit_exit_start(struct ovs_net *ovs_net)
{
- const struct ovs_ct_limit_info *info = ovs_net->ct_limit_info;
+ return rcu_replace_pointer(ovs_net->ct_limit_info, NULL,
+ lockdep_ovsl_is_held());
+}
+
+/* The CT limit state must be detached by ovs_ct_limit_exit_start() and an
+ * RCU grace period must elapse before this function runs. The pernet core
+ * guarantees the grace period between the .pre_exit and .exit callbacks.
+ */
+static void ovs_ct_limit_exit_finish(struct net *net, void *data)
+{
+ const struct ovs_ct_limit_info *info = data;
int i;
+ if (!info)
+ return;
+
nf_conncount_destroy(net, NFPROTO_INET, info->data);
for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) {
struct hlist_head *head = &info->limits[i];
@@ -1601,7 +1618,7 @@ static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net)
struct hlist_node *next;
hlist_for_each_entry_safe(ct_limit, next, head, hlist_node)
- kfree_rcu(ct_limit, rcu);
+ kfree(ct_limit);
}
kfree(info->limits);
kfree(info);
@@ -1640,12 +1657,13 @@ static bool check_zone_id(int zone_id, u16 *pzone)
return false;
}
-static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit,
- struct ovs_ct_limit_info *info)
+static int ovs_ct_limit_set_zone_limit(struct ovs_net *ovs_net,
+ struct nlattr *nla_zone_limit)
{
struct ovs_zone_limit *zone_limit;
- int rem;
+ struct ovs_ct_limit_info *info;
u16 zone;
+ int rem;
rem = NLA_ALIGN(nla_len(nla_zone_limit));
zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit);
@@ -1654,6 +1672,7 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit,
if (unlikely(zone_limit->zone_id ==
OVS_ZONE_LIMIT_DEFAULT_ZONE)) {
ovs_lock();
+ info = ovsl_dereference(ovs_net->ct_limit_info);
info->default_limit = zone_limit->limit;
ovs_unlock();
} else if (unlikely(!check_zone_id(
@@ -1671,6 +1690,7 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit,
ct_limit->limit = zone_limit->limit;
ovs_lock();
+ info = ovsl_dereference(ovs_net->ct_limit_info);
ct_limit_set(info, ct_limit);
ovs_unlock();
}
@@ -1685,12 +1705,13 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit,
return 0;
}
-static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit,
- struct ovs_ct_limit_info *info)
+static int ovs_ct_limit_del_zone_limit(struct ovs_net *ovs_net,
+ struct nlattr *nla_zone_limit)
{
struct ovs_zone_limit *zone_limit;
- int rem;
+ struct ovs_ct_limit_info *info;
u16 zone;
+ int rem;
rem = NLA_ALIGN(nla_len(nla_zone_limit));
zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit);
@@ -1699,6 +1720,7 @@ static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit,
if (unlikely(zone_limit->zone_id ==
OVS_ZONE_LIMIT_DEFAULT_ZONE)) {
ovs_lock();
+ info = ovsl_dereference(ovs_net->ct_limit_info);
info->default_limit = OVS_CT_LIMIT_DEFAULT;
ovs_unlock();
} else if (unlikely(!check_zone_id(
@@ -1706,6 +1728,7 @@ static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit,
OVS_NLERR(true, "zone id is out of range");
} else {
ovs_lock();
+ info = ovsl_dereference(ovs_net->ct_limit_info);
ct_limit_del(info, zone);
ovs_unlock();
}
@@ -1749,6 +1772,7 @@ static int __ovs_ct_limit_get_zone_limit(struct net *net,
return nla_put_nohdr(reply, sizeof(zone_limit), &zone_limit);
}
+/* Called with RCU read lock held. */
static int ovs_ct_limit_get_zone_limit(struct net *net,
struct nlattr *nla_zone_limit,
struct ovs_ct_limit_info *info,
@@ -1772,12 +1796,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net,
&zone))) {
OVS_NLERR(true, "zone id is out of range");
} else {
- rcu_read_lock();
limit = ct_limit_get(info, zone);
err = __ovs_ct_limit_get_zone_limit(
net, info->data, zone, limit, reply);
- rcu_read_unlock();
if (err)
return err;
}
@@ -1792,6 +1814,7 @@ static int ovs_ct_limit_get_zone_limit(struct net *net,
return 0;
}
+/* Called with RCU read lock held. */
static int ovs_ct_limit_get_all_zone_limit(struct net *net,
struct ovs_ct_limit_info *info,
struct sk_buff *reply)
@@ -1804,19 +1827,16 @@ static int ovs_ct_limit_get_all_zone_limit(struct net *net,
if (err)
return err;
- rcu_read_lock();
for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) {
head = &info->limits[i];
hlist_for_each_entry_rcu(ct_limit, head, hlist_node) {
err = __ovs_ct_limit_get_zone_limit(net, info->data,
ct_limit->zone, ct_limit->limit, reply);
if (err)
- goto exit_err;
+ return err;
}
}
-exit_err:
- rcu_read_unlock();
return err;
}
@@ -1826,7 +1846,6 @@ static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info)
struct sk_buff *reply;
struct ovs_header *ovs_reply_header;
struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id);
- struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;
int err;
reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_SET,
@@ -1839,8 +1858,8 @@ static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info)
goto exit_err;
}
- err = ovs_ct_limit_set_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT],
- ct_limit_info);
+ err = ovs_ct_limit_set_zone_limit(ovs_net,
+ a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]);
if (err)
goto exit_err;
@@ -1860,7 +1879,6 @@ static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info)
struct sk_buff *reply;
struct ovs_header *ovs_reply_header;
struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id);
- struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;
int err;
reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_DEL,
@@ -1873,8 +1891,8 @@ static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info)
goto exit_err;
}
- err = ovs_ct_limit_del_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT],
- ct_limit_info);
+ err = ovs_ct_limit_del_zone_limit(ovs_net,
+ a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]);
if (err)
goto exit_err;
@@ -1894,7 +1912,7 @@ static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info)
struct ovs_header *ovs_reply_header;
struct net *net = sock_net(skb->sk);
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
- struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;
+ struct ovs_ct_limit_info *ct_limit_info;
int err;
reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_GET,
@@ -1908,18 +1926,19 @@ static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info)
goto exit_err;
}
+ rcu_read_lock();
+ ct_limit_info = rcu_dereference(ovs_net->ct_limit_info);
if (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) {
err = ovs_ct_limit_get_zone_limit(
net, a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], ct_limit_info,
reply);
- if (err)
- goto exit_err;
} else {
err = ovs_ct_limit_get_all_zone_limit(net, ct_limit_info,
reply);
- if (err)
- goto exit_err;
}
+ rcu_read_unlock();
+ if (err)
+ goto exit_err;
nla_nest_end(reply, nla_reply);
genlmsg_end(reply, ovs_reply_header);
@@ -1977,6 +1996,7 @@ int ovs_ct_init(struct net *net)
{
unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE;
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+ int err = 0;
if (nf_connlabels_get(net, n_bits - 1)) {
ovs_net->xt_label = false;
@@ -1986,18 +2006,36 @@ int ovs_ct_init(struct net *net)
}
#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
- return ovs_ct_limit_init(net, ovs_net);
-#else
- return 0;
+ err = ovs_ct_limit_init(net, ovs_net);
+ if (err && ovs_net->xt_label)
+ nf_connlabels_put(net);
#endif
+ return err;
}
-void ovs_ct_exit(struct net *net)
+/* Must be called with ovs_mutex held. Detaches the RCU-protected
+ * ct_limit_info and stores it in ovs_net->ct_limit_exit_data for
+ * ovs_ct_exit_finish() to complete the teardown after an RCU grace period.
+ */
+void ovs_ct_exit_start(struct net *net __maybe_unused)
+{
+#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
+ struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+
+ ovs_net->ct_limit_exit_data = ovs_ct_limit_exit_start(ovs_net);
+#endif
+}
+
+/* Completes the CT limit teardown. The pernet core guarantees an RCU
+ * grace period between detaching the state in ovs_ct_exit_start() and
+ * this call, so no RCU readers remain.
+ */
+void ovs_ct_exit_finish(struct net *net)
{
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
- ovs_ct_limit_exit(net, ovs_net);
+ ovs_ct_limit_exit_finish(net, ovs_net->ct_limit_exit_data);
#endif
if (ovs_net->xt_label)
diff --git a/net/openvswitch/conntrack.h b/net/openvswitch/conntrack.h
index 317e525c8a11..ab21d032fde3 100644
--- a/net/openvswitch/conntrack.h
+++ b/net/openvswitch/conntrack.h
@@ -14,7 +14,8 @@ enum ovs_key_attr;
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
int ovs_ct_init(struct net *);
-void ovs_ct_exit(struct net *);
+void ovs_ct_exit_start(struct net *net);
+void ovs_ct_exit_finish(struct net *net);
bool ovs_ct_verify(struct net *, enum ovs_key_attr attr);
int ovs_ct_copy_action(struct net *, const struct nlattr *,
const struct sw_flow_key *, struct sw_flow_actions **,
@@ -40,7 +41,8 @@ void ovs_ct_free_action(const struct nlattr *a);
static inline int ovs_ct_init(struct net *net) { return 0; }
-static inline void ovs_ct_exit(struct net *net) { }
+static inline void ovs_ct_exit_start(struct net *net) { }
+static inline void ovs_ct_exit_finish(struct net *net) { }
static inline bool ovs_ct_verify(struct net *net, int attr)
{
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 5706f2393fb7..a43d3af3218f 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -282,6 +282,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
consume_skb(skb);
break;
default:
+ skb_tx_error(skb);
kfree_skb(skb);
break;
}
@@ -582,8 +583,6 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);
user_skb = NULL;
out:
- if (err)
- skb_tx_error(skb);
consume_skb(user_skb);
consume_skb(nskb);
@@ -1444,33 +1443,34 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
goto unlock;
}
- ovs_flow_tbl_remove(&dp->table, flow);
- ovs_unlock();
-
- reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts,
+ reply = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts),
&flow->id, info, false, ufid_flags);
- if (likely(reply)) {
- if (!IS_ERR(reply)) {
- rcu_read_lock(); /*To keep RCU checker happy. */
- err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
- reply, info->snd_portid,
- info->snd_seq, 0,
- OVS_FLOW_CMD_DEL,
- ufid_flags);
- rcu_read_unlock();
- if (WARN_ON_ONCE(err < 0)) {
- kfree_skb(reply);
- goto out_free;
- }
+ if (IS_ERR(reply)) {
+ netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0,
+ PTR_ERR(reply));
+ reply = NULL;
+ }
- ovs_notify(&dp_flow_genl_family, reply, info);
- } else {
- netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0,
- PTR_ERR(reply));
+ if (likely(reply)) {
+ err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
+ reply, info->snd_portid,
+ info->snd_seq, 0,
+ OVS_FLOW_CMD_DEL, ufid_flags);
+ if (WARN_ON_ONCE(err < 0)) {
+ kfree_skb(reply);
+ reply = NULL;
}
}
+ /* Removal has to happen after ovs_flow_cmd_fill_info(), as it uses
+ * the flow->mask that can be scheduled to be freed by the
+ * ovs_flow_tbl_remove() and we're not holding the RCU read lock.
+ */
+ ovs_flow_tbl_remove(&dp->table, flow);
+ ovs_unlock();
+
+ if (likely(reply))
+ ovs_notify(&dp_flow_genl_family, reply, info);
-out_free:
ovs_flow_free(flow, true);
return 0;
unlock:
@@ -2731,6 +2731,13 @@ static void __net_exit list_vports_from_net(struct net *net, struct net *dnet,
}
}
+static void __net_exit ovs_pre_exit_net(struct net *dnet)
+{
+ ovs_lock();
+ ovs_ct_exit_start(dnet);
+ ovs_unlock();
+}
+
static void __net_exit ovs_exit_net(struct net *dnet)
{
struct datapath *dp, *dp_next;
@@ -2741,7 +2748,7 @@ static void __net_exit ovs_exit_net(struct net *dnet)
ovs_lock();
- ovs_ct_exit(dnet);
+ ovs_ct_exit_finish(dnet);
list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
__dp_destroy(dp);
@@ -2765,6 +2772,7 @@ static void __net_exit ovs_exit_net(struct net *dnet)
static struct pernet_operations ovs_net_ops = {
.init = ovs_init_net,
+ .pre_exit = ovs_pre_exit_net,
.exit = ovs_exit_net,
.id = &ovs_net_id,
.size = sizeof(struct ovs_net),
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index 88156a677f22..c16e249380fe 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -29,8 +29,8 @@
* datapath.
* @n_hit: Number of received packets for which a matching flow was found in
* the flow table.
- * @n_miss: Number of received packets that had no matching flow in the flow
- * table. The sum of @n_hit and @n_miss is the number of packets that have
+ * @n_missed: Number of received packets that had no matching flow in the flow
+ * table. The sum of @n_hit and @n_missed is the number of packets that have
* been received by the datapath.
* @n_lost: Number of received packets that had no matching flow in the flow
* table that could not be sent to userspace (normally due to an overflow in
@@ -40,6 +40,7 @@
* up per packet.
* @n_cache_hit: The number of received packets that had their mask found using
* the mask cache.
+ * @syncp: Synchronization point for 64bit counters.
*/
struct dp_stats_percpu {
u64 n_hit;
@@ -74,8 +75,10 @@ struct dp_nlsk_pids {
* ovs_mutex and RCU.
* @stats_percpu: Per-CPU datapath statistics.
* @net: Reference to net namespace.
- * @max_headroom: the maximum headroom of all vports in this datapath; it will
+ * @user_features: Bitmap of enabled %OVS_DP_F_* features.
+ * @max_headroom: The maximum headroom of all vports in this datapath; it will
* be used by all the internal vports in this dp.
+ * @meter_tbl: Meter table.
* @upcall_portids: RCU protected 'struct dp_nlsk_pids'.
*
* Context: See the comment on locking at the top of datapath.c for additional
@@ -125,10 +128,13 @@ struct ovs_skb_cb {
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
/**
- * struct dp_upcall - metadata to include with a packet to send to userspace
+ * struct dp_upcall_info - metadata to include with a packet sent to userspace
* @cmd: One of %OVS_PACKET_CMD_*.
* @userdata: If nonnull, its variable-length value is passed to userspace as
* %OVS_PACKET_ATTR_USERDATA.
+ * @actions: If nonnull, its variable-length value is passed to userspace as
+ * %OVS_PACKET_ATTR_ACTIONS.
+ * @actions_len: The length of the @actions.
* @portid: Netlink portid to which packet should be sent. If @portid is 0
* then no packet is sent and the packet is accounted in the datapath's @n_lost
* counter.
@@ -149,16 +155,22 @@ struct dp_upcall_info {
* struct ovs_net - Per net-namespace data for ovs.
* @dps: List of datapaths to enable dumping them all out.
* Protected by genl_mutex.
+ * @dp_notify_work: A work notifier to handle port unregistering.
+ * @masks_rebalance: A work to periodically optimize flow table caches.
+ * @ct_limit_info: Hash table of conntrack zone connection limits. Protected
+ * by RCU; updates and teardown are serialized by ovs_mutex. May be NULL during
+ * netns teardown.
+ * @ct_limit_exit_data: CT limit state detached at .pre_exit, freed at .exit.
+ * @xt_label: Whether connlables are configured for the network or not.
*/
struct ovs_net {
struct list_head dps;
struct work_struct dp_notify_work;
struct delayed_work masks_rebalance;
#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
- struct ovs_ct_limit_info *ct_limit_info;
+ struct ovs_ct_limit_info __rcu *ct_limit_info;
+ struct ovs_ct_limit_info *ct_limit_exit_data;
#endif
-
- /* Module reference for configuring conntrack. */
bool xt_label;
};
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 3e71ca8ad8a7..9f67b9dd49f9 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -97,6 +97,8 @@ struct vport {
* @desired_ifindex: New vport's ifindex.
* @dp: New vport's datapath.
* @port_no: New vport's port number.
+ * @upcall_portids: %OVS_VPORT_ATTR_UPCALL_PID attribute from Netlink message,
+ * %NULL if none was supplied.
*/
struct vport_parms {
const char *name;
@@ -125,6 +127,8 @@ struct vport_parms {
* have any configuration.
* @send: Send a packet on the device.
* zero for dropped packets or negative for error.
+ * @owner: Module that implements this vport type.
+ * @list: List entry in the global list of vport types.
*/
struct vport_ops {
enum ovs_vport_type type;
@@ -144,6 +148,7 @@ struct vport_ops {
/**
* struct vport_upcall_stats_percpu - per-cpu packet upcall statistics for
* a given vport.
+ * @syncp: Synchronization point for 64bit counters.
* @n_success: Number of packets that upcall to userspace succeed.
* @n_fail: Number of packets that upcall to userspace failed.
*/
@@ -164,6 +169,8 @@ void ovs_vport_free(struct vport *);
*
* @vport: vport to access
*
+ * Returns: A void pointer to a private data allocated in the @vport.
+ *
* If a nonzero size was passed in priv_size of vport_alloc() a private data
* area was allocated on creation. This allows that area to be accessed and
* used for any purpose needed by the vport implementer.
@@ -178,6 +185,8 @@ static inline void *vport_priv(const struct vport *vport)
*
* @priv: Start of private data area.
*
+ * Returns: A reference to a vport structure that contains @priv.
+ *
* It is sometimes useful to translate from a pointer to the private data
* area to the vport, such as in the case where the private data pointer is
* the result of a hash table lookup. @priv must point to the start of the
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 9f3f70829cb1..4f3c3d312914 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -1116,7 +1116,7 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
len = min_t(unsigned int, sizeof(int), len);
if (put_user(len, optlen))
return -EFAULT;
- if (put_user(val, (int __user *) optval))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
diff --git a/net/rds/cong.c b/net/rds/cong.c
index 8b689ebbd5b5..c85af56e42e7 100644
--- a/net/rds/cong.c
+++ b/net/rds/cong.c
@@ -256,9 +256,9 @@ void rds_cong_map_updated(struct rds_cong_map *map, uint64_t portmask)
map, &map->m_addr);
rds_stats_inc(s_cong_update_received);
atomic_inc(&rds_cong_generation);
- if (waitqueue_active(&map->m_waitq))
+ if (wq_has_sleeper(&map->m_waitq))
wake_up(&map->m_waitq);
- if (waitqueue_active(&rds_poll_waitq))
+ if (wq_has_sleeper(&rds_poll_waitq))
wake_up_all(&rds_poll_waitq);
if (portmask && !list_empty(&rds_cong_monitor)) {
diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
index e34f1be15164..80286135256e 100644
--- a/net/sched/act_skbmod.c
+++ b/net/sched/act_skbmod.c
@@ -38,7 +38,6 @@ TC_INDIRECT_SCOPE int tcf_skbmod_act(struct sk_buff *skb,
if (unlikely(action == TC_ACT_SHOT))
goto drop;
- max_edit_len = skb_mac_header_len(skb);
p = rcu_dereference_bh(d->skbmod_p);
flags = p->flags;
@@ -52,14 +51,19 @@ TC_INDIRECT_SCOPE int tcf_skbmod_act(struct sk_buff *skb,
if (flags == SKBMOD_F_ECN) {
switch (skb_protocol(skb, true)) {
case cpu_to_be16(ETH_P_IP):
+ max_edit_len = sizeof(struct iphdr);
+ break;
case cpu_to_be16(ETH_P_IPV6):
- max_edit_len += skb_network_header_len(skb);
+ max_edit_len = sizeof(struct ipv6hdr);
break;
default:
goto out;
}
- } else if (!skb->dev || skb->dev->type != ARPHRD_ETHER) {
- goto out;
+ max_edit_len += skb_network_offset(skb);
+ } else {
+ if (!skb->dev || skb->dev->type != ARPHRD_ETHER)
+ goto out;
+ max_edit_len = ETH_HLEN;
}
err = skb_ensure_writable(skb, max_edit_len);
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 7f47e937e1b8..ac6d27172c84 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -2220,6 +2220,12 @@ static bool is_ingress_or_clsact(struct tcf_block *block, struct Qdisc *q)
return tcf_block_shared(block) || (q && !!(q->flags & TCQ_F_INGRESS));
}
+enum tcf_tp_insert_state {
+ TP_NOT_CREATED = 0, /* did not create and insert a new tp */
+ TP_CREATED, /* created and inserted a new tp */
+ TP_NOT_OWNED, /* created a proto but failed to insert */
+};
+
static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
struct netlink_ext_ack *extack)
{
@@ -2240,12 +2246,12 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
unsigned long cl;
void *fh;
int err;
- int tp_created;
+ enum tcf_tp_insert_state tp_state;
bool rtnl_held = false;
u32 flags;
replay:
- tp_created = 0;
+ tp_state = TP_NOT_CREATED;
err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
rtm_tca_policy, extack);
@@ -2368,13 +2374,15 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
goto errout_tp;
}
- tp_created = 1;
+ tp_state = TP_CREATED;
tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio,
rtnl_held);
if (IS_ERR(tp)) {
err = PTR_ERR(tp);
goto errout_tp;
}
+ if (tp != tp_new)
+ tp_state = TP_NOT_OWNED;
} else {
mutex_unlock(&chain->filter_chain_lock);
}
@@ -2428,13 +2436,13 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
}
errout:
- if (err && tp_created)
+ if (err && tp_state == TP_CREATED)
tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
errout_tp:
if (chain) {
if (tp && !IS_ERR(tp))
tcf_proto_put(tp, rtnl_held, NULL);
- if (!tp_created)
+ if (tp_state == TP_NOT_CREATED)
tcf_chain_put(chain);
}
tcf_block_release(q, block, rtnl_held);
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 22210e628ab3..6aa39c3c6ff4 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -608,8 +608,11 @@ void __qdisc_calculate_pkt_len(struct sk_buff *skb,
pkt_len <<= stab->szopts.size_log;
out:
- if (unlikely(pkt_len < 1))
- pkt_len = 1;
+ /* A size table can inflate qdisc_pkt_len() beyond any real packet
+ * (via overhead, the data table, or size_log); cap it so deficit
+ * schedulers such as DRR/ETS terminate their refill loops.
+ */
+ pkt_len = clamp_t(int, pkt_len, 1, QDISC_PKT_LEN_MAX);
qdisc_skb_cb(skb)->pkt_len = pkt_len;
}
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index 0dfdc8b65699..c05be1eeea56 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -228,7 +228,7 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt,
codel_params_init(&q->params);
codel_vars_init(&q->vars);
codel_stats_init(&q->stats);
- q->params.mtu = psched_mtu(qdisc_dev(sch));
+ q->params.mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, 1 << 20);
if (opt) {
int err = codel_change(sch, opt, extack);
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index e059d6c14574..ac2f643dd842 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -495,6 +495,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
+ u32 mtu;
int i;
int err;
@@ -502,13 +503,14 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
q->flows_cnt = 1024;
q->memory_limit = 32 << 20; /* 32 MBytes */
q->drop_batch_size = 64;
- q->quantum = psched_mtu(qdisc_dev(sch));
+ mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, FQ_CODEL_QUANTUM_MAX);
+ q->quantum = mtu;
INIT_LIST_HEAD(&q->new_flows);
INIT_LIST_HEAD(&q->old_flows);
codel_params_init(&q->cparams);
codel_stats_init(&q->cstats);
q->cparams.ecn = true;
- q->cparams.mtu = psched_mtu(qdisc_dev(sch));
+ q->cparams.mtu = mtu;
if (opt) {
err = fq_codel_change(sch, opt, extack);
diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c
index 4bd5ca9acc53..fc57b1daa235 100644
--- a/net/sched/sch_fq_pie.c
+++ b/net/sched/sch_fq_pie.c
@@ -416,7 +416,8 @@ static int fq_pie_init(struct Qdisc *sch, struct nlattr *opt,
pie_params_init(&q->p_params);
sch->limit = 10 * 1024;
q->p_params.limit = sch->limit;
- q->quantum = psched_mtu(qdisc_dev(sch));
+ q->quantum = clamp_t(u32, psched_mtu(qdisc_dev(sch)),
+ 256, 1 << 20);
q->sch = sch;
q->ecn_prob = 10;
q->flows_cnt = 1024;
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 65561ecae516..98c339b03bf5 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1237,7 +1237,7 @@ static void transition_one_qdisc(struct net_device *dev,
rcu_assign_pointer(dev_queue->qdisc, new_qdisc);
if (need_watchdog_p) {
- WRITE_ONCE(dev_queue->trans_start, 0);
+ WRITE_ONCE(dev_queue->trans_start, jiffies);
*need_watchdog_p = 1;
}
}
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index aca4795c4101..f9eab403ac16 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -617,6 +617,10 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
q->hhf_evict_timeout = HZ; /* 1 sec */
q->hhf_non_hh_weight = 2;
+ if ((int)q->quantum <= 0 ||
+ (u64)q->quantum * q->hhf_non_hh_weight > INT_MAX)
+ q->quantum = 256;
+
if (opt) {
int err = hhf_change(sch, opt, extack);
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index f1b903b36717..ed1bf8406ae6 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -223,6 +223,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
struct htb_class *cl;
struct tcf_result res;
struct tcf_proto *tcf;
+ unsigned int hops = 0;
int result;
/* allow to select class by setting skb->priority to valid classid;
@@ -265,6 +266,10 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
if (!cl->level)
return cl; /* we hit leaf; return it */
+ if (++hops > TC_HTB_MAXDEPTH) {
+ pr_warn_ratelimited("htb: classify loop detected, dropping packet\n");
+ return NULL;
+ }
/* we have got inner class; apply inner filter chain */
tcf = rcu_dereference_bh(cl->filter_list);
}
@@ -632,13 +637,11 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
} else {
return qdisc_drop(skb, sch, to_free);
}
-#ifdef CONFIG_NET_CLS_ACT
} else if (!cl) {
if (ret & __NET_XMIT_BYPASS)
qdisc_qstats_drop(sch);
__qdisc_drop(skb, to_free);
return ret;
-#endif
} else if ((ret = qdisc_enqueue(skb, cl->leaf.q,
to_free)) != NET_XMIT_SUCCESS) {
if (net_xmit_drop_count(ret)) {
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index e871cb53946a..a53ea6ac2296 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -796,7 +796,8 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt,
q->tail = NULL;
q->divisor = SFQ_DEFAULT_HASH_DIVISOR;
q->maxflows = SFQ_DEFAULT_FLOWS;
- q->quantum = psched_mtu(qdisc_dev(sch));
+ q->quantum = clamp_t(u32, psched_mtu(qdisc_dev(sch)),
+ 256, 1 << 20);
q->perturb_period = 0;
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 13e69bce987e..ace524afdcf5 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -377,6 +377,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
nores = 1;
break;
}
+ skb->dev = dev;
__skb_pull(skb, skb_network_offset(skb));
} while ((q = rcu_dereference(NEXT_SLAVE(q))) != start);
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index a024c0843247..5f988b3a8814 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -71,8 +71,11 @@ void sctp_inq_free(struct sctp_inq *queue)
*/
void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
{
- /* Directly call the packet handling routine. */
- if (chunk->rcvr->dead) {
+ /* Directly call the packet handling routine. Drop the chunk if the
+ * receiver or the transport it was looked up on is gone.
+ */
+ if (chunk->rcvr->dead ||
+ (chunk->transport && chunk->transport->dead)) {
sctp_chunk_free(chunk);
return;
}
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 23d6633966b1..ed0a36acc5e1 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1326,6 +1326,10 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0;
}
+ /* No chunk left in this packet may use this asoc. */
+ if (event_type == SCTP_EVENT_T_CHUNK &&
+ chunk->asoc == asoc)
+ chunk->pdiscard = 1;
/* Delete the current association. */
sctp_cmd_delete_tcb(commands, asoc);
asoc = NULL;
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index 0f81820c730b..c62a8a1cf405 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -22,6 +22,15 @@
#include <net/sctp/sm.h>
#include <net/sctp/stream_sched.h>
+#define SCTP_STRRESET_MASK(type) \
+ BIT(ntohs(type) - ntohs(SCTP_PARAM_RESET_OUT_REQUEST))
+#define SCTP_STRRESET_TEST(asoc, type) \
+ ((asoc)->strreset_outstanding & SCTP_STRRESET_MASK(type))
+#define SCTP_STRRESET_SET(asoc, type) \
+ ((asoc)->strreset_outstanding |= SCTP_STRRESET_MASK(type))
+#define SCTP_STRRESET_CLEAR(asoc, type) \
+ ((asoc)->strreset_outstanding &= ~SCTP_STRRESET_MASK(type))
+
static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
{
struct sctp_association *asoc;
@@ -372,7 +381,10 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
goto out;
}
- asoc->strreset_outstanding = out + in;
+ if (out)
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST);
+ if (in)
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_IN_REQUEST);
out:
return retval;
@@ -417,7 +429,7 @@ int sctp_send_reset_assoc(struct sctp_association *asoc)
return retval;
}
- asoc->strreset_outstanding = 1;
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_TSN_REQUEST);
return 0;
}
@@ -474,7 +486,10 @@ int sctp_send_add_streams(struct sctp_association *asoc,
goto out;
}
- asoc->strreset_outstanding = !!out + !!in;
+ if (out)
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS);
+ if (in)
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS);
out:
return retval;
@@ -482,13 +497,13 @@ int sctp_send_add_streams(struct sctp_association *asoc,
static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
struct sctp_association *asoc, __be32 resp_seq,
- __be16 type)
+ __be16 type, bool match_seq)
{
struct sctp_chunk *chunk = asoc->strreset_chunk;
struct sctp_reconf_chunk *hdr;
union sctp_params param;
- if (!chunk)
+ if (!chunk || !chunk->transport)
return NULL;
hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
@@ -499,7 +514,7 @@ static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
*/
struct sctp_strreset_tsnreq *req = param.v;
- if ((!resp_seq || req->request_seq == resp_seq) &&
+ if ((!match_seq || req->request_seq == resp_seq) &&
(!type || type == req->param_hdr.type))
return param.v;
}
@@ -564,13 +579,14 @@ struct sctp_chunk *sctp_process_strreset_outreq(
if (asoc->strreset_chunk) {
if (!sctp_chunk_lookup_strreset_param(
asoc, outreq->response_seq,
- SCTP_PARAM_RESET_IN_REQUEST)) {
+ SCTP_PARAM_RESET_IN_REQUEST, true) ||
+ !SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_IN_REQUEST)) {
/* same process with outstanding isn't 0 */
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
- asoc->strreset_outstanding--;
+ SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_IN_REQUEST);
asoc->strreset_outseq++;
if (!asoc->strreset_outstanding) {
@@ -669,7 +685,7 @@ struct sctp_chunk *sctp_process_strreset_inreq(
SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
asoc->strreset_chunk = chunk;
- asoc->strreset_outstanding = 1;
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST);
sctp_chunk_hold(asoc->strreset_chunk);
result = SCTP_STRRESET_PERFORMED;
@@ -816,13 +832,14 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out(
if (asoc->strreset_chunk) {
if (!sctp_chunk_lookup_strreset_param(
- asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
+ asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS, false) ||
+ !SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
/* same process with outstanding isn't 0 */
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
- asoc->strreset_outstanding--;
+ SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS);
asoc->strreset_outseq++;
if (!asoc->strreset_outstanding) {
@@ -899,7 +916,7 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in(
goto out;
asoc->strreset_chunk = chunk;
- asoc->strreset_outstanding = 1;
+ SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS);
sctp_chunk_hold(asoc->strreset_chunk);
stream->outcnt = outcnt;
@@ -927,8 +944,9 @@ struct sctp_chunk *sctp_process_strreset_resp(
struct sctp_paramhdr *req;
__u32 result;
- req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
- if (!req)
+ req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0,
+ true);
+ if (!req || !SCTP_STRRESET_TEST(asoc, req->type))
return NULL;
result = ntohl(resp->result);
@@ -1078,7 +1096,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
nums, 0, GFP_ATOMIC);
}
- asoc->strreset_outstanding--;
+ SCTP_STRRESET_CLEAR(asoc, req->type);
asoc->strreset_outseq++;
/* remove everything for this reconf request */
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index 087102ff3c64..e5b5490da251 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -119,7 +119,9 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct smc_sock *smc;
struct sock *child;
@@ -144,7 +146,7 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
/* passthrough to original syn recv sock fct */
child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
- own_req);
+ own_req, opt_child_init);
/* child must not inherit smc or its ops */
if (child) {
rcu_assign_sk_user_data(child, NULL);
@@ -389,13 +391,13 @@ void smc_sk_init(struct net *net, struct sock *sk, int protocol)
INIT_LIST_HEAD(&smc->accept_q);
spin_lock_init(&smc->accept_q_lock);
spin_lock_init(&smc->conn.send_lock);
- sk->sk_prot->hash(sk);
mutex_init(&smc->clcsock_release_lock);
smc_init_saved_callbacks(smc);
smc->limit_smc_hs = net->smc.limit_smc_hs;
smc->use_fallback = false; /* assume rdma capability first */
smc->fallback_rsn = 0;
smc_close_init(smc);
+ sk->sk_prot->hash(sk);
}
static struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
@@ -3224,7 +3226,8 @@ static int smc_ioctl(struct socket *sock, unsigned int cmd,
return -EINVAL;
}
if (smc->sk.sk_state == SMC_INIT ||
- smc->sk.sk_state == SMC_CLOSED)
+ smc->sk.sk_state == SMC_CLOSED ||
+ !READ_ONCE(smc->conn.sndbuf_desc))
answ = 0;
else
answ = smc->conn.sndbuf_desc->len -
diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
index d325dbd01225..b64ea013c225 100644
--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -1086,13 +1086,13 @@ struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
read_unlock_bh(&lgr->conns_lock);
/* pre-fetch buffer outside of send_lock, might sleep */
rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
- if (rc)
- goto err_out;
- /* avoid race with smcr_tx_sndbuf_nonempty() */
- spin_lock_bh(&conn->send_lock);
- smc_switch_link_and_count(conn, to_lnk);
- rc = smc_switch_cursor(smc, pend, wr_buf);
- spin_unlock_bh(&conn->send_lock);
+ if (!rc) {
+ /* avoid race with smcr_tx_sndbuf_nonempty() */
+ spin_lock_bh(&conn->send_lock);
+ smc_switch_link_and_count(conn, to_lnk);
+ rc = smc_switch_cursor(smc, pend, wr_buf);
+ spin_unlock_bh(&conn->send_lock);
+ }
sock_put(&smc->sk);
if (rc)
goto err_out;
@@ -1147,14 +1147,16 @@ static void smcd_buf_detach(struct smc_connection *conn)
{
struct smcd_dev *smcd = conn->lgr->smcd;
u64 peer_token = conn->peer_token;
+ struct smc_buf_desc *buf_desc;
if (!conn->sndbuf_desc)
return;
smc_ism_detach_dmb(smcd, peer_token);
- kfree(conn->sndbuf_desc);
+ buf_desc = conn->sndbuf_desc;
conn->sndbuf_desc = NULL;
+ kfree(buf_desc);
}
static void smc_buf_unuse(struct smc_connection *conn,
@@ -1198,11 +1200,10 @@ void smc_conn_free(struct smc_connection *conn)
goto lgr_put;
if (lgr->is_smcd) {
- if (!list_empty(&lgr->list))
- smc_ism_unset_conn(conn);
+ smc_ism_unset_conn(conn);
+ tasklet_kill(&conn->rx_tsklet);
if (smc_ism_support_dmb_nocopy(lgr->smcd))
smcd_buf_detach(conn);
- tasklet_kill(&conn->rx_tsklet);
} else {
smc_cdc_wait_pend_tx_wr(conn);
if (current_work() != &conn->abort_work)
@@ -1455,12 +1456,12 @@ static void smc_conn_kill(struct smc_connection *conn, bool soft)
smc_sk_wake_ups(smc);
if (conn->lgr->is_smcd) {
smc_ism_unset_conn(conn);
- if (smc_ism_support_dmb_nocopy(conn->lgr->smcd))
- smcd_buf_detach(conn);
if (soft)
tasklet_kill(&conn->rx_tsklet);
else
tasklet_unlock_wait(&conn->rx_tsklet);
+ if (smc_ism_support_dmb_nocopy(conn->lgr->smcd))
+ smcd_buf_detach(conn);
} else {
smc_cdc_wait_pend_tx_wr(conn);
}
diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c
index 149d63cff667..1932983ef763 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -320,6 +320,7 @@ int smc_llc_flow_initiate(struct smc_link_group *lgr,
void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
{
spin_lock_bh(&lgr->llc_flow_lock);
+ smc_llc_flow_qentry_del(flow);
memset(flow, 0, sizeof(*flow));
flow->type = SMC_LLC_FLOW_NONE;
spin_unlock_bh(&lgr->llc_flow_lock);
@@ -1902,6 +1903,8 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
if (lgr->llc_flow_lcl.type ==
SMC_LLC_FLOW_REQ_ADD_LINK) {
/* server started add_link processing */
+ /* free any qentry stashed in REQ_ADD_LINK state */
+ smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK;
smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
qentry);
diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
index 05229a0a07e4..374886f33228 100644
--- a/net/smc/smc_rx.c
+++ b/net/smc/smc_rx.c
@@ -115,16 +115,15 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private;
+ struct smc_connection *conn = &priv->smc->conn;
struct smc_sock *smc = priv->smc;
- struct smc_connection *conn;
struct sock *sk = &smc->sk;
- if (sk->sk_state == SMC_CLOSED ||
- sk->sk_state == SMC_PEERFINCLOSEWAIT ||
- sk->sk_state == SMC_APPFINCLOSEWAIT)
- goto out;
- conn = &smc->conn;
lock_sock(sk);
+ if (conn->freed) {
+ release_sock(sk);
+ goto out;
+ }
smc_rx_update_cons(smc, priv->len);
release_sock(sk);
if (atomic_sub_and_test(priv->len, &conn->splice_pending))
diff --git a/net/smc/smc_tx.h b/net/smc/smc_tx.h
index a59f370b8b43..610a945aefd6 100644
--- a/net/smc/smc_tx.h
+++ b/net/smc/smc_tx.h
@@ -20,11 +20,15 @@
static inline int smc_tx_prepared_sends(struct smc_connection *conn)
{
+ struct smc_buf_desc *sndbuf_desc = READ_ONCE(conn->sndbuf_desc);
union smc_host_cursor sent, prep;
+ if (!sndbuf_desc)
+ return 0;
+
smc_curs_copy(&sent, &conn->tx_curs_sent, conn);
smc_curs_copy(&prep, &conn->tx_curs_prep, conn);
- return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep);
+ return smc_curs_diff(sndbuf_desc->len, &sent, &prep);
}
void smc_tx_pending(struct smc_connection *conn);
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 13a948e2f83d..73cabe65de1c 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -2070,7 +2070,11 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
goto unwrap_failed;
opaque_len = be32_to_cpup(p++);
offset = (u8 *)(p) - (u8 *)head->iov_base;
- if (offset + opaque_len > rcv_buf->len)
+ if (offset > rcv_buf->len)
+ goto unwrap_failed;
+ if (opaque_len > rcv_buf->len - offset)
+ goto unwrap_failed;
+ if (opaque_len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index b3e1738ff6bf..71b406d00472 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -74,6 +74,8 @@ static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
int shifted = 0;
int this_shift;
+ if (!buf->len)
+ return;
shift %= buf->len;
while (shifted < shift) {
this_shift = min(shift - shifted, LOCAL_BUF_LEN);
@@ -86,6 +88,8 @@ static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
{
struct xdr_buf subbuf;
+ if (buf->len <= base)
+ return;
xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
_rotate_left(&subbuf, shift);
}
@@ -155,6 +159,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
dprintk("RPC: %s\n", __func__);
+ if (len - offset <= GSS_KRB5_TOK_HDR_LEN)
+ return GSS_S_DEFECTIVE_TOKEN;
+
ptr = buf->head[0].iov_base + offset;
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
@@ -221,14 +228,16 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
* head buffer space rather than that actually occupied.
*/
movelen = min_t(unsigned int, buf->head[0].iov_len, len);
+ if (movelen < offset + GSS_KRB5_TOK_HDR_LEN + headskip)
+ return GSS_S_DEFECTIVE_TOKEN;
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
- BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
- buf->head[0].iov_len);
memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
/* Trim off the trailing "extra count" and checksum blob */
+ if (ec + GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset)
+ return GSS_S_DEFECTIVE_TOKEN;
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c
index f549e4c05def..711857ac688a 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -121,12 +121,6 @@ static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt)
return result;
}
-void init_gssp_clnt(struct sunrpc_net *sn)
-{
- mutex_init(&sn->gssp_lock);
- sn->gssp_clnt = NULL;
-}
-
int set_gssp_clnt(struct net *net)
{
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.h b/net/sunrpc/auth_gss/gss_rpc_upcall.h
index 31e96344167e..b3c2b2b90798 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.h
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.h
@@ -29,7 +29,6 @@ int gssp_accept_sec_context_upcall(struct net *net,
struct gssp_upcall_data *data);
void gssp_free_upcall_data(struct gssp_upcall_data *data);
-void init_gssp_clnt(struct sunrpc_net *);
int set_gssp_clnt(struct net *);
void clear_gssp_clnt(struct sunrpc_net *);
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 49a8623cc1d5..01a158343463 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -959,6 +959,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
}
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
+ if (len <= GSS_KRB5_TOK_HDR_LEN)
+ goto unwrap_failed;
offset = xdr_stream_pos(xdr);
saved_len = buf->len;
@@ -1476,7 +1478,6 @@ static int create_use_gss_proxy_proc_entry(struct net *net)
&use_gss_proxy_proc_ops, net);
if (!*p)
return -ENOMEM;
- init_gssp_clnt(sn);
return 0;
}
@@ -1583,6 +1584,9 @@ svcauth_gss_decode_credbody(struct xdr_stream *xdr,
u32 body_len;
__be32 *p;
+ /* Early-return paths leave deterministic state, not stale residue. */
+ memset(gc, 0, sizeof(*gc));
+
p = xdr_inline_decode(xdr, XDR_UNIT);
if (!p)
return false;
@@ -1951,6 +1955,8 @@ svcauth_gss_release(struct svc_rqst *rqstp)
if (!gsd)
goto out;
+ if (rqstp->rq_auth_stat != rpc_auth_ok)
+ goto out;
gc = &gsd->clcred;
if (gc->gc_proc != RPC_GSS_PROC_DATA)
goto out;
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 447d2a0b75fd..f2c0764b9586 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -410,10 +410,9 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd)
list_del_init(&cd->others);
spin_unlock(&cd->hash_lock);
spin_unlock(&cache_list_lock);
- if (list_empty(&cache_list)) {
- /* module must be being unloaded so its safe to kill the worker */
- cancel_delayed_work_sync(&cache_cleaner);
- }
+ cancel_delayed_work_sync(&cache_cleaner);
+ if (!list_empty(&cache_list))
+ queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0);
}
EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 53bcca365fb1..2d74c660de66 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -490,6 +490,8 @@ static int rpcb_register_inet4(struct sunrpc_net *sn,
int result;
map->r_addr = rpc_sockaddr2uaddr(sap, GFP_KERNEL);
+ if (!map->r_addr)
+ return -ENOMEM;
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET];
if (port != 0) {
@@ -516,6 +518,8 @@ static int rpcb_register_inet6(struct sunrpc_net *sn,
int result;
map->r_addr = rpc_sockaddr2uaddr(sap, GFP_KERNEL);
+ if (!map->r_addr)
+ return -ENOMEM;
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET];
if (port != 0) {
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index 691c0000e9ea..a0fb2a7a72f5 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -54,6 +54,7 @@ static __net_init int sunrpc_init_net(struct net *net)
INIT_LIST_HEAD(&sn->all_clients);
spin_lock_init(&sn->rpc_client_lock);
spin_lock_init(&sn->rpcb_clnt_lock);
+ mutex_init(&sn->gssp_lock);
return 0;
err_pipefs:
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 4d8d46074716..3ac71af655ed 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -372,6 +372,7 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)
struct svc_pool_map *m = &svc_pool_map;
int cpu = raw_smp_processor_id();
unsigned int pidx = 0;
+ unsigned int i;
if (serv->sv_nrpools <= 1)
return serv->sv_pools;
@@ -384,8 +385,34 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)
pidx = m->to_pool[cpu_to_node(cpu)];
break;
}
+ pidx %= serv->sv_nrpools;
+
+ /*
+ * It's possible to have a pool with no threads. Userland can just set
+ * things up this way directly. Also, when threads are autodistributed
+ * they are spread evenly across the pools, but when there are fewer
+ * threads than pools some pools can end up with none.
+ *
+ * A transport enqueued on a threadless pool would never be picked up,
+ * since each thread only services its own pool. Fall back to the next
+ * populated pool, trading NUMA locality for a guarantee that the
+ * transport is serviced.
+ */
+ for (i = 0; i < serv->sv_nrpools; i++) {
+ struct svc_pool *pool = &serv->sv_pools[pidx];
+
+ /* This is set under the service mutex and rarely ever
+ * changes. A data race here is harmless.
+ */
+ if (data_race(pool->sp_nrthreads))
+ return pool;
- return &serv->sv_pools[pidx % serv->sv_nrpools];
+ if (++pidx >= serv->sv_nrpools)
+ pidx = 0;
+ }
+
+ /* No pool has any threads; nothing can service the transport. */
+ return &serv->sv_pools[pidx];
}
int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
@@ -449,6 +476,35 @@ __svc_init_bc(struct svc_serv *serv)
}
#endif
+static int svc_pool_init_counters(struct svc_pool *pool)
+{
+ int err;
+
+ err = percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL);
+ if (err)
+ return err;
+ err = percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL);
+ if (err)
+ goto err_sockets;
+ err = percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL);
+ if (err)
+ goto err_threads;
+ return 0;
+
+err_threads:
+ percpu_counter_destroy(&pool->sp_sockets_queued);
+err_sockets:
+ percpu_counter_destroy(&pool->sp_messages_arrived);
+ return err;
+}
+
+static void svc_pool_destroy_counters(struct svc_pool *pool)
+{
+ percpu_counter_destroy(&pool->sp_messages_arrived);
+ percpu_counter_destroy(&pool->sp_sockets_queued);
+ percpu_counter_destroy(&pool->sp_threads_woken);
+}
+
/*
* Create an RPC service
*/
@@ -513,12 +569,18 @@ __svc_create(struct svc_program *prog, struct svc_stat *stats,
INIT_LIST_HEAD(&pool->sp_all_threads);
spin_lock_init(&pool->sp_lock);
- percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL);
- percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL);
- percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL);
+ if (svc_pool_init_counters(pool))
+ goto out_err;
}
return serv;
+
+out_err:
+ while (i--)
+ svc_pool_destroy_counters(&serv->sv_pools[i]);
+ kfree(serv->sv_pools);
+ kfree(serv);
+ return NULL;
}
/**
@@ -591,9 +653,7 @@ svc_destroy(struct kref *ref)
for (i = 0; i < serv->sv_nrpools; i++) {
struct svc_pool *pool = &serv->sv_pools[i];
- percpu_counter_destroy(&pool->sp_messages_arrived);
- percpu_counter_destroy(&pool->sp_sockets_queued);
- percpu_counter_destroy(&pool->sp_threads_woken);
+ svc_pool_destroy_counters(pool);
}
kfree(serv->sv_pools);
kfree(serv);
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 94912fc08051..ceb9c4b730c9 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -2073,7 +2073,7 @@ void xdr_buf_trim(struct xdr_buf *buf, unsigned int len)
trim -= cur;
}
fix_len:
- buf->len -= (len - trim);
+ buf->len -= min_t(unsigned int, buf->len, len - trim);
}
EXPORT_SYMBOL_GPL(xdr_buf_trim);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index 82c4202fa3c1..347db3b7e0bc 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -981,17 +981,20 @@ static int svc_rdma_read_chunk_range(struct svc_rdma_read_info *info,
const struct svc_rdma_segment *segment;
int ret;
+ if (!length)
+ return 0;
+
ret = -EINVAL;
pcl_for_each_segment(segment, chunk) {
struct svc_rdma_segment dummy;
- if (offset > segment->rs_length) {
+ if (offset >= segment->rs_length) {
offset -= segment->rs_length;
continue;
}
dummy.rs_handle = segment->rs_handle;
- dummy.rs_length = min_t(u32, length, segment->rs_length) - offset;
+ dummy.rs_length = min_t(u32, length, segment->rs_length - offset);
dummy.rs_offset = segment->rs_offset + offset;
ret = svc_rdma_build_read_segment(info, &dummy);
@@ -1000,6 +1003,8 @@ static int svc_rdma_read_chunk_range(struct svc_rdma_read_info *info,
info->ri_totalbytes += dummy.rs_length;
length -= dummy.rs_length;
+ if (!length)
+ break;
offset = 0;
}
return ret;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 6263aa95e2ec..aa1d5b6a2bfc 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1191,9 +1191,9 @@ static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk)
static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *sk)
{
- sk->sk_data_ready = transport->old_data_ready;
+ WRITE_ONCE(sk->sk_data_ready, transport->old_data_ready);
sk->sk_state_change = transport->old_state_change;
- sk->sk_write_space = transport->old_write_space;
+ WRITE_ONCE(sk->sk_write_space, transport->old_write_space);
sk->sk_error_report = transport->old_error_report;
}
@@ -1649,6 +1649,7 @@ static void xs_udp_do_set_buffer_size(struct rpc_xprt *xprt)
{
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct sock *sk = transport->inet;
+ void (*write_space)(struct sock *sock);
if (transport->rcvsize) {
sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
@@ -1657,7 +1658,8 @@ static void xs_udp_do_set_buffer_size(struct rpc_xprt *xprt)
if (transport->sndsize) {
sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
sk->sk_sndbuf = transport->sndsize * xprt->max_reqs * 2;
- sk->sk_write_space(sk);
+ write_space = READ_ONCE(sk->sk_write_space);
+ write_space(sk);
}
}
@@ -1968,8 +1970,8 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt,
xs_save_old_callbacks(transport, sk);
sk->sk_user_data = xprt;
- sk->sk_data_ready = xs_data_ready;
- sk->sk_write_space = xs_udp_write_space;
+ WRITE_ONCE(sk->sk_data_ready, xs_data_ready);
+ WRITE_ONCE(sk->sk_write_space, xs_udp_write_space);
sk->sk_state_change = xs_local_state_change;
sk->sk_error_report = xs_error_report;
sk->sk_use_task_frag = false;
@@ -2171,8 +2173,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
xs_save_old_callbacks(transport, sk);
sk->sk_user_data = xprt;
- sk->sk_data_ready = xs_data_ready;
- sk->sk_write_space = xs_udp_write_space;
+ WRITE_ONCE(sk->sk_data_ready, xs_data_ready);
+ WRITE_ONCE(sk->sk_write_space, xs_udp_write_space);
sk->sk_use_task_frag = false;
xprt_set_connected(xprt);
@@ -2358,9 +2360,9 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
xs_save_old_callbacks(transport, sk);
sk->sk_user_data = xprt;
- sk->sk_data_ready = xs_data_ready;
+ WRITE_ONCE(sk->sk_data_ready, xs_data_ready);
sk->sk_state_change = xs_tcp_state_change;
- sk->sk_write_space = xs_tcp_write_space;
+ WRITE_ONCE(sk->sk_write_space, xs_tcp_write_space);
sk->sk_error_report = xs_error_report;
sk->sk_use_task_frag = false;
@@ -2627,7 +2629,17 @@ static int xs_tls_handshake_sync(struct rpc_xprt *lower_xprt, struct xprtsec_par
rc = wait_for_completion_interruptible_timeout(&lower_transport->handshake_done,
XS_TLS_HANDSHAKE_TO);
if (rc <= 0) {
- tls_handshake_cancel(sk);
+ if (!tls_handshake_cancel(sk)) {
+ /*
+ * Cancellation lost to handshake_complete(): the
+ * callback still owns its xprt reference and is in
+ * flight. Wait for it to finish before returning.
+ */
+ wait_for_completion(&lower_transport->handshake_done);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ goto out;
+ }
if (rc == 0)
rc = -ETIMEDOUT;
goto out_put_xprt;
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index 7d85feebdfd9..8d35e2912dd3 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -430,9 +430,10 @@ static int tls_strp_read_copy(struct tls_strparser *strp, bool qshort)
return 0;
}
-static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
+static bool tls_strp_check_queue_ok(struct tls_strparser *strp,
+ unsigned int len)
{
- unsigned int len = strp->stm.offset + strp->stm.full_len;
+ unsigned int remaining = strp->stm.offset + len;
struct sk_buff *first, *skb;
u32 seq;
@@ -443,9 +444,9 @@ static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
/* Make sure there's no duplicate data in the queue,
* and the decrypted status matches.
*/
- while (skb->len < len) {
+ while (skb->len < remaining) {
seq += skb->len;
- len -= skb->len;
+ remaining -= skb->len;
skb = skb->next;
if (TCP_SKB_CB(skb)->seq != seq)
@@ -524,6 +525,11 @@ static int tls_strp_read_sock(struct tls_strparser *strp)
tls_strp_load_anchor_with_queue(strp, inq);
if (!strp->stm.full_len) {
+ if (inq < TLS_HEADER_SIZE)
+ return tls_strp_read_copy(strp, true);
+ if (!tls_strp_check_queue_ok(strp, TLS_HEADER_SIZE))
+ return tls_strp_read_copy(strp, false);
+
sz = tls_rx_msg_size(strp, strp->anchor);
if (sz < 0)
return sz;
@@ -534,7 +540,7 @@ static int tls_strp_read_sock(struct tls_strparser *strp)
return tls_strp_read_copy(strp, true);
}
- if (!tls_strp_check_queue_ok(strp))
+ if (!tls_strp_check_queue_ok(strp, strp->stm.full_len))
return tls_strp_read_copy(strp, false);
WRITE_ONCE(strp->msg_ready, 1);
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index a06f708f300c..1392fcde7776 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1605,12 +1605,10 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr,
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
}
- if (sk->sk_err) {
- err = -sk->sk_err;
+ err = sock_error(sk);
+ if (err) {
sk->sk_state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
- } else {
- err = 0;
}
out_wait:
diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
index 70db737c52da..6c7221a032ee 100644
--- a/net/vmw_vsock/virtio_transport.c
+++ b/net/vmw_vsock/virtio_transport.c
@@ -717,10 +717,10 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
/* Other works can be queued before 'config->del_vqs()', so we flush
* all works before to free the vsock object to avoid use after free.
*/
- flush_work(&vsock->rx_work);
flush_work(&vsock->tx_work);
flush_work(&vsock->event_work);
flush_work(&vsock->send_pkt_work);
+ flush_work(&vsock->rx_work);
mutex_unlock(&the_virtio_vsock_mutex);
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index aed8338d591d..3caeac3a5f16 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -142,7 +142,7 @@ static int __xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff_xsk *xskb, u32 len,
u64 addr;
int err;
- addr = xp_get_handle(xskb);
+ addr = xp_get_handle(xskb, xskb->pool);
err = xskq_prod_reserve_desc(xs->rx, addr, len, flags);
if (err) {
xs->rx_queue_full++;
@@ -153,6 +153,17 @@ static int __xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff_xsk *xskb, u32 len,
return 0;
}
+static void __xsk_rcv_zc_safe(struct xdp_sock *xs, struct xdp_buff_xsk *xskb,
+ u32 len, u32 flags)
+{
+ u64 addr;
+
+ addr = xp_get_handle(xskb, xskb->pool);
+ __xskq_prod_reserve_desc(xs->rx, addr, len, flags);
+
+ xp_release(xskb);
+}
+
static int xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
{
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
@@ -235,9 +246,11 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
u32 frame_size = __xsk_pool_get_rx_frame_size(xs->pool);
void *copy_from = xsk_copy_xdp_start(xdp), *copy_to;
u32 from_len, meta_len, rem, num_desc;
- struct xdp_buff_xsk *xskb;
+ struct xdp_buff_xsk *xskb, *tmp;
struct xdp_buff *xsk_xdp;
+ LIST_HEAD(xsk_buffs);
skb_frag_t *frag;
+ u32 i;
from_len = xdp->data_end - copy_from;
meta_len = xdp->data - copy_from;
@@ -280,22 +293,45 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
frag = &sinfo->frags[0];
}
+ for (i = 0; i < num_desc; i++) {
+ xsk_xdp = xsk_buff_alloc(xs->pool);
+ if (!xsk_xdp)
+ goto err_alloc;
+
+ xskb = container_of(xsk_xdp, struct xdp_buff_xsk, xdp);
+ if (unlikely(!list_empty(&xskb->list_node)))
+ goto err_alloc;
+
+ list_add_tail(&xskb->list_node, &xsk_buffs);
+ }
+
do {
u32 to_len = frame_size + meta_len;
u32 copied;
- xsk_xdp = xsk_buff_alloc(xs->pool);
+ xskb = list_first_entry(&xsk_buffs, struct xdp_buff_xsk,
+ list_node);
+ list_del_init(&xskb->list_node);
+ xsk_xdp = &xskb->xdp;
copy_to = xsk_xdp->data - meta_len;
copied = xsk_copy_xdp(copy_to, ©_from, to_len, &from_len, &frag, rem);
rem -= copied;
- xskb = container_of(xsk_xdp, struct xdp_buff_xsk, xdp);
- __xsk_rcv_zc(xs, xskb, copied - meta_len, rem ? XDP_PKT_CONTD : 0);
+ __xsk_rcv_zc_safe(xs, xskb, copied - meta_len,
+ rem ? XDP_PKT_CONTD : 0);
meta_len = 0;
} while (rem);
return 0;
+
+err_alloc:
+ list_for_each_entry_safe(xskb, tmp, &xsk_buffs, list_node) {
+ list_del_init(&xskb->list_node);
+ xsk_buff_free(&xskb->xdp);
+ }
+ xs->rx_dropped++;
+ return -ENOMEM;
}
static bool xsk_tx_writeable(struct xdp_sock *xs)
diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
index bb9dfbe419e7..9dc809ed9927 100644
--- a/net/xdp/xsk_buff_pool.c
+++ b/net/xdp/xsk_buff_pool.c
@@ -424,8 +424,10 @@ static int xp_init_dma_info(struct xsk_buff_pool *pool, struct xsk_dma_map *dma_
for (i = 0; i < pool->heads_cnt; i++) {
struct xdp_buff_xsk *xskb = &pool->heads[i];
+ u64 orig_addr;
- xp_init_xskb_dma(xskb, pool, dma_map->dma_pages, xskb->orig_addr);
+ orig_addr = xskb->xdp.data_hard_start - pool->addrs - pool->headroom;
+ xp_init_xskb_dma(xskb, pool, dma_map->dma_pages, orig_addr);
}
}
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
index 13354a1e4280..811d3c739a3a 100644
--- a/net/xdp/xsk_queue.h
+++ b/net/xdp/xsk_queue.h
@@ -405,20 +405,26 @@ static inline void xskq_prod_write_addr_batch(struct xsk_queue *q, struct xdp_de
q->cached_prod = cached_prod;
}
-static inline int xskq_prod_reserve_desc(struct xsk_queue *q,
- u64 addr, u32 len, u32 flags)
+static inline void __xskq_prod_reserve_desc(struct xsk_queue *q,
+ u64 addr, u32 len, u32 flags)
{
struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring;
u32 idx;
- if (xskq_prod_is_full(q))
- return -ENOBUFS;
-
/* A, matches D */
idx = q->cached_prod++ & q->ring_mask;
ring->desc[idx].addr = addr;
ring->desc[idx].len = len;
ring->desc[idx].options = flags;
+}
+
+static inline int xskq_prod_reserve_desc(struct xsk_queue *q,
+ u64 addr, u32 len, u32 flags)
+{
+ if (xskq_prod_is_full(q))
+ return -ENOBUFS;
+
+ __xskq_prod_reserve_desc(q, addr, len, flags);
return 0;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index a5b5d82bd755..53654c5fa9e9 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -627,10 +627,8 @@ static int xfrm_dev_direct_output(struct sock *sk, struct xfrm_state *x,
nf_reset_ct(skb);
err = skb_dst(skb)->ops->local_out(net, sk, skb);
- if (unlikely(err != 1)) {
- kfree_skb(skb);
+ if (unlikely(err != 1))
return err;
- }
/* In transport mode, network destination is
* directly reachable, while in tunnel mode,
diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c
index eeeafe3375c4..394fab99fc4c 100644
--- a/samples/ftrace/ftrace-direct-modify.c
+++ b/samples/ftrace/ftrace-direct-modify.c
@@ -202,9 +202,15 @@ static int __init ftrace_direct_init(void)
ftrace_set_filter_ip(&direct, (unsigned long) my_ip, 0, 0);
ret = register_ftrace_direct(&direct, my_tramp);
- if (!ret)
- simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
- return ret;
+ if (ret)
+ return ret;
+ simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
+ if (IS_ERR(simple_tsk)) {
+ unregister_ftrace_direct(&direct, my_tramp, true);
+ return PTR_ERR(simple_tsk);
+ }
+
+ return 0;
}
static void __exit ftrace_direct_exit(void)
diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
index 35a496603558..e16eeca8c135 100644
--- a/samples/ftrace/ftrace-direct-multi-modify.c
+++ b/samples/ftrace/ftrace-direct-multi-modify.c
@@ -224,9 +224,15 @@ static int __init ftrace_direct_multi_init(void)
ret = register_ftrace_direct(&direct, my_tramp);
- if (!ret)
- simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
- return ret;
+ if (ret)
+ return ret;
+ simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
+ if (IS_ERR(simple_tsk)) {
+ unregister_ftrace_direct(&direct, my_tramp, true);
+ return PTR_ERR(simple_tsk);
+ }
+
+ return 0;
}
static void __exit ftrace_direct_multi_exit(void)
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 96a6e6873098..cfcf2c1bb5ec 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1632,14 +1632,14 @@ static void read_symbols(const char *modname)
struct elf_info info = { };
Elf_Sym *sym;
- if (!parse_elf(&info, modname))
- return;
-
if (!strends(modname, ".o")) {
error("%s: filename must be suffixed with .o\n", modname);
return;
}
+ if (!parse_elf(&info, modname))
+ return;
+
/* strip trailing .o */
mod = new_module(modname, strlen(modname) - strlen(".o"));
diff --git a/scripts/tags.sh b/scripts/tags.sh
index a70d43723146..f5d895cb2178 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -105,7 +105,7 @@ all_compiled_sources()
{
echo include/generated/autoconf.h
find $ignore -name "*.cmd" -exec \
- grep -Poh '(?<=^ )\S+|(?<== )\S+[^\\](?=$)' {} \+ |
+ grep -Poh '(?<=^ )\S+\.([chS]|rs)(?=\s)|(?<== )\S+\.(?1)(?=$)' {} \+ |
awk '!a[$0]++'
} | xargs realpath -esq $([ -z "$KBUILD_ABS_SRCTREE" ] && echo --relative-to=.) |
sort -u
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index cd569fbbfe36..1165d51072ce 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -448,24 +448,22 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const char *name, gfp_t gfp)
{
char *hname;
+ size_t hname_sz;
+ INIT_LIST_HEAD(&policy->list);
+ INIT_LIST_HEAD(&policy->profiles);
+ hname_sz = (prefix ? strlen(prefix) + 2 : 0) + strlen(name) + 1;
/* freed by policy_free */
- if (prefix) {
- hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
- if (hname)
- sprintf(hname, "%s//%s", prefix, name);
- } else {
- hname = aa_str_alloc(strlen(name) + 1, gfp);
- if (hname)
- strcpy(hname, name);
- }
+ hname = aa_str_alloc(hname_sz, gfp);
if (!hname)
return false;
+ if (prefix)
+ scnprintf(hname, hname_sz, "%s//%s", prefix, name);
+ else
+ strscpy(hname, name, hname_sz);
policy->hname = hname;
/* base.name is a substring of fqname */
policy->name = basename(policy->hname);
- INIT_LIST_HEAD(&policy->list);
- INIT_LIST_HEAD(&policy->profiles);
return true;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 919133dc8ef2..a5dff9c38393 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -754,6 +754,8 @@ static int validate_hash_algo(struct dentry *dentry,
return -EACCES;
path = dentry_path(dentry, pathbuf, PATH_MAX);
+ if (IS_ERR(path))
+ path = NULL;
integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
"set_data", errmsg, -EACCES, 0);
diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h
index e8a2bff2e5b6..80930957e84d 100644
--- a/security/landlock/errata/abi-1.h
+++ b/security/landlock/errata/abi-1.h
@@ -14,3 +14,26 @@
* actions.
*/
LANDLOCK_ERRATUM(3)
+
+/**
+ * DOC: erratum_4
+ *
+ * Erratum 4: Creation of whiteout objects
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This fix changes the access rights required for the creation of whiteout
+ * objects through :manpage:`mknod(2)`, :manpage:`renameat2(2)`, or
+ * :manpage:`link(2)`. Creating whiteout objects is now guarded by
+ * ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ *
+ * Whiteout objects are used in OverlayFS to mark the absence of a file in an
+ * upper file system. Despite being created with ``S_IFCHR``, whiteout objects
+ * do not count as character devices.
+ *
+ * Impact:
+ *
+ * Sandboxed programs that create OverlayFS whiteouts (such as fuse-overlayfs)
+ * now require ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of
+ * ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ */
+LANDLOCK_ERRATUM(4)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 7145162f1e59..9c1cee4c4c3c 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -15,6 +15,7 @@
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
+#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/list.h>
@@ -671,7 +672,7 @@ static inline int current_check_access_path(const struct path *const path,
return check_access_path(dom, path, access_request);
}
-static inline access_mask_t get_mode_access(const umode_t mode)
+static inline access_mask_t get_mode_access(const umode_t mode, const dev_t dev)
{
switch (mode & S_IFMT) {
case S_IFLNK:
@@ -679,6 +680,9 @@ static inline access_mask_t get_mode_access(const umode_t mode)
case S_IFDIR:
return LANDLOCK_ACCESS_FS_MAKE_DIR;
case S_IFCHR:
+ /* Whiteout objects are guarded with MAKE_REG. */
+ if (dev == WHITEOUT_DEV)
+ return LANDLOCK_ACCESS_FS_MAKE_REG;
return LANDLOCK_ACCESS_FS_MAKE_CHAR;
case S_IFBLK:
return LANDLOCK_ACCESS_FS_MAKE_BLOCK;
@@ -695,6 +699,13 @@ static inline access_mask_t get_mode_access(const umode_t mode)
}
}
+static inline access_mask_t get_dentry_access(const struct dentry *const dentry)
+{
+ const struct inode *const inode = d_backing_inode(dentry);
+
+ return get_mode_access(inode->i_mode, inode->i_rdev);
+}
+
static inline access_mask_t maybe_remove(const struct dentry *const dentry)
{
if (d_is_negative(dentry))
@@ -782,6 +793,7 @@ static bool collect_domain_accesses(
* @new_dentry: Destination file or directory.
* @removable: Sets to true if it is a rename operation.
* @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE.
+ * @whiteout: Sets to true if it is a rename operation with RENAME_WHITEOUT.
*
* Because of its unprivileged constraints, Landlock relies on file hierarchies
* (and not only inodes) to tie access rights to files. Being able to link or
@@ -830,7 +842,8 @@ static bool collect_domain_accesses(
static int current_check_refer_path(struct dentry *const old_dentry,
const struct path *const new_dir,
struct dentry *const new_dentry,
- const bool removable, const bool exchange)
+ const bool removable, const bool exchange,
+ const bool whiteout)
{
const struct landlock_ruleset *const dom =
landlock_get_current_domain();
@@ -850,18 +863,25 @@ static int current_check_refer_path(struct dentry *const old_dentry,
if (exchange) {
if (unlikely(d_is_negative(new_dentry)))
return -ENOENT;
- access_request_parent1 =
- get_mode_access(d_backing_inode(new_dentry)->i_mode);
+ access_request_parent1 = get_dentry_access(new_dentry);
} else {
access_request_parent1 = 0;
}
- access_request_parent2 =
- get_mode_access(d_backing_inode(old_dentry)->i_mode);
+ access_request_parent2 = get_dentry_access(old_dentry);
if (removable) {
access_request_parent1 |= maybe_remove(old_dentry);
access_request_parent2 |= maybe_remove(new_dentry);
}
+ /*
+ * In case of renameat2(2) with RENAME_WHITEOUT, a whiteout object is
+ * created in the source location, so we require an additional access
+ * right there.
+ */
+ if (whiteout)
+ access_request_parent1 |=
+ get_mode_access(S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+
/* The mount points are the same for old and new paths, cf. EXDEV. */
if (old_dentry->d_parent == new_dir->dentry) {
/*
@@ -1130,7 +1150,7 @@ static int hook_path_link(struct dentry *const old_dentry,
struct dentry *const new_dentry)
{
return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
- false);
+ false, false);
}
static int hook_path_rename(const struct path *const old_dir,
@@ -1141,7 +1161,8 @@ static int hook_path_rename(const struct path *const old_dir,
{
/* old_dir refers to old_dentry->d_parent and new_dir->mnt */
return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
- !!(flags & RENAME_EXCHANGE));
+ !!(flags & RENAME_EXCHANGE),
+ !!(flags & RENAME_WHITEOUT));
}
static int hook_path_mkdir(const struct path *const dir,
@@ -1159,7 +1180,8 @@ static int hook_path_mknod(const struct path *const dir,
if (!dom)
return 0;
- return check_access_path(dom, dir, get_mode_access(mode));
+ return check_access_path(dom, dir,
+ get_mode_access(mode, new_decode_dev(dev)));
}
static int hook_path_symlink(const struct path *const dir,
diff --git a/security/smack/smack.h b/security/smack/smack.h
index aac0574cdfad..e44f4e9f5d2b 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -299,8 +299,9 @@ int smack_populate_secattr(struct smack_known *skp);
* Shared data.
*/
extern int smack_enabled __initdata;
-extern int smack_cipso_direct;
-extern int smack_cipso_mapped;
+extern u8 smack_cipso_auto_level[2];
+#define smack_cipso_direct (+smack_cipso_auto_level[0])
+#define smack_cipso_mapped (+smack_cipso_auto_level[1])
extern struct smack_known *smack_net_ambient;
extern struct smack_known *smack_syslog_label;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 54e22c1afe6c..cda7099d8286 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -145,12 +145,13 @@ static int smk_bu_note(char *note, struct smack_known *sskp,
#define smk_bu_note(note, sskp, oskp, mode, RC) (RC)
#endif
-#ifdef CONFIG_SECURITY_SMACK_BRINGUP
-static int smk_bu_current(char *note, struct smack_known *oskp,
- int mode, int rc)
+static int
+smk_bu_tsk_to_obj(struct task_struct *tsk, const struct task_smack *tsp,
+ char *note, struct smack_known *oskp, int mode, int rc)
{
- struct task_smack *tsp = smack_cred(current_cred());
+#ifdef CONFIG_SECURITY_SMACK_BRINGUP
char acc[SMK_NUM_ACCESS_TYPE + 1];
+ char comm[TASK_COMM_LEN];
if (rc <= 0)
return rc;
@@ -158,14 +159,22 @@ static int smk_bu_current(char *note, struct smack_known *oskp,
rc = 0;
smk_bu_mode(mode, acc);
+
pr_info("Smack %s: (%s %s %s) %s %s\n", smk_bu_mess[rc],
- tsp->smk_task->smk_known, oskp->smk_known,
- acc, current->comm, note);
+ smk_of_task(tsp)->smk_known, oskp->smk_known,
+ acc, get_task_comm(comm, tsk), note);
return 0;
-}
#else
-#define smk_bu_current(note, oskp, mode, RC) (RC)
+ return rc;
#endif
+}
+
+static int smk_bu_current(char *note, struct smack_known *oskp,
+ int mode, int rc)
+{
+ return smk_bu_tsk_to_obj(current, smack_cred(current_cred()),
+ note, oskp, mode, rc);
+}
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_task(struct task_struct *otp, int mode, int rc)
@@ -1907,7 +1916,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
{
struct smack_known **blob;
struct smack_known *skp;
- struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
+ struct smack_known *tkp = smk_of_task_struct_obj(tsk);
const struct cred *tcred;
struct file *file;
int rc;
@@ -3283,14 +3292,20 @@ static int smack_sem_semop(struct kern_ipc_perm *isp, struct sembuf *sops,
}
/**
- * smk_curacc_msq : helper to check if current has access on msq
- * @isp : the msq
+ * smk_tskacc_msq : helper to check if tsk has access on msq
+ * @tsk: the task that requests access
+ * @isp : the sysv msg queue permissions
* @access : access requested
*
- * return 0 if current has access, error otherwise
+ * return 0 if tsk has access, error otherwise
*/
-static int smk_curacc_msq(struct kern_ipc_perm *isp, int access)
+static int
+smk_tskacc_msq(struct task_struct *tsk, struct kern_ipc_perm *isp, int access)
{
+ const bool tsk_is_current = (tsk == current);
+ const struct cred * const tsk_cred =
+ (tsk_is_current ? current_cred() : get_task_cred(tsk));
+ struct task_smack * const tsp = smack_cred(tsk_cred);
struct smack_known *msp = smack_of_ipc(isp);
struct smk_audit_info ad;
int rc;
@@ -3299,11 +3314,25 @@ static int smk_curacc_msq(struct kern_ipc_perm *isp, int access)
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
ad.a.u.ipc_id = isp->id;
#endif
- rc = smk_curacc(msp, access, &ad);
- rc = smk_bu_current("msq", msp, access, rc);
+ rc = smk_tskacc(tsp, msp, access, &ad);
+ rc = smk_bu_tsk_to_obj(tsk, tsp, "msq", msp, access, rc);
+ if (!tsk_is_current)
+ put_cred(tsk_cred);
return rc;
}
+/**
+ * smk_curacc_msq : helper to check if current has access on msq
+ * @isp : the sysv msg queue permissions
+ * @access : access requested
+ *
+ * return 0 if current has access, error otherwise
+ */
+static int smk_curacc_msq(struct kern_ipc_perm *isp, int access)
+{
+ return smk_tskacc_msq(current, isp, access);
+}
+
/**
* smack_msg_queue_associate - Smack access check for msg_queue
* @isp: the object
@@ -3371,21 +3400,21 @@ static int smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg
}
/**
- * smack_msg_queue_msgrcv - Smack access check for msg_queue
+ * smack_msg_queue_msgrcv - check it target has r/w access to msg_queue
* @isp: the object
* @msg: unused
- * @target: unused
+ * @target: the task that msgrcv() from the queue
* @type: unused
* @mode: unused
*
- * Returns 0 if current has read and write access, error code otherwise
+ * Returns 0 if target has read and write access, error code otherwise
*/
static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp,
struct msg_msg *msg,
struct task_struct *target, long type,
int mode)
{
- return smk_curacc_msq(isp, MAY_READWRITE);
+ return smk_tskacc_msq(target, isp, MAY_READWRITE);
}
/**
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 109ad155ffc2..0f304667d857 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -81,18 +81,27 @@ static DEFINE_MUTEX(smk_net6addr_lock);
struct smack_known *smack_net_ambient;
/*
- * This is the level in a CIPSO header that indicates a
+ * Sensitivity levels for automatically created CIPSO labels.
+ * See smack_access.c`smack_populate_secattr()
+ *
+ * [0] "direct" labeling, label length < SMK_CIPSOLEN(24):
* smack label is contained directly in the category set.
* It can be reset via smackfs/direct
- */
-int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
-
-/*
- * This is the level in a CIPSO header that indicates a
+ *
+ * [1] "mapped" labeling, label length >= SMK_CIPSOLEN(24):
* secid is contained directly in the category set.
* It can be reset via smackfs/mapped
*/
-int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
+u8 smack_cipso_auto_level[2] = {
+ SMACK_CIPSO_DIRECT_DEFAULT,
+ SMACK_CIPSO_MAPPED_DEFAULT,
+};
+
+static int
+smk_cipso_auto_level_idx(const struct file *file)
+{
+ return (file_inode(file)->i_ino != SMK_DIRECT);
+}
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
@@ -1616,24 +1625,17 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf,
static ssize_t smk_write_doi(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char temp[80];
- unsigned long u;
+ int ret;
+ u32 u;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- if (count >= sizeof(temp) || count == 0)
- return -EINVAL;
-
- if (copy_from_user(temp, buf, count) != 0)
- return -EFAULT;
+ ret = kstrtou32_from_user(buf, count, 10, &u);
+ if (unlikely(ret))
+ return ret;
- temp[count] = '\0';
-
- if (kstrtoul(temp, 10, &u))
- return -EINVAL;
-
- if (u == CIPSO_V4_DOI_UNKNOWN || u > U32_MAX)
+ if (u == CIPSO_V4_DOI_UNKNOWN)
return -EINVAL;
return smk_cipso_doi(u, GFP_KERNEL) ? : count;
@@ -1646,158 +1648,80 @@ static const struct file_operations smk_doi_ops = {
};
/**
- * smk_read_direct - read() for /smack/direct
- * @filp: file pointer, not actually used
+ * smk_read_cipso_auto_level - read() for smackfs/direct and smackfs/mapped
+ * @filp: file pointer
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
-static ssize_t smk_read_direct(struct file *filp, char __user *buf,
+static ssize_t smk_read_cipso_auto_level(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
- char temp[80];
- ssize_t rc;
+ char temp[sizeof "255"];
+ int n;
if (*ppos != 0)
return 0;
- sprintf(temp, "%d", smack_cipso_direct);
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
-
- return rc;
+ n = sprintf(temp, "%u", (unsigned int)smack_cipso_auto_level[
+ smk_cipso_auto_level_idx(filp)]);
+ return simple_read_from_buffer(buf, count, ppos, temp, n);
}
/**
- * smk_write_direct - write() for /smack/direct
- * @file: file pointer, not actually used
+ * smk_write_cipso_auto_level - write() for smackfs/direct and smackfs/mapped
+ * @filp: file pointer
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Returns number of bytes written or error code, as appropriate
*/
-static ssize_t smk_write_direct(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t
+smk_write_cipso_auto_level(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
{
- struct smack_known *skp;
- char temp[80];
- int i;
+ int ret, idx;
+ u8 i, old_lvl;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
-
- if (count >= sizeof(temp) || count == 0)
- return -EINVAL;
-
- if (copy_from_user(temp, buf, count) != 0)
- return -EFAULT;
-
- temp[count] = '\0';
-
- if (sscanf(temp, "%d", &i) != 1)
- return -EINVAL;
-
/*
- * Don't do anything if the value hasn't actually changed.
- * If it is changing reset the level on entries that were
- * set up to be direct when they were created.
+ * draft-ietf-cipso-ipsecurity-01 (CIPSO 2.2), 3.4.2.4:
+ * "Sensitivity Level is 1 octet in length. Its value is from 0 to 255"
*/
- if (smack_cipso_direct != i) {
- mutex_lock(&smack_known_lock);
- list_for_each_entry_rcu(skp, &smack_known_list, list)
- if (skp->smk_netlabel.attr.mls.lvl ==
- smack_cipso_direct)
- skp->smk_netlabel.attr.mls.lvl = i;
- smack_cipso_direct = i;
- mutex_unlock(&smack_known_lock);
- }
-
- return count;
-}
-
-static const struct file_operations smk_direct_ops = {
- .read = smk_read_direct,
- .write = smk_write_direct,
- .llseek = default_llseek,
-};
-
-/**
- * smk_read_mapped - read() for /smack/mapped
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @count: maximum to send along
- * @ppos: where to start
- *
- * Returns number of bytes read or error code, as appropriate
- */
-static ssize_t smk_read_mapped(struct file *filp, char __user *buf,
- size_t count, loff_t *ppos)
-{
- char temp[80];
- ssize_t rc;
-
- if (*ppos != 0)
- return 0;
-
- sprintf(temp, "%d", smack_cipso_mapped);
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
-
- return rc;
-}
-
-/**
- * smk_write_mapped - write() for /smack/mapped
- * @file: file pointer, not actually used
- * @buf: where to get the data from
- * @count: bytes sent
- * @ppos: where to start
- *
- * Returns number of bytes written or error code, as appropriate
- */
-static ssize_t smk_write_mapped(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct smack_known *skp;
- char temp[80];
- int i;
-
- if (!smack_privileged(CAP_MAC_ADMIN))
- return -EPERM;
-
- if (count >= sizeof(temp) || count == 0)
- return -EINVAL;
-
- if (copy_from_user(temp, buf, count) != 0)
- return -EFAULT;
-
- temp[count] = '\0';
-
- if (sscanf(temp, "%d", &i) != 1)
- return -EINVAL;
+ ret = kstrtou8_from_user(buf, count, 10, &i);
+ if (unlikely(ret))
+ return ret;
/*
* Don't do anything if the value hasn't actually changed.
* If it is changing reset the level on entries that were
- * set up to be mapped when they were created.
+ * set up to be "auto" level when they were created.
*/
- if (smack_cipso_mapped != i) {
+ idx = smk_cipso_auto_level_idx(filp);
+ old_lvl = smack_cipso_auto_level[idx];
+
+ if (old_lvl != i) {
+ struct smack_known *skp;
mutex_lock(&smack_known_lock);
list_for_each_entry_rcu(skp, &smack_known_list, list)
if (skp->smk_netlabel.attr.mls.lvl ==
- smack_cipso_mapped)
+ old_lvl)
skp->smk_netlabel.attr.mls.lvl = i;
- smack_cipso_mapped = i;
+ smack_cipso_auto_level[idx] = i;
mutex_unlock(&smack_known_lock);
}
return count;
}
-static const struct file_operations smk_mapped_ops = {
- .read = smk_read_mapped,
- .write = smk_write_mapped,
+static const struct file_operations
+smk_cipso_auto_level_ops = {
+ .read = smk_read_cipso_auto_level,
+ .write = smk_write_cipso_auto_level,
.llseek = default_llseek,
};
@@ -2196,22 +2120,15 @@ static ssize_t smk_read_logging(struct file *filp, char __user *buf,
static ssize_t smk_write_logging(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char temp[32];
- int i;
+ int i, ret;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- if (count >= sizeof(temp) || count == 0)
- return -EINVAL;
+ ret = kstrtos32_from_user(buf, count, 10, &i);
+ if (unlikely(ret))
+ return ret;
- if (copy_from_user(temp, buf, count) != 0)
- return -EFAULT;
-
- temp[count] = '\0';
-
- if (sscanf(temp, "%d", &i) != 1)
- return -EINVAL;
if (i < 0 || i > 3)
return -EINVAL;
log_policy = i;
@@ -2854,22 +2771,15 @@ static ssize_t smk_read_ptrace(struct file *filp, char __user *buf,
static ssize_t smk_write_ptrace(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char temp[32];
- int i;
+ int i, ret;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- if (*ppos != 0 || count >= sizeof(temp) || count == 0)
- return -EINVAL;
-
- if (copy_from_user(temp, buf, count) != 0)
- return -EFAULT;
-
- temp[count] = '\0';
+ ret = kstrtos32_from_user(buf, count, 10, &i);
+ if (unlikely(ret))
+ return ret;
- if (sscanf(temp, "%d", &i) != 1)
- return -EINVAL;
if (i < SMACK_PTRACE_DEFAULT || i > SMACK_PTRACE_MAX)
return -EINVAL;
smack_ptrace_rule = i;
@@ -2904,7 +2814,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
[SMK_DOI] = {
"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
[SMK_DIRECT] = {
- "direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
+ "direct", &smk_cipso_auto_level_ops, 0644},
[SMK_AMBIENT] = {
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NET4ADDR] = {
@@ -2918,7 +2828,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
[SMK_ACCESSES] = {
"access", &smk_access_ops, S_IRUGO|S_IWUGO},
[SMK_MAPPED] = {
- "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR},
+ "mapped", &smk_cipso_auto_level_ops, 0644},
[SMK_LOAD2] = {
"load2", &smk_load2_ops, S_IRUGO|S_IWUSR},
[SMK_LOAD_SELF2] = {
diff --git a/sound/core/control.c b/sound/core/control.c
index 3b464260795e..784867f29f0b 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -44,7 +44,6 @@ static int snd_ctl_remove_locked(struct snd_card *card,
static int snd_ctl_open(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
int i, err;
@@ -80,9 +79,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
ctl->preferred_subdevice[i] = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_add_tail(&ctl->list, &card->ctl_files);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+ list_add_tail(&ctl->list, &card->ctl_files);
snd_card_unref(card);
return 0;
@@ -98,21 +96,18 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
- unsigned long flags;
struct snd_kctl_event *cread;
- spin_lock_irqsave(&ctl->read_lock, flags);
+ guard(spinlock_irqsave)(&ctl->read_lock);
while (!list_empty(&ctl->events)) {
cread = snd_kctl_event(ctl->events.next);
list_del(&cread->list);
kfree(cread);
}
- spin_unlock_irqrestore(&ctl->read_lock, flags);
}
static int snd_ctl_release(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
struct snd_kcontrol *control;
@@ -121,15 +116,17 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
ctl = file->private_data;
file->private_data = NULL;
card = ctl->card;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_del(&ctl->list);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
- down_write(&card->controls_rwsem);
- list_for_each_entry(control, &card->controls, list)
- for (idx = 0; idx < control->count; idx++)
- if (control->vd[idx].owner == ctl)
- control->vd[idx].owner = NULL;
- up_write(&card->controls_rwsem);
+
+ scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+ list_del(&ctl->list);
+
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ list_for_each_entry(control, &card->controls, list)
+ for (idx = 0; idx < control->count; idx++)
+ if (control->vd[idx].owner == ctl)
+ control->vd[idx].owner = NULL;
+ }
+
snd_fasync_free(ctl->fasync);
snd_ctl_empty_read_queue(ctl);
put_pid(ctl->pid);
@@ -152,7 +149,6 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
- unsigned long flags;
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
@@ -160,34 +156,34 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
return;
if (card->shutdown)
return;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
+
+ guard(read_lock_irqsave)(&card->ctl_files_rwlock);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
list_for_each_entry(ctl, &card->ctl_files, list) {
if (!ctl->subscribed)
continue;
- spin_lock(&ctl->read_lock);
- list_for_each_entry(ev, &ctl->events, list) {
- if (ev->id.numid == id->numid) {
- ev->mask |= mask;
- goto _found;
+ scoped_guard(spinlock, &ctl->read_lock) {
+ list_for_each_entry(ev, &ctl->events, list) {
+ if (ev->id.numid == id->numid) {
+ ev->mask |= mask;
+ goto _found;
+ }
}
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (ev) {
+ ev->id = *id;
+ ev->mask = mask;
+ list_add_tail(&ev->list, &ctl->events);
+ } else {
+ dev_err(card->dev, "No memory available to allocate event\n");
+ }
+_found:
+ wake_up(&ctl->change_sleep);
}
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (ev) {
- ev->id = *id;
- ev->mask = mask;
- list_add_tail(&ev->list, &ctl->events);
- } else {
- dev_err(card->dev, "No memory available to allocate event\n");
- }
- _found:
- wake_up(&ctl->change_sleep);
- spin_unlock(&ctl->read_lock);
snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
}
EXPORT_SYMBOL(snd_ctl_notify);
@@ -210,10 +206,9 @@ void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
id.index += ioff;
id.numid += ioff;
snd_ctl_notify(card, mask, &id);
- down_read(&snd_ctl_layer_rwsem);
+ guard(rwsem_read)(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->lnotify(card, mask, kctl, ioff);
- up_read(&snd_ctl_layer_rwsem);
}
EXPORT_SYMBOL(snd_ctl_notify_one);
@@ -520,9 +515,9 @@ static int snd_ctl_add_replace(struct snd_card *card,
if (snd_BUG_ON(!card || !kcontrol->info))
goto error;
- down_write(&card->controls_rwsem);
- err = __snd_ctl_add_replace(card, kcontrol, mode);
- up_write(&card->controls_rwsem);
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = __snd_ctl_add_replace(card, kcontrol, mode);
+
if (err < 0)
goto error;
return 0;
@@ -616,12 +611,8 @@ static inline int snd_ctl_remove_locked(struct snd_card *card,
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
- int ret;
-
- down_write(&card->controls_rwsem);
- ret = snd_ctl_remove_locked(card, kcontrol);
- up_write(&card->controls_rwsem);
- return ret;
+ guard(rwsem_write)(&card->controls_rwsem);
+ return snd_ctl_remove_locked(card, kcontrol);
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -638,17 +629,12 @@ EXPORT_SYMBOL(snd_ctl_remove);
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
- int ret;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
- ret = snd_ctl_remove_locked(card, kctl);
- up_write(&card->controls_rwsem);
- return ret;
+ return snd_ctl_remove_locked(card, kctl);
}
EXPORT_SYMBOL(snd_ctl_remove_id);
@@ -667,27 +653,18 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
{
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
- int idx, ret;
+ int idx;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
- if (kctl == NULL) {
- ret = -ENOENT;
- goto error;
- }
- if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
- ret = -EINVAL;
- goto error;
- }
+ if (kctl == NULL)
+ return -ENOENT;
+ if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER))
+ return -EINVAL;
for (idx = 0; idx < kctl->count; idx++)
- if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
- ret = -EBUSY;
- goto error;
- }
- ret = snd_ctl_remove_locked(card, kctl);
-error:
- up_write(&card->controls_rwsem);
- return ret;
+ if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file)
+ return -EBUSY;
+ return snd_ctl_remove_locked(card, kctl);
}
/**
@@ -764,18 +741,15 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_kcontrol *kctl;
int saved_numid;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, src_id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
saved_numid = kctl->id.numid;
remove_hash_entries(card, kctl);
kctl->id = *dst_id;
kctl->id.numid = saved_numid;
add_hash_entries(card, kctl);
- up_write(&card->controls_rwsem);
return 0;
}
EXPORT_SYMBOL(snd_ctl_rename_id);
@@ -793,7 +767,7 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
const char *name)
{
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
remove_hash_entries(card, kctl);
if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -801,7 +775,6 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
name, kctl->id.name);
add_hash_entries(card, kctl);
- up_write(&card->controls_rwsem);
}
EXPORT_SYMBOL(snd_ctl_rename);
@@ -859,12 +832,8 @@ EXPORT_SYMBOL(snd_ctl_find_numid_locked);
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
unsigned int numid)
{
- struct snd_kcontrol *kctl;
-
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid_locked(card, numid);
- up_read(&card->controls_rwsem);
- return kctl;
+ guard(rwsem_read)(&card->controls_rwsem);
+ return snd_ctl_find_numid_locked(card, numid);
}
EXPORT_SYMBOL(snd_ctl_find_numid);
@@ -920,37 +889,30 @@ EXPORT_SYMBOL(snd_ctl_find_id_locked);
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
const struct snd_ctl_elem_id *id)
{
- struct snd_kcontrol *kctl;
-
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, id);
- up_read(&card->controls_rwsem);
- return kctl;
+ guard(rwsem_read)(&card->controls_rwsem);
+ return snd_ctl_find_id_locked(card, id);
}
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
- struct snd_ctl_card_info *info;
+ struct snd_ctl_card_info *info __free(kfree) = NULL;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
- down_read(&snd_ioctl_rwsem);
- info->card = card->number;
- strscpy(info->id, card->id, sizeof(info->id));
- strscpy(info->driver, card->driver, sizeof(info->driver));
- strscpy(info->name, card->shortname, sizeof(info->name));
- strscpy(info->longname, card->longname, sizeof(info->longname));
- strscpy(info->mixername, card->mixername, sizeof(info->mixername));
- strscpy(info->components, card->components, sizeof(info->components));
- up_read(&snd_ioctl_rwsem);
- if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
- kfree(info);
+ scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+ info->card = card->number;
+ strscpy(info->id, card->id, sizeof(info->id));
+ strscpy(info->driver, card->driver, sizeof(info->driver));
+ strscpy(info->name, card->shortname, sizeof(info->name));
+ strscpy(info->longname, card->longname, sizeof(info->longname));
+ strscpy(info->mixername, card->mixername, sizeof(info->mixername));
+ strscpy(info->components, card->components, sizeof(info->components));
+ }
+ if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
return -EFAULT;
- }
- kfree(info);
return 0;
}
@@ -960,37 +922,31 @@ static int snd_ctl_elem_list(struct snd_card *card,
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id id;
unsigned int offset, space, jidx;
- int err = 0;
offset = list->offset;
space = list->space;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
list->count = card->controls_count;
list->used = 0;
- if (space > 0) {
- list_for_each_entry(kctl, &card->controls, list) {
- if (offset >= kctl->count) {
- offset -= kctl->count;
- continue;
- }
- for (jidx = offset; jidx < kctl->count; jidx++) {
- snd_ctl_build_ioff(&id, kctl, jidx);
- if (copy_to_user(list->pids + list->used, &id,
- sizeof(id))) {
- err = -EFAULT;
- goto out;
- }
- list->used++;
- if (!--space)
- goto out;
- }
- offset = 0;
+ if (!space)
+ return 0;
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (offset >= kctl->count) {
+ offset -= kctl->count;
+ continue;
}
+ for (jidx = offset; jidx < kctl->count; jidx++) {
+ snd_ctl_build_ioff(&id, kctl, jidx);
+ if (copy_to_user(list->pids + list->used, &id, sizeof(id)))
+ return -EFAULT;
+ list->used++;
+ if (!--space)
+ return 0;
+ }
+ offset = 0;
}
- out:
- up_read(&card->controls_rwsem);
- return err;
+ return 0;
}
static int snd_ctl_elem_list_user(struct snd_card *card,
@@ -1238,16 +1194,12 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
{
struct snd_card *card = ctl->card;
struct snd_kcontrol *kctl;
- int result;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &info->id);
- if (kctl == NULL)
- result = -ENOENT;
- else
- result = __snd_ctl_elem_info(card, kctl, info, ctl);
- up_read(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ return __snd_ctl_elem_info(card, kctl, info, ctl);
}
static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
@@ -1279,19 +1231,15 @@ static int snd_ctl_elem_read(struct snd_card *card,
const u32 pattern = 0xdeadbeef;
int ret;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &control->id);
- if (kctl == NULL) {
- ret = -ENOENT;
- goto unlock;
- }
+ if (!kctl)
+ return -ENOENT;
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
- ret = -EPERM;
- goto unlock;
- }
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || !kctl->get)
+ return -EPERM;
snd_ctl_build_ioff(&control->id, kctl, index_offset);
@@ -1301,7 +1249,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
info.id = control->id;
ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
if (ret < 0)
- goto unlock;
+ return ret;
#endif
if (!snd_ctl_skip_validation(&info))
@@ -1311,7 +1259,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
ret = kctl->get(kctl, control);
snd_power_unref(card);
if (ret < 0)
- goto unlock;
+ return ret;
if (!snd_ctl_skip_validation(&info) &&
sanity_check_elem_value(card, control, &info, pattern) < 0) {
dev_err(card->dev,
@@ -1319,32 +1267,27 @@ static int snd_ctl_elem_read(struct snd_card *card,
control->id.iface, control->id.device,
control->id.subdevice, control->id.name,
control->id.index);
- ret = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
-unlock:
- up_read(&card->controls_rwsem);
- return ret;
+ return 0;
}
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
int result;
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
- return PTR_ERR(control);
+ return PTR_ERR(no_free_ptr(control));
result = snd_ctl_elem_read(card, control);
if (result < 0)
- goto error;
+ return result;
if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- error:
- kfree(control);
+ return -EFAULT;
return result;
}
@@ -1406,23 +1349,21 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
struct snd_card *card;
int result;
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
- return PTR_ERR(control);
+ return PTR_ERR(no_free_ptr(control));
card = file->card;
result = snd_ctl_elem_write(card, file, control);
if (result < 0)
- goto error;
+ return result;
if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- error:
- kfree(control);
+ return -EFAULT;
return result;
}
@@ -1433,25 +1374,18 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
- int result;
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner != NULL)
- result = -EBUSY;
- else {
- vd->owner = file;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (vd->owner)
+ return -EBUSY;
+ vd->owner = file;
+ return 0;
}
static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
@@ -1461,27 +1395,20 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
- int result;
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner == NULL)
- result = -EINVAL;
- else if (vd->owner != file)
- result = -EPERM;
- else {
- vd->owner = NULL;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (!vd->owner)
+ return -EINVAL;
+ if (vd->owner != file)
+ return -EPERM;
+ vd->owner = NULL;
+ return 0;
}
struct user_element {
@@ -1771,11 +1698,9 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
private_size = value_sizes[info->type] * info->count;
alloc_size = compute_user_elem_size(private_size, count);
- down_write(&card->controls_rwsem);
- if (check_user_elem_overflow(card, alloc_size)) {
- err = -ENOMEM;
- goto unlock;
- }
+ guard(rwsem_write)(&card->controls_rwsem);
+ if (check_user_elem_overflow(card, alloc_size))
+ return -ENOMEM;
/*
* Keep memory object for this userspace control. After passing this
@@ -1785,13 +1710,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
*/
err = snd_ctl_new(&kctl, count, access, file);
if (err < 0)
- goto unlock;
+ return err;
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
ue = kzalloc(alloc_size, GFP_KERNEL);
if (!ue) {
kfree(kctl);
- err = -ENOMEM;
- goto unlock;
+ return -ENOMEM;
}
kctl->private_data = ue;
kctl->private_free = snd_ctl_elem_user_free;
@@ -1809,7 +1733,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
err = snd_ctl_elem_init_enum_names(ue);
if (err < 0) {
snd_ctl_free_one(kctl);
- goto unlock;
+ return err;
}
}
@@ -1829,7 +1753,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
if (err < 0) {
snd_ctl_free_one(kctl);
- goto unlock;
+ return err;
}
offset = snd_ctl_get_ioff(kctl, &info->id);
snd_ctl_build_ioff(&info->id, kctl, offset);
@@ -1840,9 +1764,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
* applications because the field originally means PID of a process
* which locks the element.
*/
- unlock:
- up_write(&card->controls_rwsem);
- return err;
+ return 0;
}
static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
@@ -2044,34 +1966,29 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
- down_read(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
- up_read(&ctl->card->controls_rwsem);
+ scoped_guard(rwsem_read, &ctl->card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- down_write(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
- up_write(&ctl->card->controls_rwsem);
+ scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- down_write(&ctl->card->controls_rwsem);
- err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
- up_write(&ctl->card->controls_rwsem);
+ scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
return err;
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
}
- down_read(&snd_ioctl_rwsem);
+
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
- up_read(&snd_ioctl_rwsem);
dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
@@ -2163,9 +2080,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
if (pn == NULL)
return -ENOMEM;
pn->fioctl = fcn;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_add_tail(&pn->list, lists);
- up_write(&snd_ioctl_rwsem);
return 0;
}
@@ -2208,16 +2124,14 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
if (snd_BUG_ON(!fcn))
return -EINVAL;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
list_del(&p->list);
- up_write(&snd_ioctl_rwsem);
kfree(p);
return 0;
}
}
- up_write(&snd_ioctl_rwsem);
snd_BUG();
return -EINVAL;
}
@@ -2264,9 +2178,8 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
{
struct snd_ctl_file *kctl;
int subdevice = -1;
- unsigned long flags;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
+ guard(read_lock_irqsave)(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == task_pid(current)) {
subdevice = kctl->preferred_subdevice[type];
@@ -2274,7 +2187,6 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
break;
}
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
return subdevice;
}
EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
@@ -2304,13 +2216,11 @@ int snd_ctl_request_layer(const char *module_name)
if (module_name == NULL)
return 0;
- down_read(&snd_ctl_layer_rwsem);
- for (lops = snd_ctl_layer; lops; lops = lops->next)
- if (strcmp(lops->module_name, module_name) == 0)
- break;
- up_read(&snd_ctl_layer_rwsem);
- if (lops)
- return 0;
+ scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) {
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ if (strcmp(lops->module_name, module_name) == 0)
+ return 0;
+ }
return request_module(module_name);
}
EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
@@ -2324,20 +2234,19 @@ EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
*/
void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
{
- struct snd_card *card;
int card_number;
- down_write(&snd_ctl_layer_rwsem);
- lops->next = snd_ctl_layer;
- snd_ctl_layer = lops;
- up_write(&snd_ctl_layer_rwsem);
+ scoped_guard(rwsem_write, &snd_ctl_layer_rwsem) {
+ lops->next = snd_ctl_layer;
+ snd_ctl_layer = lops;
+ }
for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
- card = snd_card_ref(card_number);
+ struct snd_card *card __free(snd_card_unref) =
+ snd_card_ref(card_number);
+
if (card) {
- down_read(&card->controls_rwsem);
- lops->lregister(card);
- up_read(&card->controls_rwsem);
- snd_card_unref(card);
+ scoped_guard(rwsem_read, &card->controls_rwsem)
+ lops->lregister(card);
}
}
}
@@ -2355,7 +2264,7 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
{
struct snd_ctl_layer_ops *lops2, *prev_lops2;
- down_write(&snd_ctl_layer_rwsem);
+ guard(rwsem_write)(&snd_ctl_layer_rwsem);
for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) {
if (lops2 == lops) {
if (!prev_lops2)
@@ -2366,7 +2275,6 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
}
prev_lops2 = lops2;
}
- up_write(&snd_ctl_layer_rwsem);
}
EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
@@ -2387,25 +2295,29 @@ static const struct file_operations snd_ctl_f_ops =
.fasync = snd_ctl_fasync,
};
+/* call lops under rwsems; called from snd_ctl_dev_*() below() */
+#define call_snd_ctl_lops(_card, _op) \
+ do { \
+ struct snd_ctl_layer_ops *lops; \
+ guard(rwsem_read)(&(_card)->controls_rwsem); \
+ guard(rwsem_read)(&snd_ctl_layer_rwsem); \
+ for (lops = snd_ctl_layer; lops; lops = lops->next) \
+ lops->_op(_card); \
+ } while (0)
+
/*
* registration of the control device
*/
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
- struct snd_ctl_layer_ops *lops;
int err;
err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, card->ctl_dev);
if (err < 0)
return err;
- down_read(&card->controls_rwsem);
- down_read(&snd_ctl_layer_rwsem);
- for (lops = snd_ctl_layer; lops; lops = lops->next)
- lops->lregister(card);
- up_read(&snd_ctl_layer_rwsem);
- up_read(&card->controls_rwsem);
+ call_snd_ctl_lops(card, lregister);
return 0;
}
@@ -2416,23 +2328,15 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
- struct snd_ctl_layer_ops *lops;
- unsigned long flags;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_for_each_entry(ctl, &card->ctl_files, list) {
- wake_up(&ctl->change_sleep);
- snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+ scoped_guard(read_lock_irqsave, &card->ctl_files_rwlock) {
+ list_for_each_entry(ctl, &card->ctl_files, list) {
+ wake_up(&ctl->change_sleep);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+ }
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
-
- down_read(&card->controls_rwsem);
- down_read(&snd_ctl_layer_rwsem);
- for (lops = snd_ctl_layer; lops; lops = lops->next)
- lops->ldisconnect(card);
- up_read(&snd_ctl_layer_rwsem);
- up_read(&card->controls_rwsem);
+ call_snd_ctl_lops(card, ldisconnect);
return snd_unregister_device(card->ctl_dev);
}
@@ -2444,17 +2348,17 @@ static int snd_ctl_dev_free(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_kcontrol *control;
- down_write(&card->controls_rwsem);
- while (!list_empty(&card->controls)) {
- control = snd_kcontrol(card->controls.next);
- __snd_ctl_remove(card, control, false);
- }
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ while (!list_empty(&card->controls)) {
+ control = snd_kcontrol(card->controls.next);
+ __snd_ctl_remove(card, control, false);
+ }
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
- xa_destroy(&card->ctl_numids);
- xa_destroy(&card->ctl_hash);
+ xa_destroy(&card->ctl_numids);
+ xa_destroy(&card->ctl_hash);
#endif
- up_write(&card->controls_rwsem);
+ }
put_device(card->ctl_dev);
return 0;
}
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 0e8b1bfb040e..ec5456ef0247 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -79,61 +79,56 @@ struct snd_ctl_elem_info32 {
static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info32 __user *data32)
{
- struct snd_ctl_elem_info *data;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
int err;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* copy id */
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
- goto error;
+ return -EFAULT;
/* we need to copy the item index.
* hope this doesn't break anything..
*/
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
- goto error;
+ return -EFAULT;
err = snd_ctl_elem_info(ctl, data);
if (err < 0)
- goto error;
+ return err;
/* restore info to 32bit */
- err = -EFAULT;
/* id, type, access, count */
if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
- goto error;
+ return -EFAULT;
if (put_user(data->owner, &data32->owner))
- goto error;
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (put_user(data->value.integer.min, &data32->value.integer.min) ||
put_user(data->value.integer.max, &data32->value.integer.max) ||
put_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_to_user(&data32->value.integer64,
&data->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_to_user(&data32->value.enumerated,
&data->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
break;
default:
break;
}
- err = 0;
- error:
- kfree(data);
- return err;
+ return 0;
}
/* read / write */
@@ -169,31 +164,25 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
int *countp)
{
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info *info __free(kfree) = NULL;
int err;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
- if (! kctl) {
- up_read(&card->controls_rwsem);
+ if (!kctl)
return -ENOENT;
- }
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- up_read(&card->controls_rwsem);
+ if (info == NULL)
return -ENOMEM;
- }
info->id = *id;
err = snd_power_ref_and_wait(card);
if (!err)
err = kctl->info(kctl, info);
snd_power_unref(card);
- up_read(&card->controls_rwsem);
if (err >= 0) {
err = info->type;
*countp = info->count;
}
- kfree(info);
return err;
}
@@ -289,7 +278,7 @@ static int copy_ctl_value_to_user(void __user *userdata,
static int ctl_elem_read_user(struct snd_card *card,
void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data;
+ struct snd_ctl_elem_value *data __free(kfree) = NULL;
int err, type, count;
data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -299,21 +288,18 @@ static int ctl_elem_read_user(struct snd_card *card,
err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
- goto error;
+ return err;
err = snd_ctl_elem_read(card, data);
if (err < 0)
- goto error;
- err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
- error:
- kfree(data);
- return err;
+ return err;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
}
static int ctl_elem_write_user(struct snd_ctl_file *file,
void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data;
+ struct snd_ctl_elem_value *data __free(kfree) = NULL;
struct snd_card *card = file->card;
int err, type, count;
@@ -324,15 +310,12 @@ static int ctl_elem_write_user(struct snd_ctl_file *file,
err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
- goto error;
+ return err;
err = snd_ctl_elem_write(card, file, data);
if (err < 0)
- goto error;
- err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
- error:
- kfree(data);
- return err;
+ return err;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
}
static int snd_ctl_elem_read_user_compat(struct snd_card *card,
@@ -366,49 +349,44 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
int replace)
{
- struct snd_ctl_elem_info *data;
- int err;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* id, type, access, count */ \
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
- goto error;
+ return -EFAULT;
if (get_user(data->owner, &data32->owner))
- goto error;
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (get_user(data->value.integer.min, &data32->value.integer.min) ||
get_user(data->value.integer.max, &data32->value.integer.max) ||
get_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_from_user(&data->value.integer64,
&data32->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_from_user(&data->value.enumerated,
&data32->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
data->value.enumerated.names_ptr =
(uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
break;
default:
break;
}
- err = snd_ctl_elem_add(file, data, replace);
- error:
- kfree(data);
- return err;
+ return snd_ctl_elem_add(file, data, replace);
}
enum {
@@ -468,16 +446,13 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
#endif /* CONFIG_X86_X32_ABI */
}
- down_read(&snd_ioctl_rwsem);
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_compat_ioctls, list) {
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
}
- up_read(&snd_ioctl_rwsem);
return -ENOIOCTLCMD;
}
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index ba984ed00972..15acb07c6515 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -775,18 +775,17 @@ static int __init snd_ctl_led_init(void)
static void __exit snd_ctl_led_exit(void)
{
struct snd_ctl_led *led;
- struct snd_card *card;
unsigned int group, card_number;
snd_ctl_disconnect_layer(&snd_ctl_led_lops);
for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
if (!snd_ctl_led_card_valid[card_number])
continue;
- card = snd_card_ref(card_number);
- if (card) {
+ struct snd_card *card __free(snd_card_unref) =
+ snd_card_ref(card_number);
+
+ if (card)
snd_ctl_led_sysfs_remove(card);
- snd_card_unref(card);
- }
}
for (group = 0; group < MAX_LED; group++) {
led = &snd_ctl_leds[group];
diff --git a/sound/core/init.c b/sound/core/init.c
index 0dc1d25be184..bc6d7a79b0c6 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -591,6 +591,8 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
static int snd_card_do_free(struct snd_card *card)
{
+ bool managed = card->managed;
+
card->releasing = true;
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
@@ -605,7 +607,7 @@ static int snd_card_do_free(struct snd_card *card)
}
if (card->release_completion)
complete(card->release_completion);
- if (!card->managed)
+ if (!managed)
kfree(card);
return 0;
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 35616d0d528d..11236bc7283f 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1430,6 +1430,8 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EBADFD;
+ if (atomic_read(&runtime->buffer_accessing) < 0)
+ return -EBADFD; /* during hw_params, hw_free or prepare */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!snd_pcm_playback_data(substream))
return -EPIPE;
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index c8a7c3974711..2006313dc925 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -42,9 +42,11 @@ struct seq_midisynth {
struct snd_rawmidi *rmidi;
int device;
int subdevice;
+ struct snd_rawmidi_substream __rcu *input_substream;
+ snd_use_lock_t input_use_lock; /* in-flight event_input users */
struct snd_rawmidi_file input_rfile;
- spinlock_t output_lock; /* protects output_rfile publication */
snd_use_lock_t output_use_lock; /* in-flight event_input users */
+ struct snd_rawmidi_substream __rcu *output_substream;
struct snd_rawmidi_file output_rfile;
int seq_client;
int seq_port;
@@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
msynth = runtime->private_data;
if (msynth == NULL)
return;
+
+ scoped_guard(rcu) {
+ if (rcu_dereference(msynth->input_substream) != substream)
+ return;
+
+ snd_use_lock_use(&msynth->input_use_lock);
+ }
+
memset(&ev, 0, sizeof(ev));
while (runtime->avail > 0) {
res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
@@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
memset(&ev, 0, sizeof(ev));
}
}
+
+ snd_use_lock_free(&msynth->input_use_lock);
}
static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count)
@@ -134,8 +146,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
if (snd_BUG_ON(!msynth))
return -EINVAL;
- scoped_guard(spinlock_irqsave, &msynth->output_lock) {
- substream = msynth->output_rfile.output;
+ scoped_guard(rcu) {
+ substream = rcu_dereference(msynth->output_substream);
if (!substream)
return -ENODEV;
snd_use_lock_use(&msynth->output_use_lock);
@@ -177,7 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
msynth->card = card;
msynth->device = device;
msynth->subdevice = subdevice;
- spin_lock_init(&msynth->output_lock);
+ snd_use_lock_init(&msynth->input_use_lock);
snd_use_lock_init(&msynth->output_use_lock);
return 0;
}
@@ -188,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
int err;
struct seq_midisynth *msynth = private_data;
struct snd_rawmidi_runtime *runtime;
+ struct snd_rawmidi_file rfile = {};
struct snd_rawmidi_params params;
/* open midi port */
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_INPUT,
- &msynth->input_rfile);
+ &rfile);
if (err < 0) {
pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
return err;
}
- runtime = msynth->input_rfile.input->runtime;
+ runtime = rfile.input->runtime;
memset(¶ms, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = input_buffer_size;
- err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms);
+ err = snd_rawmidi_input_params(rfile.input, ¶ms);
if (err < 0) {
- snd_rawmidi_kernel_release(&msynth->input_rfile);
+ snd_rawmidi_kernel_release(&rfile);
return err;
}
snd_midi_event_reset_encode(msynth->parser);
runtime->event = snd_midi_input_event;
runtime->private_data = msynth;
+ msynth->input_rfile = rfile;
+ rcu_assign_pointer(msynth->input_substream, rfile.input);
snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
return 0;
}
@@ -219,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri
{
int err;
struct seq_midisynth *msynth = private_data;
+ struct snd_rawmidi_file rfile;
- if (snd_BUG_ON(!msynth->input_rfile.input))
+ rcu_assign_pointer(msynth->input_substream, NULL);
+ synchronize_rcu();
+ snd_use_lock_sync(&msynth->input_use_lock);
+
+ rfile = msynth->input_rfile;
+ msynth->input_rfile = (struct snd_rawmidi_file){};
+
+ if (snd_BUG_ON(!rfile.input))
return -EINVAL;
- err = snd_rawmidi_kernel_release(&msynth->input_rfile);
+
+ err = snd_rawmidi_kernel_release(&rfile);
return err;
}
@@ -252,8 +276,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
return err;
}
snd_midi_event_reset_decode(msynth->parser);
- scoped_guard(spinlock_irqsave, &msynth->output_lock)
- msynth->output_rfile = rfile;
+ msynth->output_rfile = rfile;
+ rcu_assign_pointer(msynth->output_substream, rfile.output);
return 0;
}
@@ -261,17 +285,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
- struct snd_rawmidi_file rfile = {};
+ struct snd_rawmidi_file rfile;
- scoped_guard(spinlock_irqsave, &msynth->output_lock) {
- rfile = msynth->output_rfile;
- msynth->output_rfile = (struct snd_rawmidi_file){};
- }
+ rcu_assign_pointer(msynth->output_substream, NULL);
+ synchronize_rcu();
+ snd_use_lock_sync(&msynth->output_use_lock);
+ rfile = msynth->output_rfile;
+ msynth->output_rfile = (struct snd_rawmidi_file){};
if (snd_BUG_ON(!rfile.output))
return -EINVAL;
-
- snd_use_lock_sync(&msynth->output_use_lock);
snd_rawmidi_drain_output(rfile.output);
return snd_rawmidi_kernel_release(&rfile);
}
diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c
index 0fe13d031656..85cc67de6330 100644
--- a/sound/core/ump_convert.c
+++ b/sound/core/ump_convert.c
@@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt,
else
status = UMP_SYSEX_STATUS_CONTINUE;
*data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len);
+ data[1] = 0;
offset = 8;
for (i = 0; i < cvt->len; i++) {
*data |= cvt->buf[i] << offset;
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index a38e602b4fc6..9c7671f034d5 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -98,6 +98,9 @@ struct loopback_ops {
struct loopback_cable {
spinlock_t lock;
struct loopback_pcm *streams[2];
+ /* in-flight peer stops running outside cable->lock */
+ atomic_t stop_count;
+ wait_queue_head_t stop_wait;
struct snd_pcm_hardware hw;
/* flags */
unsigned int valid;
@@ -319,35 +322,44 @@ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
+ struct loopback_pcm *dpcm_play, *dpcm_capt;
struct snd_pcm_runtime *runtime, *cruntime;
struct loopback_setup *setup;
struct snd_card *card;
+ bool stop_capture = false;
int check;
- if (cable->valid != CABLE_VALID_BOTH) {
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- goto __notify;
- return 0;
- }
- runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
- substream->runtime;
- cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
- substream->runtime;
- check = runtime->format != cruntime->format ||
- runtime->rate != cruntime->rate ||
- runtime->channels != cruntime->channels;
- if (!check)
- return 0;
- if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- return -EIO;
- } else {
- snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
- substream, SNDRV_PCM_STATE_DRAINING);
- __notify:
- runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
- substream->runtime;
- setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
- card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
+ scoped_guard(spinlock_irqsave, &cable->lock) {
+ dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (cable->valid != CABLE_VALID_BOTH) {
+ if (stream == SNDRV_PCM_STREAM_CAPTURE || !dpcm_play)
+ return 0;
+ } else {
+ if (!dpcm_play || !dpcm_capt)
+ return -EIO;
+ runtime = dpcm_play->substream->runtime;
+ cruntime = dpcm_capt->substream->runtime;
+ if (!runtime || !cruntime)
+ return -EIO;
+ check = runtime->format != cruntime->format ||
+ runtime->rate != cruntime->rate ||
+ runtime->channels != cruntime->channels;
+ if (!check)
+ return 0;
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EIO;
+ else if (cruntime->state == SNDRV_PCM_STATE_RUNNING) {
+ /* close must not free the peer runtime below */
+ atomic_inc(&cable->stop_count);
+ stop_capture = true;
+ }
+ }
+
+ setup = get_setup(dpcm_play);
+ card = dpcm_play->loopback->card;
+ runtime = dpcm_play->substream->runtime;
if (setup->format != runtime->format) {
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&setup->format_id);
@@ -364,6 +376,13 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
setup->channels = runtime->channels;
}
}
+
+ if (stop_capture) {
+ snd_pcm_stop(dpcm_capt->substream, SNDRV_PCM_STATE_DRAINING);
+ if (atomic_dec_and_test(&cable->stop_count))
+ wake_up(&cable->stop_wait);
+ }
+
return 0;
}
@@ -994,24 +1013,29 @@ static void free_cable(struct snd_pcm_substream *substream)
struct loopback *loopback = substream->private_data;
int dev = get_cable_index(substream);
struct loopback_cable *cable;
+ struct loopback_pcm *dpcm;
+ bool other_alive;
cable = loopback->cables[substream->number][dev];
if (!cable)
return;
- if (cable->streams[!substream->stream]) {
- /* other stream is still alive */
- spin_lock_irq(&cable->lock);
- cable->streams[substream->stream] = NULL;
- spin_unlock_irq(&cable->lock);
- } else {
- struct loopback_pcm *dpcm = substream->runtime->private_data;
- if (cable->ops && cable->ops->close_cable && dpcm)
- cable->ops->close_cable(dpcm);
- /* free the cable */
- loopback->cables[substream->number][dev] = NULL;
- kfree(cable);
+ scoped_guard(spinlock_irq, &cable->lock) {
+ cable->streams[substream->stream] = NULL;
+ other_alive = cable->streams[!substream->stream];
}
+
+ /* Pair with the stop_count increment in loopback_check_format(). */
+ wait_event(cable->stop_wait, !atomic_read(&cable->stop_count));
+ if (other_alive)
+ return;
+
+ dpcm = substream->runtime->private_data;
+ if (cable->ops && cable->ops->close_cable && dpcm)
+ cable->ops->close_cable(dpcm);
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
}
static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm)
@@ -1206,6 +1230,8 @@ static int loopback_open(struct snd_pcm_substream *substream)
goto unlock;
}
spin_lock_init(&cable->lock);
+ atomic_set(&cable->stop_count, 0);
+ init_waitqueue_head(&cable->stop_wait);
cable->hw = loopback_pcm_hardware;
if (loopback->timer_source)
cable->ops = &loopback_snd_timer_ops;
@@ -1713,6 +1739,12 @@ static int loopback_probe(struct platform_device *devptr)
int dev = devptr->id;
int err;
+ if (dev < 0 || dev >= SNDRV_CARDS) {
+ dev_warn(&devptr->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
sizeof(struct loopback), &card);
if (err < 0)
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index 3398aee33baa..161ccf194ca4 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platform_device *devptr)
int err;
struct snd_card *card;
+ if (dev < 0 || dev >= SNDRV_CARDS) {
+ dev_warn(&devptr->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
if (port[dev] == SNDRV_AUTO_PORT) {
snd_printk(KERN_ERR "specify port\n");
return -EINVAL;
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 5cfd0e99a13f..3129191b2b50 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -919,6 +919,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
+ if (dev < 0) {
+ dev_warn(&pdev->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev])
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index 619e3f594477..b87f9beb7dd0 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -705,6 +705,12 @@ static int snd_portman_probe(struct platform_device *pdev)
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
+ if (dev < 0) {
+ dev_warn(&pdev->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev])
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 3cbc7a4adcb4..650a7f6c7e0c 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -863,6 +863,12 @@ static int snd_serial_probe(struct platform_device *devptr)
int err;
int dev = devptr->id;
+ if (dev < 0 || dev >= SNDRV_CARDS) {
+ dev_warn(&devptr->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
switch (adaptor[dev]) {
case SNDRV_SERIAL_SOUNDCANVAS:
ins[dev] = 1;
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 58012de90c38..7d41955030e5 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct platform_device *devptr)
int idx, err;
int dev = devptr->id;
+ if (dev < 0 || dev >= SNDRV_CARDS) {
+ dev_warn(&devptr->dev,
+ "Invalid card index %d, using default 0\n", dev);
+ dev = 0;
+ }
+
err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
sizeof(struct snd_card_virmidi), &card);
if (err < 0)
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index db9c296dd688..b9cc9e5e7056 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -889,6 +889,9 @@ snd_harmony_create(struct snd_card *card,
goto free_and_ret;
}
+ spin_lock_init(&h->mixer_lock);
+ spin_lock_init(&h->lock);
+
err = request_irq(padev->irq, snd_harmony_interrupt, 0,
"harmony", h);
if (err) {
@@ -898,9 +901,6 @@ snd_harmony_create(struct snd_card *card,
}
h->irq = padev->irq;
- spin_lock_init(&h->mixer_lock);
- spin_lock_init(&h->lock);
-
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
if (err < 0)
goto free_and_ret;
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 72aa135d69f8..8aa5832f1f2a 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -537,6 +537,11 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
hr1.size = sizeof(hr1);
error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0);
+ if (error) {
+ HPI_DEBUG_LOG(ERROR, "message transport error %d\n",
+ error);
+ return error;
+ }
if (hr0.error) {
HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error);
return hr0.error;
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 00c2eeb2c472..e8a4cf3c84d8 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -624,7 +624,7 @@ static void print_conn_list(struct snd_info_buffer *buffer,
/* Get Cache connections info */
cache_len = snd_hda_get_conn_list(codec, nid, &list);
if (cache_len >= 0 && (cache_len != conn_len ||
- memcmp(list, conn, conn_len) != 0)) {
+ memcmp(list, conn, conn_len * sizeof(*conn)) != 0)) {
snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
if (cache_len > 0) {
snd_iprintf(buffer, " ");
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index ad2180659187..45cb3e7b8d21 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2549,7 +2549,7 @@ static int snd_ice1712_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
- struct snd_card *card;
+ struct snd_card *card __free(snd_card_unref) = NULL;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
const struct snd_ice1712_card_info * const *tbl, *c;
@@ -2561,8 +2561,8 @@ static int snd_ice1712_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
- sizeof(*ice), &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ice), &card);
if (err < 0)
return err;
ice = card->private_data;
@@ -2666,6 +2666,7 @@ static int snd_ice1712_probe(struct pci_dev *pci,
if (err < 0)
return err;
pci_set_drvdata(pci, card);
+ card = NULL; /* probe succeeded, don't release as error */
dev++;
return 0;
}
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 242bd7e04b3e..24e60a876b5a 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1551,6 +1551,13 @@ static int pcxhr_probe(struct pci_dev *pci,
mgr->pci = pci;
mgr->irq = -1;
+ /* ISR lock */
+ mutex_init(&mgr->lock);
+ mutex_init(&mgr->msg_lock);
+
+ /* init setup mutex*/
+ mutex_init(&mgr->setup_mutex);
+
if (request_threaded_irq(pci->irq, pcxhr_interrupt,
pcxhr_threaded_irq, IRQF_SHARED,
KBUILD_MODNAME, mgr)) {
@@ -1564,13 +1571,6 @@ static int pcxhr_probe(struct pci_dev *pci,
"Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
- /* ISR lock */
- mutex_init(&mgr->lock);
- mutex_init(&mgr->msg_lock);
-
- /* init setup mutex*/
- mutex_init(&mgr->setup_mutex);
-
mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
PCXHR_SIZE_MAX_STATUS),
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index ca7f024bf8ec..473024e7d8ff 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -576,24 +576,18 @@ static inline unsigned int calc_linear_pos(struct via82xx_modem *chip,
viadev->bufsize2, viadev->idx_table[idx].offset,
viadev->idx_table[idx].size, count);
#endif
- if (count && size < count) {
+ if (! count)
+ /* bogus count 0 on the DMA boundary? */
+ res = viadev->idx_table[idx].offset;
+ else
+ /* count register returns full size
+ * when end of buffer is reached
+ */
+ res = viadev->idx_table[idx].offset + size;
+ if (check_invalid_pos(viadev, res)) {
dev_dbg(chip->card->dev,
- "invalid via82xx_cur_ptr, using last valid pointer\n");
+ "invalid via82xx_cur_ptr (2), using last valid pointer\n");
res = viadev->lastpos;
- } else {
- if (! count)
- /* bogus count 0 on the DMA boundary? */
- res = viadev->idx_table[idx].offset;
- else
- /* count register returns full size
- * when end of buffer is reached
- */
- res = viadev->idx_table[idx].offset + size;
- if (check_invalid_pos(viadev, res)) {
- dev_dbg(chip->card->dev,
- "invalid via82xx_cur_ptr (2), using last valid pointer\n");
- res = viadev->lastpos;
- }
}
}
viadev->lastpos = res; /* remember the last position */
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index 8281cdae9fd0..da25433986ef 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -738,6 +738,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "BM1403CDA"),
}
},
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7UC"),
+ }
+ },
{
.driver_data = &acp6x_card,
.matches = {
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
index 0e96caa607fb..88d6de6bdbc4 100644
--- a/sound/soc/apple/mca.c
+++ b/sound/soc/apple/mca.c
@@ -210,10 +210,10 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
SERDES_STATUS_EN | SERDES_STATUS_RST,
SERDES_STATUS_RST);
/*
- * Experiments suggest that it takes at most ~1 us
- * for the bit to clear, so wait 2 us for good measure.
+ * The SERDES cluster needs a bit of time to reset itself
+ * and settle before we start poking it. This is... slow...
*/
- udelay(2);
+ udelay(25);
WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) &
SERDES_STATUS_RST);
mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index d25455f39566..ffc8d08e86d4 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -42,6 +42,7 @@ struct cs35l33_private {
struct regmap *regmap;
struct gpio_desc *reset_gpio;
bool amp_cal;
+ bool irq_requested;
int mclk_int;
struct regulator_bulk_data core_supplies[2];
int num_core_supplies;
@@ -883,6 +884,9 @@ static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
goto err;
}
+ if (cs35l33->irq_requested)
+ enable_irq(to_i2c_client(dev)->irq);
+
return 0;
err:
@@ -902,6 +906,10 @@ static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
/* redo the calibration in next power up */
cs35l33->amp_cal = false;
+ /* Drain and block the threaded IRQ before cache_only/power-off. */
+ if (cs35l33->irq_requested)
+ disable_irq(to_i2c_client(dev)->irq);
+
regcache_cache_only(cs35l33->regmap, true);
regcache_mark_dirty(cs35l33->regmap);
regulator_bulk_disable(cs35l33->num_core_supplies,
@@ -1158,10 +1166,12 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
}
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
- cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs35l33", cs35l33);
+ cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l33", cs35l33);
if (ret != 0)
dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+ else
+ cs35l33->irq_requested = true;
/* We could issue !RST or skip it based on AMP topology */
cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index 04d9117b31ac..c9b33d1a1927 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -45,6 +45,7 @@ struct cs35l34_private {
int num_core_supplies;
int mclk_int;
bool tdm_mode;
+ bool irq_requested;
struct gpio_desc *reset_gpio; /* Active-low reset GPIO */
};
@@ -1053,10 +1054,12 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
}
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
- cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs35l34", cs35l34);
+ cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l34", cs35l34);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+ else
+ cs35l34->irq_requested = true;
cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset", GPIOD_OUT_LOW);
@@ -1161,6 +1164,9 @@ static int __maybe_unused cs35l34_runtime_resume(struct device *dev)
dev_err(dev, "Failed to restore register cache\n");
goto err;
}
+
+ if (cs35l34->irq_requested)
+ enable_irq(to_i2c_client(dev)->irq);
return 0;
err:
regcache_cache_only(cs35l34->regmap, true);
@@ -1174,6 +1180,10 @@ static int __maybe_unused cs35l34_runtime_suspend(struct device *dev)
{
struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+ /* Drain and block the threaded IRQ before cache_only/power-off. */
+ if (cs35l34->irq_requested)
+ disable_irq(to_i2c_client(dev)->irq);
+
regcache_cache_only(cs35l34->regmap, true);
regcache_mark_dirty(cs35l34->regmap);
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index 52c33d56b143..033b686ad0d4 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -459,10 +459,8 @@ static int rt700_sdw_remove(struct sdw_slave *slave)
{
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
- if (rt700->hw_init) {
- cancel_delayed_work_sync(&rt700->jack_detect_work);
- cancel_delayed_work_sync(&rt700->jack_btn_check_work);
- }
+ cancel_delayed_work_sync(&rt700->jack_detect_work);
+ cancel_delayed_work_sync(&rt700->jack_btn_check_work);
pm_runtime_disable(&slave->dev);
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index f76252b3f591..f447d0cc2baf 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -573,8 +573,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
cpu_pdev = of_find_device_by_node(cpu_np);
if (!cpu_pdev) {
- dev_err(&pdev->dev, "failed to find CPU DAI device\n");
- ret = -EINVAL;
+ ret = dev_err_probe(&pdev->dev, -EPROBE_DEFER,
+ "failed to find CPU DAI device\n");
goto fail;
}
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index 413df413b5eb..9e771e36da8e 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -171,6 +171,7 @@ static int psc_i2s_of_probe(struct platform_device *op)
psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
if (rc != 0) {
pr_err("Failed to register DAI\n");
+ mpc5200_audio_dma_destroy(op);
return rc;
}
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
index d1e64c3500be..296bea33e16f 100644
--- a/sound/soc/intel/atom/sst/sst_pci.c
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -140,13 +140,15 @@ static int intel_sst_probe(struct pci_dev *pci,
sst_drv_ctx->pci = pci_dev_get(pci);
ret = sst_platform_get_resources(sst_drv_ctx);
if (ret < 0)
- goto do_free_drv_ctx;
+ goto do_put_pci;
pci_set_drvdata(pci, sst_drv_ctx);
sst_configure_runtime_pm(sst_drv_ctx);
return ret;
+do_put_pci:
+ pci_dev_put(sst_drv_ctx->pci);
do_free_drv_ctx:
sst_context_cleanup(sst_drv_ctx);
dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 1adaf0041a48..cd27c522f9da 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -50,25 +50,20 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
num_links * sizeof(*priv->card.dai_link),
GFP_KERNEL | __GFP_ZERO);
if (!links)
- goto err_links;
+ return -ENOMEM;
+
+ priv->card.dai_link = links;
+ priv->card.num_links = num_links;
ldata = krealloc(priv->link_data,
num_links * sizeof(*priv->link_data),
GFP_KERNEL | __GFP_ZERO);
+ /* meson_card_clean_references() will free the links on this error path */
if (!ldata)
- goto err_ldata;
+ return -ENOMEM;
- priv->card.dai_link = links;
priv->link_data = ldata;
- priv->card.num_links = num_links;
return 0;
-
-err_ldata:
- kfree(links);
-err_links:
- dev_err(priv->card.dev, "failed to allocate links\n");
- return -ENOMEM;
-
}
EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index b8a3cb8b7597..ba8cae8e85ef 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai)
goto err_priv;
}
- priv->extclk = devm_clk_get(dev, "extclk");
+ priv->extclk = devm_clk_get_optional(dev, "extclk");
if (IS_ERR(priv->extclk)) {
ret = PTR_ERR(priv->extclk);
- if (ret == -EPROBE_DEFER)
- goto err_priv;
-
- priv->extclk = NULL;
+ goto err_priv;
}
} else {
priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index e2ad6dc2dc85..b6d957f7c7ab 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -697,14 +697,17 @@ EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
int q6apm_graph_start(struct q6apm_graph *graph)
{
struct audioreach_graph *ar_graph = graph->ar_graph;
- int ret = 0;
+ int ret;
- if (ar_graph->start_count == 0)
+ if (ar_graph->start_count == 0) {
ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
+ if (ret)
+ return ret;
+ }
ar_graph->start_count++;
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(q6apm_graph_start);
@@ -712,6 +715,9 @@ int q6apm_graph_stop(struct q6apm_graph *graph)
{
struct audioreach_graph *ar_graph = graph->ar_graph;
+ if (ar_graph->start_count == 0)
+ return 0;
+
if (--ar_graph->start_count > 0)
return 0;
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index dd3cd2c9644a..e86d4516ee03 100644
--- a/sound/soc/samsung/aries_wm8994.c
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -659,6 +659,7 @@ static int aries_audio_probe(struct platform_device *pdev)
goto out;
}
+ of_node_get(aries_dai[0].cpus->of_node);
aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
/* Set CPU of_node for BT DAI */
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7facb7b2dba1..e28cb3c0e14f 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3525,7 +3525,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
- if (item[1] > e->items)
+ if (item[1] >= e->items)
return -EINVAL;
val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
mask |= e->mask << e->shift_r;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index b2b6b7bb8788..7eb8eb35b137 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -825,6 +825,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
struct snd_soc_tplg_mixer_control *mc =
container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
int tlv[SOF_TLV_ITEMS];
+ u32 min, max;
unsigned int mask;
int ret;
@@ -832,6 +833,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN)
return -EINVAL;
+ min = le32_to_cpu(mc->min);
+ max = le32_to_cpu(mc->max);
+ if (min > max || max >= INT_MAX)
+ return -EINVAL;
+
/*
* If control has more than 2 channels we need to override the info. This is because even if
* ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the
@@ -842,12 +848,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
kc->info = snd_sof_volume_info;
scontrol->comp_id = sdev->next_comp_id;
- scontrol->min_volume_step = le32_to_cpu(mc->min);
- scontrol->max_volume_step = le32_to_cpu(mc->max);
+ scontrol->min_volume_step = min;
+ scontrol->max_volume_step = max;
scontrol->num_channels = le32_to_cpu(mc->num_channels);
- scontrol->max = le32_to_cpu(mc->max);
- if (le32_to_cpu(mc->max) == 1)
+ scontrol->max = max;
+ if (max == 1)
goto skip;
/* extract tlv data */
@@ -857,7 +863,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
}
/* set up volume table */
- ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
+ ret = set_up_volume_table(scontrol, tlv, max + 1);
if (ret < 0) {
dev_err(scomp->dev, "error: setting up volume table\n");
return ret;
@@ -890,7 +896,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
return 0;
err:
- if (le32_to_cpu(mc->max) > 1)
+ if (max > 1)
kfree(scontrol->volume_table);
return ret;
diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c
index 56a815caf23f..bb1488e392b8 100644
--- a/sound/soc/xilinx/xlnx_formatter_pcm.c
+++ b/sound/soc/xilinx/xlnx_formatter_pcm.c
@@ -384,7 +384,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,
if (err) {
dev_err(component->dev,
"Unable to set constraint on period bytes\n");
- return err;
+ goto error;
}
/* Resize the buffer bytes as divisible by 64 */
@@ -394,7 +394,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,
if (err) {
dev_err(component->dev,
"Unable to set constraint on buffer bytes\n");
- return err;
+ goto error;
}
/* Set periods as integer multiple */
@@ -403,7 +403,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,
if (err < 0) {
dev_err(component->dev,
"Unable to set constraint on periods to be integer\n");
- return err;
+ goto error;
}
/* enable DMA IOC irq */
@@ -412,6 +412,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
return 0;
+
+error:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ adata->play_stream = NULL;
+ else
+ adata->capture_stream = NULL;
+ kfree(stream_data);
+ return err;
}
static int xlnx_formatter_pcm_close(struct snd_soc_component *component,
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
index 49629d4bb327..bcfb34db37d9 100644
--- a/sound/usb/6fire/comm.c
+++ b/sound/usb/6fire/comm.c
@@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handler(struct urb *urb)
struct midi_runtime *midi_rt = rt->chip->midi;
if (!urb->status) {
- if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+ u8 len = rt->receiver_buffer[1];
+
+ if (rt->receiver_buffer[0] == 0x10 && /* midi in event */
+ len <= COMM_RECEIVER_BUFSIZE - 2 &&
+ urb->actual_length >= len + 2)
if (midi_rt)
midi_rt->in_received(midi_rt,
- rt->receiver_buffer + 2,
- rt->receiver_buffer[1]);
+ rt->receiver_buffer + 2, len);
}
if (!rt->chip->shutdown) {
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index 392b4d8e9e76..c7e7149c6dab 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd2000 *bcd2k)
if (!midi_out_substream)
return;
+ if (!bcd2k->midi_out_urb)
+ return;
+
/* copy command prefix bytes */
memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
sizeof(device_cmd_prefix));
@@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct bcd2000 *bcd2k = substream->rmidi->private_data;
- if (bcd2k->midi_out_active) {
+ if (bcd2k->midi_out_active && bcd2k->midi_out_urb) {
usb_kill_urb(bcd2k->midi_out_urb);
bcd2k->midi_out_active = 0;
}
@@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k)
static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
struct usb_interface *interface)
{
- usb_kill_urb(bcd2k->midi_out_urb);
- usb_kill_urb(bcd2k->midi_in_urb);
+ usb_poison_urb(bcd2k->midi_out_urb);
+ usb_poison_urb(bcd2k->midi_in_urb);
usb_free_urb(bcd2k->midi_out_urb);
usb_free_urb(bcd2k->midi_in_urb);
+ bcd2k->midi_out_urb = NULL;
+ bcd2k->midi_in_urb = NULL;
if (bcd2k->intf) {
usb_set_intfdata(bcd2k->intf, NULL);
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 9094b2db33e7..156b16e48373 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -974,6 +974,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
default:
count = 2;
}
+ if (ep->max_transfer < count)
+ return;
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index f98f7bbea2b1..a89a0cdb90e2 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -659,8 +659,6 @@ static int do_show_subset(int argc, char **argv)
show_map_close_json(fds[i], &info);
else
show_map_close_plain(fds[i], &info);
-
- close(fds[i]);
}
if (json_output && nb_fds > 1)
jsonw_end_array(json_wtr); /* root array */
@@ -787,6 +785,12 @@ static int maps_have_btf(int *fds, int nb_fds)
static struct btf *btf_vmlinux;
+static void free_btf_vmlinux(void)
+{
+ btf__free(btf_vmlinux);
+ btf_vmlinux = NULL;
+}
+
static int get_map_kv_btf(const struct bpf_map_info *info, struct btf **btf)
{
int err = 0;
@@ -886,7 +890,6 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
exit_free:
free(key);
free(value);
- close(fd);
free_map_kv_btf(btf);
return err;
@@ -935,6 +938,7 @@ static int do_dump(int argc, char **argv)
for (i = 0; i < nb_fds; i++) {
if (bpf_map_get_info_by_fd(fds[i], &info, &len)) {
p_err("can't get map info: %s", strerror(errno));
+ err = -1;
break;
}
err = map_dump(fds[i], &info, wtr, nb_fds > 1);
@@ -955,7 +959,7 @@ static int do_dump(int argc, char **argv)
close(fds[i]);
exit_free:
free(fds);
- btf__free(btf_vmlinux);
+ free_btf_vmlinux();
return err;
}
@@ -1046,7 +1050,7 @@ static void print_key_value(struct bpf_map_info *info, void *key,
btf_wtr = get_btf_writer();
if (!btf_wtr) {
p_info("failed to create json writer for btf. falling back to plain output");
- btf__free(btf);
+ free_map_kv_btf(btf);
btf = NULL;
print_entry_plain(info, key, value);
} else {
@@ -1062,7 +1066,7 @@ static void print_key_value(struct bpf_map_info *info, void *key,
} else {
print_entry_plain(info, key, value);
}
- btf__free(btf);
+ free_map_kv_btf(btf);
}
static int do_lookup(int argc, char **argv)
diff --git a/tools/bpf/bpftool/struct_ops.c b/tools/bpf/bpftool/struct_ops.c
index d110c6ad8175..05f08dae661e 100644
--- a/tools/bpf/bpftool/struct_ops.c
+++ b/tools/bpf/bpftool/struct_ops.c
@@ -637,6 +637,10 @@ int do_struct_ops(int argc, char **argv)
err = cmd_select(cmds, argc, argv, do_help);
btf__free(btf_vmlinux);
+ btf_vmlinux = NULL;
+ map_info_type = NULL;
+ map_info_alloc_len = 0;
+ map_info_type_id = 0;
return err;
}
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 934e2777a2db..b87d73bbcc30 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -109,7 +109,8 @@ FEATURE_TESTS_EXTRA := \
libbpf-bpf_create_map \
libpfm4 \
libdebuginfod \
- clang-bpf-co-re
+ clang-bpf-co-re \
+ bpftool-skeletons
FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC)
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 17e03236e346..3fd36364382b 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -402,6 +402,9 @@ $(OUTPUT)test-file-handle.bin:
$(OUTPUT)test-libpfm4.bin:
$(BUILD) -lpfm
+$(OUTPUT)test-bpftool-skeletons.bin:
+ $(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons' \
+ > $(@:.bin=.make.output) 2>&1
###############################
clean:
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index 1684216e826a..d5590686a5b5 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -79,7 +79,7 @@
#define __read_mostly
#ifndef __attribute_const__
-# define __attribute_const__
+# define __attribute_const__ __attribute__ ((__const__))
#endif
#ifndef __maybe_unused
diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h
index 41ebd394b90c..377ea7f4b1c7 100644
--- a/tools/include/nolibc/arch-powerpc.h
+++ b/tools/include/nolibc/arch-powerpc.h
@@ -23,7 +23,7 @@
*/
#define _NOLIBC_SYSCALL_CLOBBERLIST \
- "memory", "cr0", "r12", "r11", "r10", "r9"
+ "memory", "cr0", "ctr", "xer", "r12", "r11", "r10", "r9"
#define my_syscall0(num) \
({ \
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 946ca07968a9..8134a32132d1 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -11234,13 +11234,14 @@ static const char *arch_specific_lib_paths(void)
/* Get full path to program/shared library. */
static int resolve_full_path(const char *file, char *result, size_t result_sz)
{
- const char *search_paths[3] = {};
+ const char *search_paths[4] = {};
int i, perm;
if (str_has_sfx(file, ".so") || strstr(file, ".so.")) {
search_paths[0] = getenv("LD_LIBRARY_PATH");
search_paths[1] = "/usr/lib64:/usr/lib";
search_paths[2] = arch_specific_lib_paths();
+ search_paths[3] = "/lib64:/lib";
perm = R_OK;
} else {
search_paths[0] = getenv("PATH");
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 16cb7278faba..dfdfac2a11ad 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -51,6 +51,7 @@
#define COMM_LEN 20
#define SYM_LEN 129
#define MAX_PID 1024000
+#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */
static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
@@ -448,17 +449,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta
static struct task_desc *register_pid(struct perf_sched *sched,
unsigned long pid, const char *comm)
{
- struct task_desc *task;
+ struct task_desc *task, **tasks_p;
static int pid_max;
+ /* perf.data is untrusted — cap pid to prevent overflow in size calculations */
+ if (pid >= PID_MAX_LIMIT) {
+ pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT);
+ return NULL;
+ }
+
if (sched->pid_to_task == NULL) {
if (sysctl__read_int("kernel/pid_max", &pid_max) < 0)
pid_max = MAX_PID;
- BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL);
+ sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *));
+ if (sched->pid_to_task == NULL)
+ return NULL;
}
if (pid >= (unsigned long)pid_max) {
- BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) *
- sizeof(struct task_desc *))) == NULL);
+ void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *));
+
+ if (p == NULL)
+ return NULL;
+ sched->pid_to_task = p;
while (pid >= (unsigned long)pid_max)
sched->pid_to_task[pid_max++] = NULL;
}
@@ -469,9 +481,11 @@ static struct task_desc *register_pid(struct perf_sched *sched,
return task;
task = zalloc(sizeof(*task));
+ if (task == NULL)
+ return NULL;
task->pid = pid;
- task->nr = sched->nr_tasks;
- strcpy(task->comm, comm);
+ if (comm)
+ strlcpy(task->comm, comm, sizeof(task->comm));
/*
* every task starts in sleeping state - this gets ignored
* if there's no wakeup pointing to this sleep state:
@@ -479,10 +493,12 @@ static struct task_desc *register_pid(struct perf_sched *sched,
add_sched_event_sleep(sched, task, 0, 0);
sched->pid_to_task[pid] = task;
- sched->nr_tasks++;
- sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *));
- BUG_ON(!sched->tasks);
- sched->tasks[task->nr] = task;
+ tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *));
+ if (!tasks_p)
+ return NULL;
+ sched->tasks = tasks_p;
+ sched->tasks[sched->nr_tasks] = task;
+ task->nr = sched->nr_tasks++;
if (verbose > 0)
printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm);
@@ -846,6 +862,8 @@ replay_wakeup_event(struct perf_sched *sched,
waker = register_pid(sched, sample->tid, "<unknown>");
wakee = register_pid(sched, pid, comm);
+ if (waker == NULL || wakee == NULL)
+ return -1;
add_sched_event_wakeup(sched, waker, sample->time, wakee);
return 0;
@@ -888,6 +906,8 @@ static int replay_switch_event(struct perf_sched *sched,
prev = register_pid(sched, prev_pid, prev_comm);
next = register_pid(sched, next_pid, next_comm);
+ if (prev == NULL || next == NULL)
+ return -1;
sched->cpu_last_switched[cpu] = timestamp;
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 1512fedd90cf..7ab980bf3c71 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -2596,7 +2596,8 @@ int cmd_stat(int argc, const char **argv)
output = fopen(output_name, mode);
if (!output) {
perror("failed to create output file");
- return -1;
+ status = -1;
+ goto out;
}
if (!stat_config.json_output) {
clock_gettime(CLOCK_REALTIME, &tm);
@@ -2607,7 +2608,8 @@ int cmd_stat(int argc, const char **argv)
output = fdopen(output_fd, mode);
if (!output) {
perror("Failed opening logfd");
- return -errno;
+ status = -errno;
+ goto out;
}
}
@@ -2616,7 +2618,8 @@ int cmd_stat(int argc, const char **argv)
parse_options_usage(stat_usage, stat_options, "o", 1);
parse_options_usage(NULL, stat_options, "log-fd", 0);
parse_options_usage(NULL, stat_options, "interval-clear", 0);
- return -1;
+ status = -1;
+ goto out;
}
stat_config.output = output;
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 72ba4a9239c6..40b2cd98abf8 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -503,13 +503,14 @@ const struct pmu_table_entry {_pending_events_tblname}[] = {{
def print_pending_metrics() -> None:
"""Optionally close metrics table."""
- def metric_cmp_key(j: JsonEvent) -> Tuple[bool, str, str]:
+ def metric_cmp_key(j: JsonEvent) -> Tuple[str, str, str, str]:
def fix_none(s: Optional[str]) -> str:
if s is None:
return ''
return s
- return (j.desc is not None, fix_none(j.pmu), fix_none(j.metric_name))
+ return (fix_none(j.pmu), fix_none(j.metric_name), j.metric_expr.ToPerfJson(),
+ fix_none(j.desc))
global _pending_metrics
if not _pending_metrics:
diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py
index 3e673f25d5fd..11cbc8dab09a 100644
--- a/tools/perf/pmu-events/metric.py
+++ b/tools/perf/pmu-events/metric.py
@@ -445,7 +445,11 @@ class Metric:
def __lt__(self, other):
"""Sort order."""
- return self.name < other.name
+ if self.name != other.name:
+ return self.name < other.name
+ if not self.expr.Equals(other.expr):
+ return self.expr.ToPerfJson() < other.expr.ToPerfJson()
+ return self.description < other.description
def AddToMetricGroup(self, group):
"""Callback used when being added to a MetricGroup."""
diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py
index 50a34a9cc040..5f32340b224d 100644
--- a/tools/perf/tests/shell/lib/perf_metric_validation.py
+++ b/tools/perf/tests/shell/lib/perf_metric_validation.py
@@ -1,4 +1,4 @@
-#SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0
import re
import csv
import json
@@ -6,36 +6,61 @@ import argparse
from pathlib import Path
import subprocess
+
+class TestError:
+ def __init__(self, metric: list[str], wl: str, value: list[float], low: float, up=float('nan'), description=str()):
+ self.metric: list = metric # multiple metrics in relationship type tests
+ self.workloads = [wl] # multiple workloads possible
+ self.collectedValue: list = value
+ self.valueLowBound = low
+ self.valueUpBound = up
+ self.description = description
+
+ def __repr__(self) -> str:
+ if len(self.metric) > 1:
+ return "\nMetric Relationship Error: \tThe collected value of metric {0}\n\
+ \tis {1} in workload(s): {2} \n\
+ \tbut expected value range is [{3}, {4}]\n\
+ \tRelationship rule description: \'{5}\'".format(self.metric, self.collectedValue, self.workloads,
+ self.valueLowBound, self.valueUpBound, self.description)
+ elif len(self.collectedValue) == 0:
+ return "\nNo Metric Value Error: \tMetric {0} returns with no value \n\
+ \tworkload(s): {1}".format(self.metric, self.workloads)
+ else:
+ return "\nWrong Metric Value Error: \tThe collected value of metric {0}\n\
+ \tis {1} in workload(s): {2}\n\
+ \tbut expected value range is [{3}, {4}]"\
+ .format(self.metric, self.collectedValue, self.workloads,
+ self.valueLowBound, self.valueUpBound)
+
+
class Validator:
def __init__(self, rulefname, reportfname='', t=5, debug=False, datafname='', fullrulefname='', workload='true', metrics=''):
self.rulefname = rulefname
self.reportfname = reportfname
self.rules = None
- self.collectlist:str = metrics
+ self.collectlist: str = metrics
self.metrics = self.__set_metrics(metrics)
self.skiplist = set()
self.tolerance = t
self.workloads = [x for x in workload.split(",") if x]
- self.wlidx = 0 # idx of current workloads
- self.allresults = dict() # metric results of all workload
- self.allignoremetrics = dict() # metrics with no results or negative results
- self.allfailtests = dict()
+ self.wlidx = 0 # idx of current workloads
+ self.allresults = dict() # metric results of all workload
self.alltotalcnt = dict()
self.allpassedcnt = dict()
- self.allerrlist = dict()
- self.results = dict() # metric results of current workload
+ self.results = dict() # metric results of current workload
# vars for test pass/failure statistics
- self.ignoremetrics= set() # metrics with no results or negative results, neg result counts as a failed test
- self.failtests = dict()
+ # metrics with no results or negative results, neg result counts failed tests
+ self.ignoremetrics = set()
self.totalcnt = 0
self.passedcnt = 0
# vars for errors
self.errlist = list()
# vars for Rule Generator
- self.pctgmetrics = set() # Percentage rule
+ self.pctgmetrics = set() # Percentage rule
# vars for debug
self.datafname = datafname
@@ -69,10 +94,10 @@ class Validator:
ensure_ascii=True,
indent=4)
- def get_results(self, idx:int = 0):
+ def get_results(self, idx: int = 0):
return self.results[idx]
- def get_bounds(self, lb, ub, error, alias={}, ridx:int = 0) -> list:
+ def get_bounds(self, lb, ub, error, alias={}, ridx: int = 0) -> list:
"""
Get bounds and tolerance from lb, ub, and error.
If missing lb, use 0.0; missing ub, use float('inf); missing error, use self.tolerance.
@@ -85,7 +110,7 @@ class Validator:
tolerance, denormalized base on upper bound value
"""
# init ubv and lbv to invalid values
- def get_bound_value (bound, initval, ridx):
+ def get_bound_value(bound, initval, ridx):
val = initval
if isinstance(bound, int) or isinstance(bound, float):
val = bound
@@ -113,10 +138,10 @@ class Validator:
return lbv, ubv, denormerr
- def get_value(self, name:str, ridx:int = 0) -> list:
+ def get_value(self, name: str, ridx: int = 0) -> list:
"""
Get value of the metric from self.results.
- If result of this metric is not provided, the metric name will be added into self.ignoremetics and self.errlist.
+ If result of this metric is not provided, the metric name will be added into self.ignoremetics.
All future test(s) on this metric will fail.
@param name: name of the metric
@@ -142,7 +167,7 @@ class Validator:
Check if metrics value are non-negative.
One metric is counted as one test.
Failure: when metric value is negative or not provided.
- Metrics with negative value will be added into the self.failtests['PositiveValueTest'] and self.ignoremetrics.
+ Metrics with negative value will be added into self.ignoremetrics.
"""
negmetric = dict()
pcnt = 0
@@ -155,25 +180,27 @@ class Validator:
else:
pcnt += 1
tcnt += 1
+ # The first round collect_perf() run these metrics with simple workload
+ # "true". We give metrics a second chance with a longer workload if less
+ # than 20 metrics failed positive test.
if len(rerun) > 0 and len(rerun) < 20:
second_results = dict()
self.second_test(rerun, second_results)
for name, val in second_results.items():
- if name not in negmetric: continue
+ if name not in negmetric:
+ continue
if val >= 0:
del negmetric[name]
pcnt += 1
- self.failtests['PositiveValueTest']['Total Tests'] = tcnt
- self.failtests['PositiveValueTest']['Passed Tests'] = pcnt
if len(negmetric.keys()):
self.ignoremetrics.update(negmetric.keys())
- negmessage = ["{0}(={1:.4f})".format(name, val) for name, val in negmetric.items()]
- self.failtests['PositiveValueTest']['Failed Tests'].append({'NegativeValue': negmessage})
+ self.errlist.extend(
+ [TestError([m], self.workloads[self.wlidx], negmetric[m], 0) for m in negmetric.keys()])
return
- def evaluate_formula(self, formula:str, alias:dict, ridx:int = 0):
+ def evaluate_formula(self, formula: str, alias: dict, ridx: int = 0):
"""
Evaluate the value of formula.
@@ -187,10 +214,11 @@ class Validator:
sign = "+"
f = str()
- #TODO: support parenthesis?
+ # TODO: support parenthesis?
for i in range(len(formula)):
if i+1 == len(formula) or formula[i] in ('+', '-', '*', '/'):
- s = alias[formula[b:i]] if i+1 < len(formula) else alias[formula[b:]]
+ s = alias[formula[b:i]] if i + \
+ 1 < len(formula) else alias[formula[b:]]
v = self.get_value(s, ridx)
if not v:
errs.append(s)
@@ -228,49 +256,49 @@ class Validator:
alias = dict()
for m in rule['Metrics']:
alias[m['Alias']] = m['Name']
- lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'], alias, ridx=rule['RuleIndex'])
- val, f = self.evaluate_formula(rule['Formula'], alias, ridx=rule['RuleIndex'])
+ lbv, ubv, t = self.get_bounds(
+ rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'], alias, ridx=rule['RuleIndex'])
+ val, f = self.evaluate_formula(
+ rule['Formula'], alias, ridx=rule['RuleIndex'])
+
+ lb = rule['RangeLower']
+ ub = rule['RangeUpper']
+ if isinstance(lb, str):
+ if lb in alias:
+ lb = alias[lb]
+ if isinstance(ub, str):
+ if ub in alias:
+ ub = alias[ub]
+
if val == -1:
- self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Description':f})
+ self.errlist.append(TestError([m['Name'] for m in rule['Metrics']], self.workloads[self.wlidx], [],
+ lb, ub, rule['Description']))
elif not self.check_bound(val, lbv, ubv, t):
- lb = rule['RangeLower']
- ub = rule['RangeUpper']
- if isinstance(lb, str):
- if lb in alias:
- lb = alias[lb]
- if isinstance(ub, str):
- if ub in alias:
- ub = alias[ub]
- self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Formula':f,
- 'RangeLower': lb, 'LowerBoundValue': self.get_value(lb),
- 'RangeUpper': ub, 'UpperBoundValue':self.get_value(ub),
- 'ErrorThreshold': t, 'CollectedValue': val})
+ self.errlist.append(TestError([m['Name'] for m in rule['Metrics']], self.workloads[self.wlidx], [val],
+ lb, ub, rule['Description']))
else:
self.passedcnt += 1
- self.failtests['RelationshipTest']['Passed Tests'] += 1
self.totalcnt += 1
- self.failtests['RelationshipTest']['Total Tests'] += 1
return
-
# Single Metric Test
- def single_test(self, rule:dict):
+ def single_test(self, rule: dict):
"""
Validate if the metrics are in the required value range.
eg. lower_bound <= metrics_value <= upper_bound
One metric is counted as one test in this type of test.
One rule may include one or more metrics.
Failure: when the metric value not provided or the value is outside the bounds.
- This test updates self.total_cnt and records failed tests in self.failtest['SingleMetricTest'].
+ This test updates self.total_cnt.
@param rule: dict with metrics to validate and the value range requirement
"""
- lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'])
+ lbv, ubv, t = self.get_bounds(
+ rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'])
metrics = rule['Metrics']
passcnt = 0
totalcnt = 0
- faillist = list()
failures = dict()
rerun = list()
for m in metrics:
@@ -286,25 +314,20 @@ class Validator:
second_results = dict()
self.second_test(rerun, second_results)
for name, val in second_results.items():
- if name not in failures: continue
+ if name not in failures:
+ continue
if self.check_bound(val, lbv, ubv, t):
passcnt += 1
del failures[name]
else:
- failures[name] = val
+ failures[name] = [val]
self.results[0][name] = val
self.totalcnt += totalcnt
self.passedcnt += passcnt
- self.failtests['SingleMetricTest']['Total Tests'] += totalcnt
- self.failtests['SingleMetricTest']['Passed Tests'] += passcnt
if len(failures.keys()) != 0:
- faillist = [{'MetricName':name, 'CollectedValue':val} for name, val in failures.items()]
- self.failtests['SingleMetricTest']['Failed Tests'].append({'RuleIndex':rule['RuleIndex'],
- 'RangeLower': rule['RangeLower'],
- 'RangeUpper': rule['RangeUpper'],
- 'ErrorThreshold':rule['ErrorThreshold'],
- 'Failure':faillist})
+ self.errlist.extend([TestError([name], self.workloads[self.wlidx], val,
+ rule['RangeLower'], rule['RangeUpper']) for name, val in failures.items()])
return
@@ -312,19 +335,11 @@ class Validator:
"""
Create final report and write into a JSON file.
"""
- alldata = list()
- for i in range(0, len(self.workloads)):
- reportstas = {"Total Rule Count": self.alltotalcnt[i], "Passed Rule Count": self.allpassedcnt[i]}
- data = {"Metric Validation Statistics": reportstas, "Tests in Category": self.allfailtests[i],
- "Errors":self.allerrlist[i]}
- alldata.append({"Workload": self.workloads[i], "Report": data})
-
- json_str = json.dumps(alldata, indent=4)
- print("Test validation finished. Final report: ")
- print(json_str)
+ print(self.errlist)
if self.debug:
- allres = [{"Workload": self.workloads[i], "Results": self.allresults[i]} for i in range(0, len(self.workloads))]
+ allres = [{"Workload": self.workloads[i], "Results": self.allresults[i]}
+ for i in range(0, len(self.workloads))]
self.json_dump(allres, self.datafname)
def check_rule(self, testtype, metric_list):
@@ -342,13 +357,13 @@ class Validator:
return True
# Start of Collector and Converter
- def convert(self, data: list, metricvalues:dict):
+ def convert(self, data: list, metricvalues: dict):
"""
Convert collected metric data from the -j output to dict of {metric_name:value}.
"""
for json_string in data:
try:
- result =json.loads(json_string)
+ result = json.loads(json_string)
if "metric-unit" in result and result["metric-unit"] != "(null)" and result["metric-unit"] != "":
name = result["metric-unit"].split(" ")[1] if len(result["metric-unit"].split(" ")) > 1 \
else result["metric-unit"]
@@ -363,11 +378,15 @@ class Validator:
wl = workload.split()
command.extend(wl)
print(" ".join(command))
- cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8')
- data = [x+'}' for x in cmd.stderr.split('}\n') if x]
+ cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
+ lines = cmd.stderr.splitlines() + cmd.stdout.splitlines()
+ data = []
+ for line in lines:
+ line = line.strip()
+ if line.startswith('{') and line.endswith('}'):
+ data.append(line)
return data
-
def collect_perf(self, workload: str):
"""
Collect metric data with "perf stat -M" on given workload with -a and -j.
@@ -385,14 +404,18 @@ class Validator:
if rule["TestType"] == "RelationshipTest":
metrics = [m["Name"] for m in rule["Metrics"]]
if not any(m not in collectlist[0] for m in metrics):
- collectlist[rule["RuleIndex"]] = [",".join(list(set(metrics)))]
+ collectlist[rule["RuleIndex"]] = [
+ ",".join(list(set(metrics)))]
for idx, metrics in collectlist.items():
- if idx == 0: wl = "true"
- else: wl = workload
+ if idx == 0:
+ wl = "true"
+ else:
+ wl = workload
for metric in metrics:
data = self._run_perf(metric, wl)
- if idx not in self.results: self.results[idx] = dict()
+ if idx not in self.results:
+ self.results[idx] = dict()
self.convert(data, self.results[idx])
return
@@ -412,7 +435,8 @@ class Validator:
2) create metric name list
"""
command = ['perf', 'list', '-j', '--details', 'metrics']
- cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
+ cmd = subprocess.run(command, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, encoding='utf-8')
try:
data = json.loads(cmd.stdout)
for m in data:
@@ -453,12 +477,12 @@ class Validator:
rules = data['RelationshipRules']
self.skiplist = set([name.lower() for name in data['SkipList']])
self.rules = self.remove_unsupported_rules(rules)
- pctgrule = {'RuleIndex':0,
- 'TestType':'SingleMetricTest',
- 'RangeLower':'0',
+ pctgrule = {'RuleIndex': 0,
+ 'TestType': 'SingleMetricTest',
+ 'RangeLower': '0',
'RangeUpper': '100',
'ErrorThreshold': self.tolerance,
- 'Description':'Metrics in percent unit have value with in [0, 100]',
+ 'Description': 'Metrics in percent unit have value with in [0, 100]',
'Metrics': [{'Name': m.lower()} for m in self.pctgmetrics]}
self.rules.append(pctgrule)
@@ -469,8 +493,9 @@ class Validator:
idx += 1
if self.debug:
- #TODO: need to test and generate file name correctly
- data = {'RelationshipRules':self.rules, 'SupportedMetrics': [{"MetricName": name} for name in self.metrics]}
+ # TODO: need to test and generate file name correctly
+ data = {'RelationshipRules': self.rules, 'SupportedMetrics': [
+ {"MetricName": name} for name in self.metrics]}
self.json_dump(data, self.fullrulefname)
return
@@ -482,20 +507,17 @@ class Validator:
@param key: key to the dictionaries (index of self.workloads).
'''
self.allresults[key] = self.results
- self.allignoremetrics[key] = self.ignoremetrics
- self.allfailtests[key] = self.failtests
self.alltotalcnt[key] = self.totalcnt
self.allpassedcnt[key] = self.passedcnt
- self.allerrlist[key] = self.errlist
- #Initialize data structures before data validation of each workload
+ # Initialize data structures before data validation of each workload
def _init_data(self):
- testtypes = ['PositiveValueTest', 'RelationshipTest', 'SingleMetricTest']
+ testtypes = ['PositiveValueTest',
+ 'RelationshipTest', 'SingleMetricTest']
self.results = dict()
- self.ignoremetrics= set()
+ self.ignoremetrics = set()
self.errlist = list()
- self.failtests = {k:{'Total Tests':0, 'Passed Tests':0, 'Failed Tests':[]} for k in testtypes}
self.totalcnt = 0
self.passedcnt = 0
@@ -525,32 +547,33 @@ class Validator:
testtype = r['TestType']
if not self.check_rule(testtype, r['Metrics']):
continue
- if testtype == 'RelationshipTest':
+ if testtype == 'RelationshipTest':
self.relationship_test(r)
elif testtype == 'SingleMetricTest':
self.single_test(r)
else:
print("Unsupported Test Type: ", testtype)
- self.errlist.append("Unsupported Test Type from rule: " + r['RuleIndex'])
- self._storewldata(i)
print("Workload: ", self.workloads[i])
- print("Total metrics collected: ", self.failtests['PositiveValueTest']['Total Tests'])
- print("Non-negative metric count: ", self.failtests['PositiveValueTest']['Passed Tests'])
print("Total Test Count: ", self.totalcnt)
print("Passed Test Count: ", self.passedcnt)
-
+ self._storewldata(i)
self.create_report()
- return sum(self.alltotalcnt.values()) != sum(self.allpassedcnt.values())
+ return len(self.errlist) > 0
# End of Class Validator
def main() -> None:
- parser = argparse.ArgumentParser(description="Launch metric value validation")
-
- parser.add_argument("-rule", help="Base validation rule file", required=True)
- parser.add_argument("-output_dir", help="Path for validator output file, report file", required=True)
- parser.add_argument("-debug", help="Debug run, save intermediate data to files", action="store_true", default=False)
- parser.add_argument("-wl", help="Workload to run while data collection", default="true")
+ parser = argparse.ArgumentParser(
+ description="Launch metric value validation")
+
+ parser.add_argument(
+ "-rule", help="Base validation rule file", required=True)
+ parser.add_argument(
+ "-output_dir", help="Path for validator output file, report file", required=True)
+ parser.add_argument("-debug", help="Debug run, save intermediate data to files",
+ action="store_true", default=False)
+ parser.add_argument(
+ "-wl", help="Workload to run while data collection", default="true")
parser.add_argument("-m", help="Metric list to validate", default="")
args = parser.parse_args()
outpath = Path(args.output_dir)
@@ -559,8 +582,8 @@ def main() -> None:
datafile = Path.joinpath(outpath, 'perf_data.json')
validator = Validator(args.rule, reportf, debug=args.debug,
- datafname=datafile, fullrulefname=fullrule, workload=args.wl,
- metrics=args.m)
+ datafname=datafile, fullrulefname=fullrule, workload=args.wl,
+ metrics=args.m)
ret = validator.test()
return ret
@@ -569,6 +592,3 @@ def main() -> None:
if __name__ == "__main__":
import sys
sys.exit(main())
-
-
-
diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh
index 54774525e18a..feeb34c6fa6d 100755
--- a/tools/perf/tests/shell/stat_all_metrics.sh
+++ b/tools/perf/tests/shell/stat_all_metrics.sh
@@ -2,42 +2,147 @@
# perf all metrics test
# SPDX-License-Identifier: GPL-2.0
-err=0
+ParanoidAndNotRoot()
+{
+ [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ]
+}
+
+test_prog="sleep 0.01"
+system_wide_flag="-a"
+if ParanoidAndNotRoot 0
+then
+ system_wide_flag=""
+ test_prog="perf test -w noploop 0.01"
+fi
+
+check_metric() {
+ local output="$1"
+ local status="$2"
+ local metric="$3"
+
+ if [[ $status -ne 0 || ! "$output" =~ ${metric:0:50} ]]; then
+ return 1
+ fi
+
+ if [[ "$output" =~ "<not counted>" || "$output" =~ "<not supported>" ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+skip=0
+err=3
for m in $(perf list --raw-dump metrics); do
echo "Testing $m"
- result=$(perf stat -M "$m" true 2>&1)
- if [[ "$result" =~ ${m:0:50} ]] || [[ "$result" =~ "<not supported>" ]]
- then
+ result=$(perf stat -M "$m" $system_wide_flag -- $test_prog 2>&1)
+ result_err=$?
+
+ if check_metric "$result" $result_err "$m"; then
+ if [[ "$err" -ne 1 ]]
+ then
+ err=0
+ fi
continue
fi
- # Failed so try system wide.
- result=$(perf stat -M "$m" -a sleep 0.01 2>&1)
- if [[ "$result" =~ ${m:0:50} ]]
+
+ if [[ "$result" =~ "Access to performance monitoring and observability operations is limited" || \
+ "$result" =~ "in per-thread mode, enable system wide" || \
+ "$result" =~ "<not supported>" || \
+ "$result" =~ "Cannot resolve IDs for" || \
+ "$result" =~ "No supported events found" || \
+ "$result" =~ "FP_ARITH" || \
+ "$result" =~ "AMX" || \
+ "$result" =~ "PMM" ]]
then
- continue
+ true
+ else
+ result=$(perf stat -M "$m" $system_wide_flag -- perf test -w noploop 0.1 2>&1)
+ result_err=$?
+
+ if check_metric "$result" $result_err "$m"; then
+ if [[ "$err" -ne 1 ]]
+ then
+ err=0
+ fi
+ continue
+ fi
fi
- # Failed again, possibly the workload was too small so retry with something
- # longer.
- result=$(perf stat -M "$m" perf bench internals synthesize 2>&1)
- if [[ "$result" =~ ${m:0:50} ]]
+
+ # If retry also failed, determine if we skip, ignore, or fail
+ if [[ "$result" =~ "Access to performance monitoring and observability operations is limited" ]]
then
+ echo "[Skipped $m] Permission failure"
+ echo $result
+ if [[ $err -eq 0 ]]
+ then
+ skip=1
+ fi
continue
- fi
- echo "Metric '$m' not printed in:"
- echo "$result"
- if [[ "$err" != "1" ]]
+ elif [[ "$result" =~ "in per-thread mode, enable system wide" ]]
then
- err=2
- if [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]]
+ echo "[Skipped $m] Permissions - need system wide mode"
+ echo $result
+ if [[ $err -eq 0 ]]
then
- echo "Skip, not fail, for FP issues"
- elif [[ "$result" =~ "PMM" ]]
+ skip=1
+ fi
+ continue
+ elif [[ "$result" =~ "<not supported>" || \
+ "$result" =~ "Cannot resolve IDs for" || \
+ "$result" =~ "No supported events found" ]]
+ then
+ if [[ $(perf list --raw-dump $m) == "Default"* ]]
then
- echo "Skip, not fail, for Optane memory issues"
- else
- err=1
+ echo "[Ignored $m] failed but as a Default metric this can be expected"
+ echo $result
+ continue
fi
+ echo "[Skipped $m] Not supported events"
+ echo $result
+ if [[ $err -eq 0 ]]
+ then
+ skip=1
+ fi
+ continue
+ elif [[ "$result" =~ "<not counted>" ]]
+ then
+ echo "[Skipped $m] Not counted events"
+ echo $result
+ if [[ $err -eq 0 ]]
+ then
+ skip=1
+ fi
+ continue
+ elif [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]]
+ then
+ echo "[Skipped $m] FP issues"
+ echo $result
+ if [[ $err -eq 0 ]]
+ then
+ skip=1
+ fi
+ continue
+ elif [[ "$result" =~ "PMM" ]]
+ then
+ echo "[Skipped $m] Optane memory issues"
+ echo $result
+ if [[ $err -eq 0 ]]
+ then
+ skip=1
+ fi
+ continue
fi
+
+ echo "[Failed $m] has non-zero error '$result_err' or not printed/counted in:"
+ echo "$result"
+ err=1
done
+# return SKIP only if no success returned
+if [[ "$err" -eq 3 && "$skip" -eq 1 ]]
+then
+ err=2
+fi
+
exit "$err"
diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh
index a87bb2814b4c..76d4a7f15a43 100755
--- a/tools/perf/tests/shell/stat_bpf_counters.sh
+++ b/tools/perf/tests/shell/stat_bpf_counters.sh
@@ -4,42 +4,91 @@
set -e
-# check whether $2 is within +/- 10% of $1
+# Get the first allowed CPU
+CPU=$(taskset -c -p $$ | awk -F': ' '{print $2}' | awk -F'[,-]' '{print $1}')
+if [ -z "$CPU" ]; then
+ CPU=0
+fi
+workload=(taskset -c "$CPU" awk 'BEGIN { for (i=0; i<10000000; i++) sum+=i }')
+
+# check whether $2 is within +/- 15% of $1
compare_number()
{
- first_num=$1
- second_num=$2
-
- # upper bound is first_num * 110%
- upper=$(expr $first_num + $first_num / 10 )
- # lower bound is first_num * 90%
- lower=$(expr $first_num - $first_num / 10 )
-
- if [ $second_num -gt $upper ] || [ $second_num -lt $lower ]; then
- echo "The difference between $first_num and $second_num are greater than 10%."
- exit 1
- fi
+ first_num=$1
+ second_num=$2
+
+ # upper bound is first_num * 115%
+ upper=$(expr $first_num + $first_num / 20 \* 3 )
+ # lower bound is first_num * 85%
+ lower=$(expr $first_num - $first_num / 20 \* 3 )
+
+ if [ $second_num -gt $upper ] || [ $second_num -lt $lower ]; then
+ echo "The difference between $first_num and $second_num are greater than 15%."
+ exit 1
+ fi
+}
+
+check_counts()
+{
+ base_instructions=$1
+ bpf_instructions=$2
+
+ if [ "$base_instructions" = "<not" ]; then
+ echo "Skipping: instructions event not counted"
+ exit 2
+ fi
+ if [ "$bpf_instructions" = "<not" ]; then
+ echo "Failed: instructions not counted with --bpf-counters"
+ exit 1
+ fi
+}
+
+test_bpf_counters()
+{
+ printf "Testing --bpf-counters "
+ base_instructions=$(perf stat --no-big-num -e instructions:u -- "${workload[@]}" 2>&1 | \
+ awk -v i=0 -v c=0 '/instructions/ { \
+ if ($1 != "<not") { i++; c += $1 } \
+ } END { if (i > 0) printf "%.0f", c; else print "<not" }')
+ bpf_instructions=$(perf stat --no-big-num --bpf-counters -e instructions:u \
+ -- "${workload[@]}" 2>&1 | \
+ awk -v i=0 -v c=0 '/instructions/ { \
+ if ($1 != "<not") { i++; c += $1 } \
+ } END { if (i > 0) printf "%.0f", c; else print "<not" }')
+ check_counts $base_instructions $bpf_instructions
+ compare_number $base_instructions $bpf_instructions
+ echo "[Success]"
+}
+
+test_bpf_modifier()
+{
+ printf "Testing bpf event modifier "
+ stat_output=$(perf stat --no-big-num \
+ -e instructions/name=base_instructions/u,instructions/name=bpf_instructions/bu \
+ -- "${workload[@]}" 2>&1)
+ base_instructions=$(echo "$stat_output"| \
+ awk -v i=0 -v c=0 '/base_instructions/ { \
+ if ($1 != "<not") { i++; c += $1 } \
+ } END { if (i > 0) printf "%.0f", c; else print "<not" }')
+ bpf_instructions=$(echo "$stat_output"| \
+ awk -v i=0 -v c=0 '/bpf_instructions/ { \
+ if ($1 != "<not") { i++; c += $1 } \
+ } END { if (i > 0) printf "%.0f", c; else print "<not" }')
+ check_counts $base_instructions $bpf_instructions
+ compare_number $base_instructions $bpf_instructions
+ echo "[Success]"
}
# skip if --bpf-counters is not supported
-if ! perf stat -e cycles --bpf-counters true > /dev/null 2>&1; then
+if ! perf stat -e instructions --bpf-counters true > /dev/null 2>&1; then
if [ "$1" = "-v" ]; then
echo "Skipping: --bpf-counters not supported"
- perf --no-pager stat -e cycles --bpf-counters true || true
+ perf --no-pager stat -e instructions --bpf-counters true || true
fi
exit 2
fi
-base_cycles=$(perf stat --no-big-num -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}')
-if [ "$base_cycles" = "<not" ]; then
- echo "Skipping: cycles event not counted"
- exit 2
-fi
-bpf_cycles=$(perf stat --no-big-num --bpf-counters -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}')
-if [ "$bpf_cycles" = "<not" ]; then
- echo "Failed: cycles not counted with --bpf-counters"
- exit 1
-fi
+test_bpf_counters
+test_bpf_modifier
-compare_number $base_cycles $bpf_cycles
exit 0
diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tests/shell/stat_metrics_values.sh
index ad94c936de7e..f3c77b1dd826 100755
--- a/tools/perf/tests/shell/stat_metrics_values.sh
+++ b/tools/perf/tests/shell/stat_metrics_values.sh
@@ -14,6 +14,13 @@ fi
grep -q GenuineIntel /proc/cpuinfo || { echo Skipping non-Intel; exit 2; }
+# Skip if no permission to record system-wide events
+if ! perf stat -a -e instructions sleep 0.01 >/dev/null 2>&1; then
+ echo "Skipping: no permission to record system-wide events (-a)"
+ exit 2
+fi
+
+
pythonvalidator=$(dirname $0)/lib/perf_metric_validation.py
rulefile=$(dirname $0)/lib/perf_metric_validation_rules.json
tmpdir=$(mktemp -d /tmp/__perf_test.program.XXXXX)
@@ -25,6 +32,8 @@ echo "Output will be stored in: $tmpdir"
$PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workload}"
ret=$?
rm -rf $tmpdir
-
+if [ $ret -ne 0 ]; then
+ echo "Metric validation return with erros. Please check metrics reported with errors."
+fi
exit $ret
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index a53a0257a4bc..cf61fc75c151 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -3005,15 +3005,15 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
browser->min_pcnt = min_pcnt;
hist_browser__update_nr_entries(browser);
+ memset(options, 0, sizeof(options));
+ memset(actions, 0, sizeof(actions));
+
browser->pstack = pstack__new(3);
if (browser->pstack == NULL)
goto out;
ui_helpline__push(helpline);
- memset(options, 0, sizeof(options));
- memset(actions, 0, sizeof(actions));
-
if (symbol_conf.col_width_list_str)
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index c51829fdef23..934dd403588c 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -232,8 +232,12 @@ static int auxtrace_queues__grow(struct auxtrace_queues *queues,
{
unsigned int nr_queues = queues->nr_queues;
struct auxtrace_queue *queue_array;
+ struct auxtrace_queue *old_array = queues->queue_array;
unsigned int i;
+ if (!new_nr_queues)
+ return -EINVAL;
+
if (!nr_queues)
nr_queues = AUXTRACE_INIT_NR_QUEUES;
@@ -248,16 +252,17 @@ static int auxtrace_queues__grow(struct auxtrace_queues *queues,
return -ENOMEM;
for (i = 0; i < queues->nr_queues; i++) {
- list_splice_tail(&queues->queue_array[i].head,
+ list_splice_tail(&old_array[i].head,
&queue_array[i].head);
- queue_array[i].tid = queues->queue_array[i].tid;
- queue_array[i].cpu = queues->queue_array[i].cpu;
- queue_array[i].set = queues->queue_array[i].set;
- queue_array[i].priv = queues->queue_array[i].priv;
+ queue_array[i].tid = old_array[i].tid;
+ queue_array[i].cpu = old_array[i].cpu;
+ queue_array[i].set = old_array[i].set;
+ queue_array[i].priv = old_array[i].priv;
}
queues->nr_queues = nr_queues;
queues->queue_array = queue_array;
+ free(old_array);
return 0;
}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
index 640e383cef44..277da9f767b9 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -398,6 +398,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_packet_queue *packet_queue,
packet_queue->packet_buffer[et].flags = 0;
packet_queue->packet_buffer[et].exception_number = UINT32_MAX;
packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id;
+ packet_queue->packet_buffer[et].el = ocsd_EL_unknown;
+ packet_queue->packet_buffer[et].tid = -1;
if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1)
return OCSD_RESP_WAIT;
@@ -445,6 +447,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq,
packet->last_instr_type = elem->last_i_type;
packet->last_instr_subtype = elem->last_i_subtype;
packet->last_instr_cond = elem->last_instr_cond;
+ packet->el = elem->context.exception_level;
if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT)
packet->last_instr_taken_branch = elem->last_instr_exec;
@@ -518,7 +521,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
const ocsd_generic_trace_elem *elem,
const uint8_t trace_chan_id)
{
+ struct cs_etm_packet *packet;
pid_t tid = -1;
+ int ret;
/*
* Process the PE_CONTEXT packets if we have a valid contextID or VMID.
@@ -539,12 +544,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
break;
}
- if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id,
- elem->context.exception_level))
+ if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id,
+ elem->context.exception_level, tid))
return OCSD_RESP_FATAL_SYS_ERR;
- if (tid == -1)
- return OCSD_RESP_CONT;
+ ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id,
+ CS_ETM_CONTEXT);
+ if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
+ return ret;
+
+ packet = &packet_queue->packet_buffer[packet_queue->tail];
+ packet->tid = tid;
+ packet->el = elem->context.exception_level;
/*
* A timestamp is generated after a PE_CONTEXT element so make sure
@@ -552,7 +563,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
*/
cs_etm_decoder__reset_timestamp(packet_queue);
- return OCSD_RESP_CONT;
+ return ret;
}
static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 799c104901b4..7e4e1195e168 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -86,15 +86,28 @@ struct cs_etm_traceid_queue {
u64 period_instructions;
size_t last_branch_pos;
union perf_event *event_buf;
- struct thread *thread;
- struct thread *prev_packet_thread;
- ocsd_ex_level prev_packet_el;
- ocsd_ex_level el;
struct branch_stack *last_branch;
struct branch_stack *last_branch_rb;
struct cs_etm_packet *prev_packet;
struct cs_etm_packet *packet;
struct cs_etm_packet_queue packet_queue;
+
+ struct thread *decode_thread;
+ ocsd_ex_level decode_el;
+
+ /*
+ * The frontend accesses the EL from '[prev_]packet' because it needs
+ * previous EL for branch and current EL for instruction samples. It's
+ * not possible to change thread in a single branch sample so no need to
+ * store or access the thread through the packet.
+ */
+ struct thread *frontend_thread;
+};
+
+enum cs_etm_format {
+ UNSET,
+ FORMATTED,
+ UNFORMATTED
};
struct cs_etm_queue {
@@ -103,6 +116,7 @@ struct cs_etm_queue {
struct auxtrace_buffer *buffer;
unsigned int queue_nr;
u8 pending_timestamp_chan_id;
+ enum cs_etm_format format;
u64 offset;
const unsigned char *buf;
size_t buf_len, buf_used;
@@ -283,22 +297,31 @@ static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata)
}
/*
- * Get a metadata for a specific cpu from an array.
+ * Get a metadata index for a specific cpu from an array.
*
*/
-static u64 *get_cpu_data(struct cs_etm_auxtrace *etm, int cpu)
+static int get_cpu_data_idx(struct cs_etm_auxtrace *etm, int cpu)
{
int i;
- u64 *metadata = NULL;
for (i = 0; i < etm->num_cpu; i++) {
if (etm->metadata[i][CS_ETM_CPU] == (u64)cpu) {
- metadata = etm->metadata[i];
- break;
+ return i;
}
}
- return metadata;
+ return -1;
+}
+
+/*
+ * Get a metadata for a specific cpu from an array.
+ *
+ */
+static u64 *get_cpu_data(struct cs_etm_auxtrace *etm, int cpu)
+{
+ int idx = get_cpu_data_idx(etm, cpu);
+
+ return (idx != -1) ? etm->metadata[idx] : NULL;
}
/*
@@ -473,10 +496,13 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
tidq->trace_chan_id = trace_chan_id;
- tidq->el = tidq->prev_packet_el = ocsd_EL_unknown;
- tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1,
+ tidq->decode_el = ocsd_EL_unknown;
+ tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1,
+ queue->tid);
+ tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1,
queue->tid);
- tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host);
+ if (!tidq->frontend_thread || !tidq->decode_thread)
+ goto out;
tidq->packet = zalloc(sizeof(struct cs_etm_packet));
if (!tidq->packet)
@@ -511,6 +537,8 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
zfree(&tidq->prev_packet);
zfree(&tidq->packet);
out:
+ thread__zput(tidq->frontend_thread);
+ thread__zput(tidq->decode_thread);
return rc;
}
@@ -609,21 +637,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm,
/*
* Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
* the next incoming packet.
- *
- * Threads and exception levels are also tracked for both the
- * previous and current packets. This is because the previous
- * packet is used for the 'from' IP for branch samples, so the
- * thread at that time must also be assigned to that sample.
- * Across discontinuity packets the thread can change, so by
- * tracking the thread for the previous packet the branch sample
- * will have the correct info.
*/
tmp = tidq->packet;
tidq->packet = tidq->prev_packet;
tidq->prev_packet = tmp;
- tidq->prev_packet_el = tidq->el;
- thread__put(tidq->prev_packet_thread);
- tidq->prev_packet_thread = thread__get(tidq->thread);
}
}
@@ -641,66 +658,80 @@ static void cs_etm__packet_dump(const char *pkt_string)
}
static void cs_etm__set_trace_param_etmv3(struct cs_etm_trace_params *t_params,
- struct cs_etm_auxtrace *etm, int idx,
- u32 etmidr)
+ struct cs_etm_auxtrace *etm, int t_idx,
+ int m_idx, u32 etmidr)
{
u64 **metadata = etm->metadata;
- t_params[idx].protocol = cs_etm__get_v7_protocol_version(etmidr);
- t_params[idx].etmv3.reg_ctrl = metadata[idx][CS_ETM_ETMCR];
- t_params[idx].etmv3.reg_trc_id = metadata[idx][CS_ETM_ETMTRACEIDR];
+ t_params[t_idx].protocol = cs_etm__get_v7_protocol_version(etmidr);
+ t_params[t_idx].etmv3.reg_ctrl = metadata[m_idx][CS_ETM_ETMCR];
+ t_params[t_idx].etmv3.reg_trc_id = metadata[m_idx][CS_ETM_ETMTRACEIDR];
}
static void cs_etm__set_trace_param_etmv4(struct cs_etm_trace_params *t_params,
- struct cs_etm_auxtrace *etm, int idx)
+ struct cs_etm_auxtrace *etm, int t_idx,
+ int m_idx)
{
u64 **metadata = etm->metadata;
- t_params[idx].protocol = CS_ETM_PROTO_ETMV4i;
- t_params[idx].etmv4.reg_idr0 = metadata[idx][CS_ETMV4_TRCIDR0];
- t_params[idx].etmv4.reg_idr1 = metadata[idx][CS_ETMV4_TRCIDR1];
- t_params[idx].etmv4.reg_idr2 = metadata[idx][CS_ETMV4_TRCIDR2];
- t_params[idx].etmv4.reg_idr8 = metadata[idx][CS_ETMV4_TRCIDR8];
- t_params[idx].etmv4.reg_configr = metadata[idx][CS_ETMV4_TRCCONFIGR];
- t_params[idx].etmv4.reg_traceidr = metadata[idx][CS_ETMV4_TRCTRACEIDR];
+ t_params[t_idx].protocol = CS_ETM_PROTO_ETMV4i;
+ t_params[t_idx].etmv4.reg_idr0 = metadata[m_idx][CS_ETMV4_TRCIDR0];
+ t_params[t_idx].etmv4.reg_idr1 = metadata[m_idx][CS_ETMV4_TRCIDR1];
+ t_params[t_idx].etmv4.reg_idr2 = metadata[m_idx][CS_ETMV4_TRCIDR2];
+ t_params[t_idx].etmv4.reg_idr8 = metadata[m_idx][CS_ETMV4_TRCIDR8];
+ t_params[t_idx].etmv4.reg_configr = metadata[m_idx][CS_ETMV4_TRCCONFIGR];
+ t_params[t_idx].etmv4.reg_traceidr = metadata[m_idx][CS_ETMV4_TRCTRACEIDR];
}
static void cs_etm__set_trace_param_ete(struct cs_etm_trace_params *t_params,
- struct cs_etm_auxtrace *etm, int idx)
+ struct cs_etm_auxtrace *etm, int t_idx,
+ int m_idx)
{
u64 **metadata = etm->metadata;
- t_params[idx].protocol = CS_ETM_PROTO_ETE;
- t_params[idx].ete.reg_idr0 = metadata[idx][CS_ETE_TRCIDR0];
- t_params[idx].ete.reg_idr1 = metadata[idx][CS_ETE_TRCIDR1];
- t_params[idx].ete.reg_idr2 = metadata[idx][CS_ETE_TRCIDR2];
- t_params[idx].ete.reg_idr8 = metadata[idx][CS_ETE_TRCIDR8];
- t_params[idx].ete.reg_configr = metadata[idx][CS_ETE_TRCCONFIGR];
- t_params[idx].ete.reg_traceidr = metadata[idx][CS_ETE_TRCTRACEIDR];
- t_params[idx].ete.reg_devarch = metadata[idx][CS_ETE_TRCDEVARCH];
+ t_params[t_idx].protocol = CS_ETM_PROTO_ETE;
+ t_params[t_idx].ete.reg_idr0 = metadata[m_idx][CS_ETE_TRCIDR0];
+ t_params[t_idx].ete.reg_idr1 = metadata[m_idx][CS_ETE_TRCIDR1];
+ t_params[t_idx].ete.reg_idr2 = metadata[m_idx][CS_ETE_TRCIDR2];
+ t_params[t_idx].ete.reg_idr8 = metadata[m_idx][CS_ETE_TRCIDR8];
+ t_params[t_idx].ete.reg_configr = metadata[m_idx][CS_ETE_TRCCONFIGR];
+ t_params[t_idx].ete.reg_traceidr = metadata[m_idx][CS_ETE_TRCTRACEIDR];
+ t_params[t_idx].ete.reg_devarch = metadata[m_idx][CS_ETE_TRCDEVARCH];
}
static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params,
struct cs_etm_auxtrace *etm,
+ enum cs_etm_format format,
+ int sample_cpu,
int decoders)
{
- int i;
+ int t_idx, m_idx;
u32 etmidr;
u64 architecture;
- for (i = 0; i < decoders; i++) {
- architecture = etm->metadata[i][CS_ETM_MAGIC];
+ for (t_idx = 0; t_idx < decoders; t_idx++) {
+ if (format == FORMATTED)
+ m_idx = t_idx;
+ else {
+ m_idx = get_cpu_data_idx(etm, sample_cpu);
+ if (m_idx == -1) {
+ pr_warning("CS_ETM: unknown CPU, falling back to first metadata\n");
+ m_idx = 0;
+ }
+ }
+
+ architecture = etm->metadata[m_idx][CS_ETM_MAGIC];
switch (architecture) {
case __perf_cs_etmv3_magic:
- etmidr = etm->metadata[i][CS_ETM_ETMIDR];
- cs_etm__set_trace_param_etmv3(t_params, etm, i, etmidr);
+ etmidr = etm->metadata[m_idx][CS_ETM_ETMIDR];
+ cs_etm__set_trace_param_etmv3(t_params, etm, t_idx, m_idx, etmidr);
break;
case __perf_cs_etmv4_magic:
- cs_etm__set_trace_param_etmv4(t_params, etm, i);
+ cs_etm__set_trace_param_etmv4(t_params, etm, t_idx, m_idx);
break;
case __perf_cs_ete_magic:
- cs_etm__set_trace_param_ete(t_params, etm, i);
+ cs_etm__set_trace_param_ete(t_params, etm, t_idx, m_idx);
break;
default:
return -EINVAL;
@@ -712,8 +743,7 @@ static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params,
static int cs_etm__init_decoder_params(struct cs_etm_decoder_params *d_params,
struct cs_etm_queue *etmq,
- enum cs_etm_decoder_operation mode,
- bool formatted)
+ enum cs_etm_decoder_operation mode)
{
int ret = -EINVAL;
@@ -723,7 +753,7 @@ static int cs_etm__init_decoder_params(struct cs_etm_decoder_params *d_params,
d_params->packet_printer = cs_etm__packet_dump;
d_params->operation = mode;
d_params->data = etmq;
- d_params->formatted = formatted;
+ d_params->formatted = etmq->format == FORMATTED;
d_params->fsyncs = false;
d_params->hsyncs = false;
d_params->frame_aligned = true;
@@ -798,8 +828,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
/* Free this traceid_queue from the array */
tidq = etmq->traceid_queues[idx];
- thread__zput(tidq->thread);
- thread__zput(tidq->prev_packet_thread);
+ thread__zput(tidq->frontend_thread);
+ thread__zput(tidq->decode_thread);
zfree(&tidq->event_buf);
zfree(&tidq->last_branch);
zfree(&tidq->last_branch_rb);
@@ -940,47 +970,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address,
}
}
-static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
- u64 address, size_t size, u8 *buffer,
- const ocsd_mem_space_acc_t mem_space)
+static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq,
+ u64 address, size_t size, u8 *buffer,
+ const ocsd_mem_space_acc_t mem_space,
+ ocsd_ex_level el, struct thread *thread)
{
u8 cpumode;
u64 offset;
int len;
struct addr_location al;
struct dso *dso;
- struct cs_etm_traceid_queue *tidq;
int ret = 0;
if (!etmq)
return 0;
addr_location__init(&al);
- tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
- if (!tidq)
- goto out;
/*
- * We've already tracked EL along side the PID in cs_etm__set_thread()
- * so double check that it matches what OpenCSD thinks as well. It
- * doesn't distinguish between EL0 and EL1 for this mem access callback
- * so we had to do the extra tracking. Skip validation if it's any of
- * the 'any' values.
+ * We track EL for the frontend and the backend when receiving context
+ * and range packets. OpenCSD doesn't distinguish between EL0 and EL1
+ * for this mem access callback so we had to do the extra tracking. Skip
+ * validation if it's any of the 'any' values.
*/
if (!(mem_space == OCSD_MEM_SPACE_ANY ||
mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) {
if (mem_space & OCSD_MEM_SPACE_EL1N) {
/* Includes both non secure EL1 and EL0 */
- assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0);
+ assert(el == ocsd_EL1 || el == ocsd_EL0);
} else if (mem_space & OCSD_MEM_SPACE_EL2)
- assert(tidq->el == ocsd_EL2);
+ assert(el == ocsd_EL2);
else if (mem_space & OCSD_MEM_SPACE_EL3)
- assert(tidq->el == ocsd_EL3);
+ assert(el == ocsd_EL3);
}
- cpumode = cs_etm__cpu_mode(etmq, address, tidq->el);
+ cpumode = cs_etm__cpu_mode(etmq, address, el);
- if (!thread__find_map(tidq->thread, cpumode, address, &al))
+ if (!thread__find_map(thread, cpumode, address, &al))
goto out;
dso = map__dso(al.map);
@@ -995,7 +1021,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
map__load(al.map);
- len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)),
+ len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)),
offset, buffer, size);
if (len <= 0) {
@@ -1015,80 +1041,58 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
return ret;
}
-static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm,
- bool formatted)
+static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet,
+ u64 address, size_t size, u8 *buffer)
{
- struct cs_etm_decoder_params d_params;
- struct cs_etm_trace_params *t_params = NULL;
- struct cs_etm_queue *etmq;
- /*
- * Each queue can only contain data from one CPU when unformatted, so only one decoder is
- * needed.
- */
- int decoders = formatted ? etm->num_cpu : 1;
+ return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el,
+ tidq->frontend_thread);
+}
- etmq = zalloc(sizeof(*etmq));
+static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
+ u64 address, size_t size, u8 *buffer,
+ const ocsd_mem_space_acc_t mem_space)
+{
+ struct cs_etm_traceid_queue *tidq;
+
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+ if (!tidq)
+ return 0;
+
+ return __cs_etm__mem_access(etmq, address, size, buffer,
+ mem_space, tidq->decode_el,
+ tidq->decode_thread);
+}
+
+static struct cs_etm_queue *cs_etm__alloc_queue(void)
+{
+ struct cs_etm_queue *etmq = zalloc(sizeof(*etmq));
if (!etmq)
return NULL;
etmq->traceid_queues_list = intlist__new(NULL);
if (!etmq->traceid_queues_list)
- goto out_free;
-
- /* Use metadata to fill in trace parameters for trace decoder */
- t_params = zalloc(sizeof(*t_params) * decoders);
-
- if (!t_params)
- goto out_free;
-
- if (cs_etm__init_trace_params(t_params, etm, decoders))
- goto out_free;
-
- /* Set decoder parameters to decode trace packets */
- if (cs_etm__init_decoder_params(&d_params, etmq,
- dump_trace ? CS_ETM_OPERATION_PRINT :
- CS_ETM_OPERATION_DECODE,
- formatted))
- goto out_free;
-
- etmq->decoder = cs_etm_decoder__new(decoders, &d_params,
- t_params);
-
- if (!etmq->decoder)
- goto out_free;
+ free(etmq);
- /*
- * Register a function to handle all memory accesses required by
- * the trace decoder library.
- */
- if (cs_etm_decoder__add_mem_access_cb(etmq->decoder,
- 0x0L, ((u64) -1L),
- cs_etm__mem_access))
- goto out_free_decoder;
-
- zfree(&t_params);
return etmq;
-
-out_free_decoder:
- cs_etm_decoder__free(etmq->decoder);
-out_free:
- intlist__delete(etmq->traceid_queues_list);
- free(etmq);
-
- return NULL;
}
static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
struct auxtrace_queue *queue,
- unsigned int queue_nr,
- bool formatted)
+ unsigned int queue_nr, enum cs_etm_format format)
{
struct cs_etm_queue *etmq = queue->priv;
+ if (etmq && format != etmq->format) {
+ pr_err("CS_ETM: mixed formatted and unformatted trace not supported\n");
+ return -EINVAL;
+ }
+
if (list_empty(&queue->head) || etmq)
return 0;
- etmq = cs_etm__alloc_queue(etm, formatted);
+ etmq = cs_etm__alloc_queue();
if (!etmq)
return -ENOMEM;
@@ -1096,7 +1100,9 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
queue->priv = etmq;
etmq->etm = etm;
etmq->queue_nr = queue_nr;
+ queue->cpu = queue_nr; /* Placeholder, may be reset to -1 in per-thread mode */
etmq->offset = 0;
+ etmq->format = format;
return 0;
}
@@ -1224,12 +1230,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
}
static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
- u8 trace_chan_id, u64 addr)
+ struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet, u64 addr)
{
u8 instrBytes[2];
- cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes),
- instrBytes, 0);
+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
+ ARRAY_SIZE(instrBytes), instrBytes);
/*
* T32 instruction size is indicated by bits[15:11] of the first
* 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111
@@ -1258,16 +1265,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet)
}
static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
- u64 trace_chan_id,
- const struct cs_etm_packet *packet,
+ struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet,
u64 offset)
{
if (packet->isa == CS_ETM_ISA_T32) {
u64 addr = packet->start_addr;
while (offset) {
- addr += cs_etm__t32_instr_size(etmq,
- trace_chan_id, addr);
+ addr += cs_etm__t32_instr_size(etmq, tidq, packet,
+ addr);
offset--;
}
return addr;
@@ -1355,38 +1362,54 @@ cs_etm__get_trace(struct cs_etm_queue *etmq)
etmq->buf_used = 0;
etmq->buf_len = aux_buffer->size;
etmq->buf = aux_buffer->data;
-
- return etmq->buf_len;
+ return 0;
}
-static void cs_etm__set_thread(struct cs_etm_queue *etmq,
- struct cs_etm_traceid_queue *tidq, pid_t tid,
- ocsd_ex_level el)
+/*
+ * Convert a raw thread number to a thread struct and assign it to **thread.
+ */
+static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq,
+ ocsd_ex_level el, pid_t tid,
+ struct thread **thread)
{
struct machine *machine = cs_etm__get_machine(etmq, el);
+ if (!machine || !*thread)
+ return -EINVAL;
+
if (tid != -1) {
- thread__zput(tidq->thread);
- tidq->thread = machine__find_thread(machine, -1, tid);
+ thread__zput(*thread);
+ *thread = machine__find_thread(machine, -1, tid);
}
/* Couldn't find a known thread */
- if (!tidq->thread)
- tidq->thread = machine__idle_thread(machine);
+ if (!*thread)
+ *thread = machine__idle_thread(machine);
- tidq->el = el;
+ return 0;
}
-int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
- u8 trace_chan_id, ocsd_ex_level el)
+/*
+ * Set the thread and EL of the decode context which is ahead in time of the
+ * frontend context.
+ */
+int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
+ u8 trace_chan_id,
+ ocsd_ex_level el, pid_t tid)
{
struct cs_etm_traceid_queue *tidq;
+ int ret;
tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
if (!tidq)
return -EINVAL;
- cs_etm__set_thread(etmq, tidq, tid, el);
+ ret = cs_etm__etmq_update_thread(etmq, el, tid,
+ &tidq->decode_thread);
+ if (ret)
+ return ret;
+
+ tidq->decode_el = el;
return 0;
}
@@ -1396,8 +1419,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq)
}
static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
- u64 trace_chan_id,
- const struct cs_etm_packet *packet,
+ struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet,
struct perf_sample *sample)
{
/*
@@ -1414,14 +1437,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
* cs_etm__t32_instr_size().
*/
if (packet->isa == CS_ETM_ISA_T32)
- sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
+ sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet,
sample->ip);
/* Otherwise, A64 and A32 instruction size are always 32-bit. */
else
sample->insn_len = 4;
- cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len,
- (void *)sample->insn, 0);
+ cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip,
+ sample->insn_len, (void *)sample->insn);
}
u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp)
@@ -1448,6 +1471,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet,
u64 addr, u64 period)
{
int ret = 0;
@@ -1456,23 +1480,23 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
struct perf_sample sample = {.ip = 0,};
event->sample.header.type = PERF_RECORD_SAMPLE;
- event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el);
+ event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el);
event->sample.header.size = sizeof(struct perf_event_header);
/* Set time field based on etm auxtrace config. */
sample.time = cs_etm__resolve_sample_time(etmq, tidq);
sample.ip = addr;
- sample.pid = thread__pid(tidq->thread);
- sample.tid = thread__tid(tidq->thread);
+ sample.pid = thread__pid(tidq->frontend_thread);
+ sample.tid = thread__tid(tidq->frontend_thread);
sample.id = etmq->etm->instructions_id;
sample.stream_id = etmq->etm->instructions_id;
sample.period = period;
- sample.cpu = tidq->packet->cpu;
+ sample.cpu = packet->cpu;
sample.flags = tidq->prev_packet->flags;
sample.cpumode = event->sample.header.misc;
- cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
+ cs_etm__copy_insn(etmq, tidq, packet, &sample);
if (etm->synth_opts.last_branch)
sample.branch_stack = tidq->last_branch;
@@ -1516,15 +1540,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
event->sample.header.type = PERF_RECORD_SAMPLE;
event->sample.header.misc = cs_etm__cpu_mode(etmq, ip,
- tidq->prev_packet_el);
+ tidq->prev_packet->el);
event->sample.header.size = sizeof(struct perf_event_header);
/* Set time field based on etm auxtrace config. */
sample.time = cs_etm__resolve_sample_time(etmq, tidq);
sample.ip = ip;
- sample.pid = thread__pid(tidq->prev_packet_thread);
- sample.tid = thread__tid(tidq->prev_packet_thread);
+ sample.pid = thread__pid(tidq->frontend_thread);
+ sample.tid = thread__tid(tidq->frontend_thread);
sample.addr = cs_etm__first_executed_instr(tidq->packet);
sample.id = etmq->etm->branches_id;
sample.stream_id = etmq->etm->branches_id;
@@ -1533,8 +1557,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
sample.flags = tidq->prev_packet->flags;
sample.cpumode = event->sample.header.misc;
- cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet,
- &sample);
+ cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample);
/*
* perf report cannot handle events without a branch stack
@@ -1687,7 +1710,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
{
struct cs_etm_auxtrace *etm = etmq->etm;
int ret;
- u8 trace_chan_id = tidq->trace_chan_id;
u64 instrs_prev;
/* Get instructions remainder from previous packet */
@@ -1773,10 +1795,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
* been executed, but PC has not advanced to next
* instruction)
*/
- addr = cs_etm__instr_addr(etmq, trace_chan_id,
- tidq->packet, offset - 1);
+ addr = cs_etm__instr_addr(etmq, tidq, tidq->packet,
+ offset - 1);
ret = cs_etm__synth_instruction_sample(
- etmq, tidq, addr,
+ etmq, tidq, tidq->packet, addr,
etm->instructions_sample_period);
if (ret)
return ret;
@@ -1858,7 +1880,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
addr = cs_etm__last_executed_instr(tidq->prev_packet);
err = cs_etm__synth_instruction_sample(
- etmq, tidq, addr,
+ etmq, tidq, tidq->prev_packet, addr,
tidq->period_instructions);
if (err)
return err;
@@ -1913,7 +1935,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
addr = cs_etm__last_executed_instr(tidq->prev_packet);
err = cs_etm__synth_instruction_sample(
- etmq, tidq, addr,
+ etmq, tidq, tidq->prev_packet, addr,
tidq->period_instructions);
if (err)
return err;
@@ -1923,6 +1945,45 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
return 0;
}
+
+static int cs_etm__flush_stack_cb(struct thread *thread,
+ void *data __maybe_unused)
+{
+ thread_stack__flush(thread);
+ return 0;
+}
+
+static void cs_etm__flush_machine_stack(struct cs_etm_queue *etmq, pid_t pid)
+{
+ struct machine *machine;
+
+ machine = machines__find(&etmq->etm->session->machines, pid);
+ if (machine)
+ machine__for_each_thread(machine, cs_etm__flush_stack_cb, NULL);
+}
+
+static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
+{
+ enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
+
+ if (!etmq->etm->synth_opts.last_branch)
+ return;
+
+ switch (pid_fmt) {
+ case CS_ETM_PIDFMT_CTXTID2:
+ /* Clear the guest stack if virtualization is supported */
+ cs_etm__flush_machine_stack(etmq, DEFAULT_GUEST_KERNEL_ID);
+ fallthrough;
+ case CS_ETM_PIDFMT_CTXTID:
+ cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID);
+ break;
+ case CS_ETM_PIDFMT_NONE:
+ default:
+ break;
+
+ }
+}
+
/*
* cs_etm__get_data_block: Fetch a block from the auxtrace_buffer queue
* if need be.
@@ -1934,25 +1995,38 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
{
int ret;
- if (!etmq->buf_len) {
- ret = cs_etm__get_trace(etmq);
- if (ret <= 0)
- return ret;
- /*
- * We cannot assume consecutive blocks in the data file
- * are contiguous, reset the decoder to force re-sync.
- */
- ret = cs_etm_decoder__reset(etmq->decoder);
- if (ret)
- return ret;
- }
+ /* The current block is not finished */
+ if (etmq->buf_len)
+ return 1;
+
+ ret = cs_etm__get_trace(etmq);
+ if (ret < 0)
+ return ret;
+
+ /* No more buffer to read */
+ if (!etmq->buf_len)
+ return 0;
+
+ /*
+ * We cannot assume consecutive blocks in the data file
+ * are contiguous, reset the decoder to force re-sync.
+ */
+ ret = cs_etm_decoder__reset(etmq->decoder);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the decoder is reset, this causes a global trace
+ * discontinuity. Flush all thread stacks.
+ */
+ cs_etm__flush_all_stack(etmq);
- return etmq->buf_len;
+ return 1;
}
-static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
- struct cs_etm_packet *packet,
- u64 end_addr)
+static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq,
+ struct cs_etm_packet *packet, u64 end_addr)
{
/* Initialise to keep compiler happy */
u16 instr16 = 0;
@@ -1974,8 +2048,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
* so below only read 2 bytes as instruction size for T32.
*/
addr = end_addr - 2;
- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16),
- (u8 *)&instr16, 0);
+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
+ sizeof(instr16), (u8 *)&instr16);
if ((instr16 & 0xFF00) == 0xDF00)
return true;
@@ -1990,8 +2064,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
* +---------+---------+-------------------------+
*/
addr = end_addr - 4;
- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
- (u8 *)&instr32, 0);
+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
+ sizeof(instr32), (u8 *)&instr32);
if ((instr32 & 0x0F000000) == 0x0F000000 &&
(instr32 & 0xF0000000) != 0xF0000000)
return true;
@@ -2007,8 +2081,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
* +-----------------------+---------+-----------+
*/
addr = end_addr - 4;
- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
- (u8 *)&instr32, 0);
+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
+ sizeof(instr32), (u8 *)&instr32);
if ((instr32 & 0xFFE0001F) == 0xd4000001)
return true;
@@ -2024,7 +2098,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
struct cs_etm_traceid_queue *tidq, u64 magic)
{
- u8 trace_chan_id = tidq->trace_chan_id;
struct cs_etm_packet *packet = tidq->packet;
struct cs_etm_packet *prev_packet = tidq->prev_packet;
@@ -2039,7 +2112,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
*/
if (magic == __perf_cs_etmv4_magic) {
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
- cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
+ cs_etm__is_svc_instr(etmq, tidq, prev_packet,
prev_packet->end_addr))
return true;
}
@@ -2077,7 +2150,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
struct cs_etm_traceid_queue *tidq,
u64 magic)
{
- u8 trace_chan_id = tidq->trace_chan_id;
struct cs_etm_packet *packet = tidq->packet;
struct cs_etm_packet *prev_packet = tidq->prev_packet;
@@ -2103,7 +2175,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
* (SMC, HVC) are taken as sync exceptions.
*/
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
- !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
+ !cs_etm__is_svc_instr(etmq, tidq, prev_packet,
prev_packet->end_addr))
return true;
@@ -2127,7 +2199,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
{
struct cs_etm_packet *packet = tidq->packet;
struct cs_etm_packet *prev_packet = tidq->prev_packet;
- u8 trace_chan_id = tidq->trace_chan_id;
u64 magic;
int ret;
@@ -2208,11 +2279,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
if (prev_packet->flags == (PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_INTERRUPT) &&
- cs_etm__is_svc_instr(etmq, trace_chan_id,
- packet, packet->start_addr))
+ cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) {
prev_packet->flags = PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_SYSCALLRET;
+ }
break;
case CS_ETM_DISCONTINUITY:
/*
@@ -2293,6 +2364,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_INTERRUPT;
break;
+ case CS_ETM_CONTEXT:
case CS_ETM_EMPTY:
default:
break;
@@ -2368,6 +2440,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
*/
cs_etm__sample(etmq, tidq);
break;
+ case CS_ETM_CONTEXT:
+ /*
+ * Update context but don't swap packet. Keep the
+ * previous one for branch source address info, if
+ * tracing the kernel the context packet will be emitted
+ * between two ranges.
+ */
+ ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el,
+ tidq->packet->tid,
+ &tidq->frontend_thread);
+ if (ret)
+ goto out;
+ break;
case CS_ETM_EXCEPTION:
case CS_ETM_EXCEPTION_RET:
/*
@@ -2396,6 +2481,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
}
}
+out:
return ret;
}
@@ -2519,7 +2605,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
if (!tidq)
continue;
- if (tid == -1 || thread__tid(tidq->thread) == tid)
+ if (tid == -1 || thread__tid(tidq->frontend_thread) == tid)
cs_etm__run_per_thread_timeless_decoder(etmq);
} else
cs_etm__run_per_cpu_timeless_decoder(etmq);
@@ -2827,7 +2913,7 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session,
* formatted in piped mode (true).
*/
err = cs_etm__setup_queue(etm, &etm->queues.queue_array[idx],
- idx, true);
+ idx, FORMATTED);
if (err)
return err;
@@ -2948,7 +3034,7 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
union perf_event auxtrace_fragment;
__u64 aux_offset, aux_size;
__u32 idx;
- bool formatted;
+ enum cs_etm_format format;
struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
struct cs_etm_auxtrace,
@@ -3031,9 +3117,10 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
return err;
idx = auxtrace_event->idx;
- formatted = !(aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW);
- return cs_etm__setup_queue(etm, &etm->queues.queue_array[idx],
- idx, formatted);
+ format = (aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW) ?
+ UNFORMATTED : FORMATTED;
+
+ return cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], idx, format);
}
/* Wasn't inside this buffer, but there were no parse errors. 1 == 'not found' */
@@ -3217,6 +3304,84 @@ static int cs_etm__clear_unused_trace_ids_metadata(int num_cpu, u64 **metadata)
return 0;
}
+/*
+ * Use the data gathered by the peeks for HW_ID (trace ID mappings) and AUX
+ * (formatted or not) packets to create the decoders.
+ */
+static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq)
+{
+ struct cs_etm_decoder_params d_params;
+
+ /*
+ * Each queue can only contain data from one CPU when unformatted, so only one decoder is
+ * needed.
+ */
+ int decoders = etmq->format == FORMATTED ? etmq->etm->num_cpu : 1;
+
+ /* Use metadata to fill in trace parameters for trace decoder */
+ struct cs_etm_trace_params *t_params = zalloc(sizeof(*t_params) * decoders);
+
+ if (!t_params)
+ goto out_free;
+
+ if (cs_etm__init_trace_params(t_params, etmq->etm, etmq->format,
+ etmq->queue_nr, decoders))
+ goto out_free;
+
+ /* Set decoder parameters to decode trace packets */
+ if (cs_etm__init_decoder_params(&d_params, etmq,
+ dump_trace ? CS_ETM_OPERATION_PRINT :
+ CS_ETM_OPERATION_DECODE))
+ goto out_free;
+
+ etmq->decoder = cs_etm_decoder__new(decoders, &d_params,
+ t_params);
+
+ if (!etmq->decoder)
+ goto out_free;
+
+ /*
+ * Register a function to handle all memory accesses required by
+ * the trace decoder library.
+ */
+ if (cs_etm_decoder__add_mem_access_cb(etmq->decoder,
+ 0x0L, ((u64) -1L),
+ cs_etm__decoder_mem_access))
+ goto out_free_decoder;
+
+ zfree(&t_params);
+ return 0;
+
+out_free_decoder:
+ cs_etm_decoder__free(etmq->decoder);
+out_free:
+ zfree(&t_params);
+ return -EINVAL;
+}
+
+static int cs_etm__create_decoders(struct cs_etm_auxtrace *etm)
+{
+ struct auxtrace_queues *queues = &etm->queues;
+
+ for (unsigned int i = 0; i < queues->nr_queues; i++) {
+ bool empty = list_empty(&queues->queue_array[i].head);
+ struct cs_etm_queue *etmq = queues->queue_array[i].priv;
+ int ret;
+
+ /*
+ * Don't create decoders for empty queues, mainly because
+ * etmq->format is unknown for empty queues.
+ */
+ if (empty)
+ continue;
+
+ ret = cs_etm__create_queue_decoders(etmq);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
int cs_etm__process_auxtrace_info_full(union perf_event *event,
struct perf_session *session)
{
@@ -3365,6 +3530,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
if (err)
goto err_free_queues;
+ err = cs_etm__queue_aux_records(session);
+ if (err)
+ goto err_free_queues;
+
/*
* Map Trace ID values to CPU metadata.
*
@@ -3387,7 +3556,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
* flags if present.
*/
- /* first scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */
+ /* Scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */
aux_hw_id_found = 0;
err = perf_session__peek_events(session, session->header.data_offset,
session->header.data_size,
@@ -3405,7 +3574,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
if (err)
goto err_free_queues;
- err = cs_etm__queue_aux_records(session);
+ err = cs_etm__create_decoders(etm);
if (err)
goto err_free_queues;
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
index 7cca37887917..f7b4903d9ef7 100644
--- a/tools/perf/util/cs-etm.h
+++ b/tools/perf/util/cs-etm.h
@@ -158,6 +158,7 @@ enum cs_etm_sample_type {
CS_ETM_DISCONTINUITY,
CS_ETM_EXCEPTION,
CS_ETM_EXCEPTION_RET,
+ CS_ETM_CONTEXT,
};
enum cs_etm_isa {
@@ -184,6 +185,8 @@ struct cs_etm_packet {
u8 last_instr_size;
u8 trace_chan_id;
int cpu;
+ int el;
+ pid_t tid;
};
#define CS_ETM_PACKET_MAX_BUFFER 1024
@@ -254,8 +257,9 @@ enum cs_etm_pid_fmt {
#include <opencsd/ocsd_if_types.h>
int cs_etm__get_cpu(u8 trace_chan_id, int *cpu);
enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq);
-int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
- u8 trace_chan_id, ocsd_ex_level el);
+int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
+ u8 trace_chan_id, ocsd_ex_level el,
+ pid_t tid);
bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq);
void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
u8 trace_chan_id);
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index 5bb3c2ba95ca..30f92656d5fb 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -242,6 +242,7 @@ static int process_sample_event(struct perf_tool *tool,
trace_seq_init(&s);
tep_print_field(&s, sample->raw_data, fields[i]);
output_json_key_string(out, true, 3, fields[i]->name, s.buffer);
+ trace_seq_destroy(&s);
i++;
}
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index bdfead36b83a..0e71bfffd4ac 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -379,7 +379,9 @@ int dso__decompress_kmodule_path(struct dso *dso, const char *name,
{
int fd = decompress_kmodule(dso, name, pathname, len);
- close(fd);
+ /* decompress_kmodule() returns -1 on failure, don't close(-1) */
+ if (fd >= 0)
+ close(fd);
return fd >= 0 ? 0 : -1;
}
@@ -913,7 +915,20 @@ static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data,
u64 size, bool out)
{
u64 cache_offset = offset - cache->offset;
- u64 cache_size = min(cache->size - cache_offset, size);
+ u64 cache_size;
+
+ /*
+ * The RB tree matches using DSO__DATA_CACHE_SIZE, but a short
+ * pread may leave cache->size smaller. For a regular file a
+ * short pread only happens at end-of-file, so an offset past
+ * the valid data is EOF: return 0, matching what a direct
+ * pread() at that offset would return, and cached_io() then
+ * stops its read loop.
+ */
+ if (cache_offset >= cache->size)
+ return 0;
+
+ cache_size = min(cache->size - cache_offset, size);
if (out)
memcpy(data, cache->data + cache_offset, cache_size);
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c
index ec1b3bd9f530..3e1d32825046 100644
--- a/tools/perf/util/intel-bts.c
+++ b/tools/perf/util/intel-bts.c
@@ -858,7 +858,7 @@ int intel_bts_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
- size_t min_sz = sizeof(u64) * INTEL_BTS_SNAPSHOT_MODE;
+ size_t min_sz = sizeof(u64) * (INTEL_BTS_SNAPSHOT_MODE + 1);
struct intel_bts *bts;
int err;
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index e9f97c0c3358..4b68c301ce72 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -4381,7 +4381,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
- size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS;
+ size_t min_sz = sizeof(u64) * (INTEL_PT_PER_CPU_MMAPS + 1);
struct intel_pt *pt;
void *info_end;
__u64 *info;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index e2a6facd1c4e..9ea386614df6 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -369,16 +369,22 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
if ((pid != HOST_KERNEL_ID) &&
(pid != DEFAULT_GUEST_KERNEL_ID) &&
(symbol_conf.guestmount)) {
- sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
+ if (snprintf(path, sizeof(path), "%s/%d",
+ symbol_conf.guestmount, pid) >= (int)sizeof(path)) {
+ pr_err("Guest path too long for pid %d\n", pid);
+ machine = NULL;
+ goto out;
+ }
if (access(path, R_OK)) {
static struct strlist *seen;
if (!seen)
seen = strlist__new(NULL, NULL);
- if (!strlist__has_entry(seen, path)) {
+ if (!seen || !strlist__has_entry(seen, path)) {
pr_err("Can't access file %s\n", path);
- strlist__add(seen, path);
+ if (seen)
+ strlist__add(seen, path);
}
machine = NULL;
goto out;
@@ -1405,27 +1411,35 @@ int machines__create_guest_kernel_maps(struct machines *machines)
for (i = 0; i < items; i++) {
if (!isdigit(namelist[i]->d_name[0])) {
/* Filter out . and .. */
+ free(namelist[i]);
continue;
}
+ errno = 0;
pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10);
if ((*endp != '\0') ||
(endp == namelist[i]->d_name) ||
(errno == ERANGE)) {
pr_debug("invalid directory (%s). Skipping.\n",
namelist[i]->d_name);
+ free(namelist[i]);
continue;
}
- sprintf(path, "%s/%s/proc/kallsyms",
- symbol_conf.guestmount,
- namelist[i]->d_name);
- ret = access(path, R_OK);
- if (ret) {
+ if (snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
+ symbol_conf.guestmount,
+ namelist[i]->d_name) >= (int)sizeof(path)) {
+ pr_debug("Guest kallsyms path too long for %s. Skipping.\n",
+ namelist[i]->d_name);
+ free(namelist[i]);
+ continue;
+ }
+ if (access(path, R_OK)) {
pr_debug("Can't access file %s\n", path);
- goto failure;
+ free(namelist[i]);
+ continue;
}
machines__create_kernel_maps(machines, pid);
+ free(namelist[i]);
}
-failure:
free(namelist);
}
@@ -2103,7 +2117,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
* (fork) event that would have removed the thread was lost. Assume the
* latter case and continue on as best we can.
*/
- if (thread__pid(parent) != (pid_t)event->fork.ppid) {
+ if (parent != NULL &&
+ thread__pid(parent) != (pid_t)event->fork.ppid) {
dump_printf("removing erroneous parent thread %d/%d\n",
thread__pid(parent), thread__tid(parent));
machine__remove_thread(machine, parent);
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index bb5faaa25d51..3846a39decff 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -1810,8 +1810,10 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
new_expr->metric_expr = old_expr->metric_expr;
new_expr->metric_threshold = old_expr->metric_threshold;
new_expr->metric_name = strdup(old_expr->metric_name);
- if (!new_expr->metric_name)
+ if (!new_expr->metric_name) {
+ free(new_expr);
return -ENOMEM;
+ }
new_expr->metric_unit = old_expr->metric_unit;
new_expr->runtime = old_expr->runtime;
@@ -1823,6 +1825,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
alloc_size = sizeof(*new_expr->metric_refs);
new_expr->metric_refs = calloc(nr + 1, alloc_size);
if (!new_expr->metric_refs) {
+ zfree(&new_expr->metric_name);
free(new_expr);
return -ENOMEM;
}
@@ -1839,6 +1842,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
alloc_size = sizeof(*new_expr->metric_events);
new_expr->metric_events = calloc(nr + 1, alloc_size);
if (!new_expr->metric_events) {
+ zfree(&new_expr->metric_name);
zfree(&new_expr->metric_refs);
free(new_expr);
return -ENOMEM;
@@ -1849,6 +1853,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
evsel = old_expr->metric_events[idx];
evsel = evlist__find_evsel(evlist, evsel->core.idx);
if (evsel == NULL) {
+ zfree(&new_expr->metric_name);
zfree(&new_expr->metric_events);
zfree(&new_expr->metric_refs);
free(new_expr);
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index a0579c7d7b9e..756673761787 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -1029,7 +1029,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
else
thread_nr = nr_threads_synthesize;
- if (thread_nr <= 1) {
+ if (thread_nr <= 1 || n <= 1) {
err = __perf_event__synthesize_threads(tool, process,
machine,
needs_mmap, mmap_data,
@@ -1075,8 +1075,8 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
}
err = 0;
out_join:
- for (i = 0; i < thread_nr; i++)
- pthread_join(synthesize_threads[i], NULL);
+ for (j = 0; j < i; j++)
+ pthread_join(synthesize_threads[j], NULL);
free(args);
free_threads:
free(synthesize_threads);
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index c6a0a27b12c2..47d5922efdee 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -642,7 +642,7 @@ void thread_stack__br_sample(struct thread *thread, int cpu,
sz -= nr;
be = &dst->entries[nr];
nr = min(ts->br_stack_pos, sz);
- memcpy(be, &src->entries[0], bsz * ts->br_stack_pos);
+ memcpy(be, &src->entries[0], bsz * nr);
}
}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 1162c49b8082..b0d3957c48e6 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -25,18 +25,18 @@ static int input_fd;
static ssize_t trace_data_size;
static bool repipe;
-static int __do_read(int fd, void *buf, int size)
+static ssize_t __do_read(int fd, void *buf, size_t size)
{
- int rsize = size;
+ size_t rsize = size;
while (size) {
- int ret = read(fd, buf, size);
+ ssize_t ret = read(fd, buf, size);
if (ret <= 0)
return -1;
if (repipe) {
- int retw = write(STDOUT_FILENO, buf, ret);
+ ssize_t retw = write(STDOUT_FILENO, buf, ret);
if (retw <= 0 || retw != ret) {
pr_debug("repiping input file");
@@ -51,13 +51,13 @@ static int __do_read(int fd, void *buf, int size)
return rsize;
}
-static int do_read(void *data, int size)
+static ssize_t do_read(void *data, size_t size)
{
- int r;
+ ssize_t r;
r = __do_read(input_fd, data, size);
if (r <= 0) {
- pr_debug("reading input file (size expected=%d received=%d)",
+ pr_debug("reading input file (size expected=%zu received=%zd)",
size, r);
return -1;
}
@@ -68,10 +68,10 @@ static int do_read(void *data, int size)
}
/* If it fails, the next read will report it */
-static void skip(int size)
+static void skip(size_t size)
{
char buf[BUFSIZ];
- int r;
+ size_t r;
while (size) {
r = size > BUFSIZ ? BUFSIZ : size;
@@ -127,6 +127,11 @@ static char *read_string(void)
}
}
+ if (size >= (int)sizeof(buf) - 1) {
+ pr_debug("string too long (max %zu bytes)", sizeof(buf) - 1);
+ goto out;
+ }
+
buf[size++] = c;
if (!c)
@@ -197,7 +202,7 @@ static int read_header_files(struct tep_handle *pevent)
unsigned long long size;
char *header_page;
char buf[BUFSIZ];
- int ret = 0;
+ ssize_t ret = 0;
if (do_read(buf, 12) < 0)
return -1;
@@ -245,7 +250,7 @@ static int read_header_files(struct tep_handle *pevent)
static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size)
{
- int ret;
+ ssize_t ret;
char *buf;
buf = malloc(size);
@@ -271,7 +276,7 @@ static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size)
static int read_event_file(struct tep_handle *pevent, char *sys,
unsigned long long size)
{
- int ret;
+ ssize_t ret;
char *buf;
buf = malloc(size);
@@ -317,7 +322,7 @@ static int read_event_files(struct tep_handle *pevent)
int systems;
int count;
int i,x;
- int ret;
+ ssize_t ret;
systems = read4(pevent);
@@ -345,7 +350,7 @@ static int read_saved_cmdline(struct tep_handle *pevent)
{
unsigned long long size;
char *buf;
- int ret;
+ ssize_t ret;
/* it can have 0 size */
size = read8(pevent);
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
index c006e72b4f43..b088c92029de 100644
--- a/tools/scripts/Makefile.include
+++ b/tools/scripts/Makefile.include
@@ -59,10 +59,18 @@ LLVM_SUFFIX := $(LLVM)
endif
$(call allow-override,CC,$(LLVM_PREFIX)clang$(LLVM_SUFFIX))
+$(call allow-override,CLANG,$(LLVM_PREFIX)clang$(LLVM_SUFFIX))
+$(call allow-override,HOSTCC,$(LLVM_PREFIX)clang$(LLVM_SUFFIX))
$(call allow-override,AR,$(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX))
+$(call allow-override,HOSTAR,$(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX))
$(call allow-override,LD,$(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX))
+$(call allow-override,HOSTLD,$(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX))
$(call allow-override,CXX,$(LLVM_PREFIX)clang++$(LLVM_SUFFIX))
$(call allow-override,STRIP,$(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX))
+$(call allow-override,LLVM_STRIP,$(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX))
+$(call allow-override,LLC,$(LLVM_PREFIX)llc$(LLVM_SUFFIX))
+$(call allow-override,LLVM_CONFIG,$(LLVM_PREFIX)llvm-config$(LLVM_SUFFIX))
+$(call allow-override,LLVM_OBJCOPY,$(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX))
else
# Allow setting various cross-compile vars or setting CROSS_COMPILE as a prefix.
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
@@ -70,26 +78,24 @@ $(call allow-override,AR,$(CROSS_COMPILE)ar)
$(call allow-override,LD,$(CROSS_COMPILE)ld)
$(call allow-override,CXX,$(CROSS_COMPILE)g++)
$(call allow-override,STRIP,$(CROSS_COMPILE)strip)
+
+# Host versions aren't prefixed
+$(call allow-override,HOSTAR,ar)
+$(call allow-override,HOSTCC,gcc)
+$(call allow-override,HOSTLD,ld)
+
+# Some tools still require Clang, LLC and/or LLVM utils
+$(call allow-override,CLANG,clang)
+$(call allow-override,LLC,llc)
+$(call allow-override,LLVM_CONFIG,llvm-config)
+$(call allow-override,LLVM_OBJCOPY,llvm-objcopy)
+$(call allow-override,LLVM_STRIP,llvm-strip)
endif
CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?)
-ifneq ($(LLVM),)
-HOSTAR ?= $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX)
-HOSTCC ?= $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
-HOSTLD ?= $(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX)
-else
-HOSTAR ?= ar
-HOSTCC ?= gcc
-HOSTLD ?= ld
-endif
-
-# Some tools require Clang, LLC and/or LLVM utils
-CLANG ?= clang
-LLC ?= llc
-LLVM_CONFIG ?= llvm-config
-LLVM_OBJCOPY ?= llvm-objcopy
-LLVM_STRIP ?= llvm-strip
+# Some tools require bpftool
+SYSTEM_BPFTOOL ?= bpftool
ifeq ($(CC_NO_CLANG), 1)
EXTRA_WARNINGS += -Wstrict-aliasing=3
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
index bc74088c458a..ebe97ba520ce 100755
--- a/tools/testing/kunit/kunit.py
+++ b/tools/testing/kunit/kunit.py
@@ -121,7 +121,7 @@ def _list_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest)
lines.pop()
# Filter out any extraneous non-test output that might have gotten mixed in.
- return [l for l in output if re.match(r'^[^\s.]+\.[^\s.]+$', l)]
+ return [l for l in lines if re.match(r'^[^\s.]+\.[^\s.]+$', l)]
def _list_tests_attr(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> Iterable[str]:
args = ['kunit.action=list_attr']
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
index 5254a25ad2d9..052b3a9fb821 100755
--- a/tools/testing/kunit/kunit_tool_test.py
+++ b/tools/testing/kunit/kunit_tool_test.py
@@ -826,6 +826,18 @@ class KUnitMainTest(unittest.TestCase):
self.linux_source_mock.run_kernel.assert_called_once_with(
args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300)
+ def test_list_tests_with_prefix(self):
+ want = ['suite.test1', 'suite.test2', 'suite2.test1']
+ self.linux_source_mock.run_kernel.return_value = [
+ '[ 0.100000] TAP version 14',
+ '[ 0.200000] suite.test1',
+ '[ 0.200000] suite.test2',
+ '[ 0.300000] suite2.test1']
+
+ got = kunit._list_tests(self.linux_source_mock,
+ kunit.KunitExecRequest(None, None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False, False))
+ self.assertEqual(got, want)
+
@mock.patch.object(kunit, '_list_tests')
def test_run_isolated_by_suite(self, mock_tests):
mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']
diff --git a/tools/testing/selftests/arm64/mte/check_buffer_fill.c b/tools/testing/selftests/arm64/mte/check_buffer_fill.c
index 1dbbbd47dd50..639ea469969a 100644
--- a/tools/testing/selftests/arm64/mte/check_buffer_fill.c
+++ b/tools/testing/selftests/arm64/mte/check_buffer_fill.c
@@ -406,6 +406,8 @@ int main(int argc, char *argv[])
size_t page_size = getpagesize();
int item = ARRAY_SIZE(sizes);
+ ksft_print_header();
+
sizes[item - 3] = page_size - 1;
sizes[item - 2] = page_size;
sizes[item - 1] = page_size + 1;
diff --git a/tools/testing/selftests/arm64/mte/check_child_memory.c b/tools/testing/selftests/arm64/mte/check_child_memory.c
index 7597fc632cad..7017a4851ff4 100644
--- a/tools/testing/selftests/arm64/mte/check_child_memory.c
+++ b/tools/testing/selftests/arm64/mte/check_child_memory.c
@@ -146,6 +146,8 @@ int main(int argc, char *argv[])
int err;
int item = ARRAY_SIZE(sizes);
+ ksft_print_header();
+
page_size = getpagesize();
if (!page_size) {
ksft_print_msg("ERR: Unable to get page size\n");
diff --git a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
index 325bca0de0f6..d23f154d3288 100644
--- a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
+++ b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
@@ -131,6 +131,7 @@ int main(int argc, char *argv[])
if (err)
return err;
+ ksft_print_header();
ksft_set_plan(1);
evaluate_test(mte_gcr_fork_test(),
diff --git a/tools/testing/selftests/arm64/mte/check_hugetlb_options.c b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c
new file mode 100644
index 000000000000..cd7667187ed5
--- /dev/null
+++ b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2024 Ampere Computing LLC
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "kselftest.h"
+#include "mte_common_util.h"
+#include "mte_def.h"
+
+#define TAG_CHECK_ON 0
+#define TAG_CHECK_OFF 1
+
+static unsigned long default_huge_page_size(void)
+{
+ unsigned long hps = 0;
+ char *line = NULL;
+ size_t linelen = 0;
+ FILE *f = fopen("/proc/meminfo", "r");
+
+ if (!f)
+ return 0;
+ while (getline(&line, &linelen, f) > 0) {
+ if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
+ hps <<= 10;
+ break;
+ }
+ }
+
+ free(line);
+ fclose(f);
+ return hps;
+}
+
+static bool is_hugetlb_allocated(void)
+{
+ unsigned long hps = 0;
+ char *line = NULL;
+ size_t linelen = 0;
+ FILE *f = fopen("/proc/meminfo", "r");
+
+ if (!f)
+ return false;
+ while (getline(&line, &linelen, f) > 0) {
+ if (sscanf(line, "Hugetlb: %lu kB", &hps) == 1) {
+ hps <<= 10;
+ break;
+ }
+ }
+
+ free(line);
+ fclose(f);
+
+ if (hps > 0)
+ return true;
+
+ return false;
+}
+
+static void write_sysfs(char *str, unsigned long val)
+{
+ FILE *f;
+
+ f = fopen(str, "w");
+ if (!f) {
+ ksft_print_msg("ERR: missing %s\n", str);
+ return;
+ }
+ fprintf(f, "%lu", val);
+ fclose(f);
+}
+
+static void allocate_hugetlb()
+{
+ write_sysfs("/proc/sys/vm/nr_hugepages", 2);
+}
+
+static void free_hugetlb()
+{
+ write_sysfs("/proc/sys/vm/nr_hugepages", 0);
+}
+
+static int check_child_tag_inheritance(char *ptr, int size, int mode)
+{
+ int i, parent_tag, child_tag, fault, child_status;
+ pid_t child;
+
+ parent_tag = MT_FETCH_TAG((uintptr_t)ptr);
+ fault = 0;
+
+ child = fork();
+ if (child == -1) {
+ ksft_print_msg("FAIL: child process creation\n");
+ return KSFT_FAIL;
+ } else if (child == 0) {
+ mte_initialize_current_context(mode, (uintptr_t)ptr, size);
+ /* Do copy on write */
+ memset(ptr, '1', size);
+ mte_wait_after_trig();
+ if (cur_mte_cxt.fault_valid == true) {
+ fault = 1;
+ goto check_child_tag_inheritance_err;
+ }
+ for (i = 0; i < size; i += MT_GRANULE_SIZE) {
+ child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
+ if (parent_tag != child_tag) {
+ ksft_print_msg("FAIL: child mte tag (%d) mismatch\n", i);
+ fault = 1;
+ goto check_child_tag_inheritance_err;
+ }
+ }
+check_child_tag_inheritance_err:
+ _exit(fault);
+ }
+ /* Wait for child process to terminate */
+ wait(&child_status);
+ if (WIFEXITED(child_status))
+ fault = WEXITSTATUS(child_status);
+ else
+ fault = 1;
+ return (fault) ? KSFT_FAIL : KSFT_PASS;
+}
+
+static int check_mte_memory(char *ptr, int size, int mode, int tag_check)
+{
+ mte_initialize_current_context(mode, (uintptr_t)ptr, size);
+ memset(ptr, '1', size);
+ mte_wait_after_trig();
+ if (cur_mte_cxt.fault_valid == true)
+ return KSFT_FAIL;
+
+ return KSFT_PASS;
+}
+
+static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int tag_check)
+{
+ char *ptr, *map_ptr;
+ int result;
+ unsigned long map_size;
+
+ map_size = default_huge_page_size();
+
+ mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
+ map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false);
+ if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS)
+ return KSFT_FAIL;
+
+ mte_initialize_current_context(mode, (uintptr_t)map_ptr, map_size);
+ /* Only mte enabled memory will allow tag insertion */
+ ptr = mte_insert_tags((void *)map_ptr, map_size);
+ if (!ptr || cur_mte_cxt.fault_valid == true) {
+ ksft_print_msg("FAIL: Insert tags on anonymous mmap memory\n");
+ munmap((void *)map_ptr, map_size);
+ return KSFT_FAIL;
+ }
+ result = check_mte_memory(ptr, map_size, mode, tag_check);
+ mte_clear_tags((void *)ptr, map_size);
+ mte_free_memory((void *)map_ptr, map_size, mem_type, false);
+ if (result == KSFT_FAIL)
+ return KSFT_FAIL;
+
+ return KSFT_PASS;
+}
+
+static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping)
+{
+ char *map_ptr;
+ int prot_flag, result;
+ unsigned long map_size;
+
+ prot_flag = PROT_READ | PROT_WRITE;
+ mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
+ map_size = default_huge_page_size();
+ map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
+ 0, 0);
+ if (check_allocated_memory_range(map_ptr, map_size, mem_type,
+ 0, 0) != KSFT_PASS)
+ return KSFT_FAIL;
+ /* Try to clear PROT_MTE property and verify it by tag checking */
+ if (mprotect(map_ptr, map_size, prot_flag)) {
+ mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type,
+ 0, 0);
+ ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n");
+ return KSFT_FAIL;
+ }
+ result = check_mte_memory(map_ptr, map_size, mode, TAG_CHECK_ON);
+ mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 0, 0);
+ if (result != KSFT_PASS)
+ return KSFT_FAIL;
+
+ return KSFT_PASS;
+}
+
+static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mapping)
+{
+ char *ptr;
+ int result;
+ unsigned long map_size;
+
+ map_size = default_huge_page_size();
+
+ mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
+ ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
+ 0, 0);
+ if (check_allocated_memory_range(ptr, map_size, mem_type,
+ 0, 0) != KSFT_PASS)
+ return KSFT_FAIL;
+ result = check_child_tag_inheritance(ptr, map_size, mode);
+ mte_free_memory_tag_range((void *)ptr, map_size, mem_type, 0, 0);
+ if (result == KSFT_FAIL)
+ return result;
+
+ return KSFT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+ void *map_ptr;
+ unsigned long map_size;
+
+ ksft_print_header();
+
+ err = mte_default_setup();
+ if (err)
+ return err;
+
+ /* Register signal handlers */
+ mte_register_signal(SIGBUS, mte_default_handler);
+ mte_register_signal(SIGSEGV, mte_default_handler);
+
+ allocate_hugetlb();
+
+ if (!is_hugetlb_allocated()) {
+ ksft_print_msg("ERR: Unable allocate hugetlb pages\n");
+ return KSFT_FAIL;
+ }
+
+ /* Check if MTE supports hugetlb mappings */
+ map_size = default_huge_page_size();
+ map_ptr = mmap(NULL, map_size, PROT_READ | PROT_MTE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+ if (map_ptr == MAP_FAILED)
+ ksft_exit_skip("PROT_MTE not supported with MAP_HUGETLB mappings\n");
+ else
+ munmap(map_ptr, map_size);
+
+ /* Set test plan */
+ ksft_set_plan(12);
+
+ mte_enable_pstate_tco();
+
+ evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
+ "Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check off\n");
+
+ mte_disable_pstate_tco();
+ evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
+ "Check hugetlb memory with private mapping, no error mode, mmap memory and tag check off\n");
+
+ evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
+ "Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check on\n");
+ evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
+ "Check hugetlb memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n");
+ evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
+ "Check hugetlb memory with private mapping, async error mode, mmap memory and tag check on\n");
+ evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
+ "Check hugetlb memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n");
+
+ evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n");
+ evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n");
+
+ evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check child hugetlb memory with private mapping, sync error mode and mmap memory\n");
+ evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check child hugetlb memory with private mapping, async error mode and mmap memory\n");
+ evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check child hugetlb memory with private mapping, sync error mode and mmap/mprotect memory\n");
+ evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
+ "Check child hugetlb memory with private mapping, async error mode and mmap/mprotect memory\n");
+
+ mte_restore_setup();
+ free_hugetlb();
+ ksft_print_cnts();
+ return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
+}
diff --git a/tools/testing/selftests/arm64/mte/check_ksm_options.c b/tools/testing/selftests/arm64/mte/check_ksm_options.c
index 88c74bc46d4f..a947e6413e56 100644
--- a/tools/testing/selftests/arm64/mte/check_ksm_options.c
+++ b/tools/testing/selftests/arm64/mte/check_ksm_options.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,6 +23,20 @@
static size_t page_sz;
static unsigned long ksm_sysfs[5];
+static bool has_merge_across_nodes;
+
+static bool merge_across_nodes_available(void)
+{
+ const char *path = PATH_KSM "merge_across_nodes";
+
+ if (!access(path, R_OK | W_OK))
+ return true;
+ if (errno == ENOENT)
+ return false;
+
+ ksft_exit_skip("Unable to read and write %s: %s\n", path,
+ strerror(errno));
+}
static unsigned long read_sysfs(char *str)
{
@@ -56,8 +71,10 @@ static void write_sysfs(char *str, unsigned long val)
static void mte_ksm_setup(void)
{
- ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
- write_sysfs(PATH_KSM "merge_across_nodes", 1);
+ if (has_merge_across_nodes) {
+ ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
+ write_sysfs(PATH_KSM "merge_across_nodes", 1);
+ }
ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs");
write_sysfs(PATH_KSM "sleep_millisecs", 0);
ksm_sysfs[2] = read_sysfs(PATH_KSM "run");
@@ -70,7 +87,8 @@ static void mte_ksm_setup(void)
static void mte_ksm_restore(void)
{
- write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
+ if (has_merge_across_nodes)
+ write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]);
write_sysfs(PATH_KSM "run", ksm_sysfs[2]);
write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]);
@@ -132,9 +150,16 @@ int main(int argc, char *argv[])
{
int err;
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
+
+ if (geteuid() != 0)
+ ksft_exit_skip("Please run the test as root\n");
+
+ has_merge_across_nodes = merge_across_nodes_available();
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");
diff --git a/tools/testing/selftests/arm64/mte/check_mmap_options.c b/tools/testing/selftests/arm64/mte/check_mmap_options.c
index 17694caaff53..703ccd4685a8 100644
--- a/tools/testing/selftests/arm64/mte/check_mmap_options.c
+++ b/tools/testing/selftests/arm64/mte/check_mmap_options.c
@@ -188,6 +188,8 @@ int main(int argc, char *argv[])
int err;
int item = ARRAY_SIZE(sizes);
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
diff --git a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
index a3d1e23fe02a..c73b4641d01a 100644
--- a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
+++ b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
@@ -175,6 +175,8 @@ int main(int argc, char *argv[])
{
int err;
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
diff --git a/tools/testing/selftests/arm64/mte/check_user_mem.c b/tools/testing/selftests/arm64/mte/check_user_mem.c
index f4ae5f87a3b7..8c790331d67c 100644
--- a/tools/testing/selftests/arm64/mte/check_user_mem.c
+++ b/tools/testing/selftests/arm64/mte/check_user_mem.c
@@ -201,6 +201,8 @@ int main(int argc, char *argv[])
int tag_offsets[] = {page_sz, MT_GRANULE_SIZE};
char test_name[TEST_NAME_MAX];
+ ksft_print_header();
+
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");
diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst
index cb9b95702ac6..8f62a641ef4f 100644
--- a/tools/testing/selftests/bpf/README.rst
+++ b/tools/testing/selftests/bpf/README.rst
@@ -85,7 +85,37 @@ In case of linker errors when running selftests, try using static linking:
If you want to change pahole and llvm, you can change `PATH` environment
variable in the beginning of script.
-.. note:: The script currently only supports x86_64 and s390x architectures.
+Running vmtest on RV64
+======================
+To speed up testing and avoid various dependency issues, it is recommended to
+run vmtest in a Docker container. Before running vmtest, we need to prepare
+Docker container and local rootfs image. The overall steps are as follows:
+
+1. Create Docker container as shown in link [0].
+
+2. Use mkrootfs_debian.sh script [1] to build local rootfs image:
+
+.. code-block:: console
+
+ $ sudo ./mkrootfs_debian.sh --arch riscv64 --distro noble
+
+3. Start Docker container [0] and run vmtest in the container:
+
+.. code-block:: console
+
+ $ PLATFORM=riscv64 CROSS_COMPILE=riscv64-linux-gnu- \
+ tools/testing/selftests/bpf/vmtest.sh \
+ -l <path of local rootfs image> -- \
+ ./test_progs -d \
+ "$(cat tools/testing/selftests/bpf/DENYLIST.riscv64 \
+ | cut -d'#' -f1 \
+ | sed -e 's/^[[:space:]]*//' \
+ -e 's/[[:space:]]*$//' \
+ | tr -s '\n' ',' \
+ )"
+
+Link: https://github.com/pulehui/riscv-bpf-vmtest.git [0]
+Link: https://github.com/libbpf/ci/blob/main/rootfs/mkrootfs_debian.sh [1]
Additional information about selftest failures are
documented here.
diff --git a/tools/testing/selftests/bpf/prog_tests/global_map_resize.c b/tools/testing/selftests/bpf/prog_tests/global_map_resize.c
index 56b5baef35c8..602ce30f1720 100644
--- a/tools/testing/selftests/bpf/prog_tests/global_map_resize.c
+++ b/tools/testing/selftests/bpf/prog_tests/global_map_resize.c
@@ -23,6 +23,7 @@ static void global_map_resize_bss_subtest(void)
struct bpf_map *map;
const __u32 desired_sz = sizeof(skel->bss->sum) + sysconf(_SC_PAGE_SIZE) * 2;
size_t array_len, actual_sz, new_sz;
+ int *array;
skel = test_global_map_resize__open();
if (!ASSERT_OK_PTR(skel, "test_global_map_resize__open"))
@@ -58,10 +59,13 @@ static void global_map_resize_bss_subtest(void)
goto teardown;
/* fill the newly resized array with ones,
- * skipping the first element which was previously set
+ * skipping the first element which was previously set;
+ * access through a plain pointer to avoid -Warray-bounds
+ * since the array was resized beyond its declared length.
*/
+ array = skel->bss->array;
for (int i = 1; i < array_len; i++)
- skel->bss->array[i] = 1;
+ array[i] = 1;
/* set global const values before loading */
skel->rodata->pid = getpid();
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index 74620ed3a166..e142d0494b06 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -447,18 +447,19 @@ bool test__start_subtest(const char *subtest_name)
struct test_state *state = env.test_state;
struct subtest_state *subtest_state;
size_t sub_state_size = sizeof(*subtest_state);
+ void *tmp;
if (env.subtest_state)
test__end_subtest();
state->subtest_num++;
- state->subtest_states =
- realloc(state->subtest_states,
- state->subtest_num * sub_state_size);
- if (!state->subtest_states) {
+ tmp = realloc(state->subtest_states, state->subtest_num * sub_state_size);
+ if (!tmp) {
+ state->subtest_num--;
fprintf(stderr, "Not enough memory to allocate subtest result\n");
return false;
}
+ state->subtest_states = tmp;
subtest_state = &state->subtest_states[state->subtest_num - 1];
@@ -1391,7 +1392,7 @@ static void server_main(void)
data[i].worker_id = i;
data[i].sock_fd = env.worker_socks[i];
rc = pthread_create(&dispatcher_threads[i], NULL, dispatch_thread, &data[i]);
- if (rc < 0) {
+ if (rc) {
perror("Failed to launch dispatcher thread");
exit(EXIT_ERR_SETUP_INFRA);
}
diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c
index dccaf9b8cb90..812f96f06a3d 100644
--- a/tools/testing/selftests/bpf/test_sockmap.c
+++ b/tools/testing/selftests/bpf/test_sockmap.c
@@ -513,7 +513,8 @@ static int msg_alloc_iov(struct msghdr *msg,
return 0;
unwind_iov:
for (i--; i >= 0 ; i--)
- free(msg->msg_iov[i].iov_base);
+ free(iov[i].iov_base);
+ free(iov);
return -ENOMEM;
}
diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
index fab89a84119f..d64fdb7d6c58 100644
--- a/tools/testing/selftests/bpf/veristat.c
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -385,6 +385,40 @@ static bool is_bpf_obj_file(const char *path) {
return err == 0;
}
+/* Exact filter match */
+static bool name_filter_matches(struct filter *f, const char *filename, const char *prog_name)
+{
+ if (f->any_glob)
+ return glob_matches(filename, f->any_glob) ||
+ (prog_name && glob_matches(prog_name, f->any_glob));
+ if (f->file_glob && f->prog_glob)
+ return prog_name &&
+ glob_matches(filename, f->file_glob) &&
+ glob_matches(prog_name, f->prog_glob);
+ if (f->file_glob)
+ return glob_matches(filename, f->file_glob);
+ if (f->prog_glob)
+ return prog_name && glob_matches(prog_name, f->prog_glob);
+ return false;
+}
+
+/* Check if the filter does not outright reject the file name */
+static bool name_filter_may_match(struct filter *f, const char *filename)
+{
+ if (f->file_glob)
+ return glob_matches(filename, f->file_glob);
+ /*
+ * If we don't know program name yet, any_glob filter
+ * has to assume that current BPF object file might be
+ * relevant; we'll check again later on after opening
+ * BPF object file, at which point program name will
+ * be known finally.
+ */
+ if (f->any_glob || f->prog_glob)
+ return true;
+ return false;
+}
+
static bool should_process_file_prog(const char *filename, const char *prog_name)
{
struct filter *f;
@@ -392,16 +426,7 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
for (i = 0; i < env.deny_filter_cnt; i++) {
f = &env.deny_filters[i];
- if (f->kind != FILTER_NAME)
- continue;
-
- if (f->any_glob && glob_matches(filename, f->any_glob))
- return false;
- if (f->any_glob && prog_name && glob_matches(prog_name, f->any_glob))
- return false;
- if (f->file_glob && glob_matches(filename, f->file_glob))
- return false;
- if (f->prog_glob && prog_name && glob_matches(prog_name, f->prog_glob))
+ if (f->kind == FILTER_NAME && name_filter_matches(f, filename, prog_name))
return false;
}
@@ -411,24 +436,15 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
continue;
allow_cnt++;
- if (f->any_glob) {
- if (glob_matches(filename, f->any_glob))
- return true;
- /* If we don't know program name yet, any_glob filter
- * has to assume that current BPF object file might be
- * relevant; we'll check again later on after opening
- * BPF object file, at which point program name will
- * be known finally.
- */
- if (!prog_name || glob_matches(prog_name, f->any_glob))
- return true;
- } else {
- if (f->file_glob && !glob_matches(filename, f->file_glob))
- continue;
- if (f->prog_glob && prog_name && !glob_matches(prog_name, f->prog_glob))
- continue;
+ if (prog_name && name_filter_matches(f, filename, prog_name))
+ return true;
+ /*
+ * If there is no prog_name and the file name is not blocked by
+ * the filter, allow to open the file. Afterwards there would be
+ * a second refining query with prog_name set.
+ */
+ if (!prog_name && name_filter_may_match(f, filename))
return true;
- }
}
/* if there are no file/prog name allow filters, allow all progs,
@@ -571,6 +587,12 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
}
}
+ if ((!f->any_glob && !f->file_glob && !f->prog_glob) ||
+ (f->any_glob && strcmp(f->any_glob, "") == 0)) {
+ fprintf(stderr, "Invalid filter: '%s'\n", str);
+ return -EINVAL;
+ }
+
*cnt += 1;
return 0;
}
diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh
index 685034528018..267f263172dd 100755
--- a/tools/testing/selftests/bpf/vmtest.sh
+++ b/tools/testing/selftests/bpf/vmtest.sh
@@ -4,9 +4,11 @@
set -u
set -e
-# This script currently only works for x86_64 and s390x, as
-# it is based on the VM image used by the BPF CI, which is
-# available only for these architectures.
+# This script currently only works for the following platforms,
+# as it is based on the VM image used by the BPF CI, which is
+# available only for these architectures. We can also specify
+# the local rootfs image generated by the following script:
+# https://github.com/libbpf/ci/blob/main/rootfs/mkrootfs_debian.sh
ARCH="$(uname -m)"
case "${ARCH}" in
s390x)
@@ -34,6 +36,7 @@ aarch64)
esac
DEFAULT_COMMAND="./test_progs"
MOUNT_DIR="mnt"
+LOCAL_ROOTFS_IMAGE=""
ROOTFS_IMAGE="root.img"
OUTPUT_DIR="$HOME/.bpf_selftests"
KCONFIG_REL_PATHS=("tools/testing/selftests/bpf/config" "tools/testing/selftests/bpf/config.${ARCH}")
@@ -67,6 +70,7 @@ or
Options:
+ -l) Specify the path to the local rootfs image.
-i) Update the rootfs image with a newer version.
-d) Update the output directory (default: ${OUTPUT_DIR})
-j) Number of jobs for compilation, similar to -j in make
@@ -90,19 +94,6 @@ populate_url_map()
fi
}
-download()
-{
- local file="$1"
-
- if [[ ! -v URLS[$file] ]]; then
- echo "$file not found" >&2
- return 1
- fi
-
- echo "Downloading $file..." >&2
- curl -Lsf "${URLS[$file]}" "${@:2}"
-}
-
newest_rootfs_version()
{
{
@@ -116,16 +107,34 @@ newest_rootfs_version()
download_rootfs()
{
- local rootfsversion="$1"
- local dir="$2"
+ populate_url_map
+
+ local rootfsversion="$(newest_rootfs_version)"
+ local file="${ARCH}/libbpf-vmtest-rootfs-$rootfsversion.tar.zst"
+
+ if [[ ! -v URLS[$file] ]]; then
+ echo "$file not found" >&2
+ return 1
+ fi
+
+ echo "Downloading $file..." >&2
+ curl -Lsf "${URLS[$file]}" "${@:2}"
+}
+
+load_rootfs()
+{
+ local dir="$1"
if ! which zstd &> /dev/null; then
echo 'Could not find "zstd" on the system, please install zstd'
exit 1
fi
- download "${ARCH}/libbpf-vmtest-rootfs-$rootfsversion.tar.zst" |
- zstd -d | sudo tar -C "$dir" -x
+ if [[ -n "${LOCAL_ROOTFS_IMAGE}" ]]; then
+ cat "${LOCAL_ROOTFS_IMAGE}" | zstd -d | sudo tar -C "$dir" -x
+ else
+ download_rootfs | zstd -d | sudo tar -C "$dir" -x
+ fi
}
recompile_kernel()
@@ -225,7 +234,7 @@ create_vm_image()
mkfs.ext4 -q "${rootfs_img}"
mount_image
- download_rootfs "$(newest_rootfs_version)" "${mount_dir}"
+ load_rootfs "${mount_dir}"
unmount_image
}
@@ -339,8 +348,11 @@ main()
local exit_command="poweroff -f"
local debug_shell="no"
- while getopts ':hskid:j:' opt; do
+ while getopts ':hskl:id:j:' opt; do
case ${opt} in
+ l)
+ LOCAL_ROOTFS_IMAGE="$OPTARG"
+ ;;
i)
update_image="yes"
;;
@@ -377,8 +389,17 @@ main()
if [[ $# -eq 0 && "${debug_shell}" == "no" ]]; then
echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
- else
- command="$@"
+ elif [[ $# -gt 0 ]]; then
+ # Quote each argument so the command survives into the guest: the
+ # host expands ${command} into the generated init script, which
+ # the guest bash then parses as shell source. Without the %q
+ # escapes an argument with a space or a glob (e.g. -a 'verifier_*')
+ # is re-split and expanded against /root/bpf there.
+ #
+ # Skip this when there is no command: printf '%q ' would still
+ # apply the format once and emit '', which is not the empty
+ # command that -s (debug shell) expects.
+ command=$(printf '%q ' "$@")
fi
local kconfig_file="${OUTPUT_DIR}/latest.config"
@@ -400,8 +421,6 @@ main()
make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"
fi
- populate_url_map
-
local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index c9fcbc6e5121..f64ec79d772e 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -117,8 +117,8 @@ TEST_FILES += va_high_addr_switch.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c
-$(TEST_GEN_FILES): vm_util.c
+$(TEST_GEN_PROGS): vm_util.c thp_settings.c
+$(TEST_GEN_FILES): vm_util.c thp_settings.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index 76d37904172d..0b7660c5f08f 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -32,7 +32,7 @@
static size_t pagesize;
static int pagemap_fd;
-static size_t thpsize;
+static size_t pmdsize;
static int nr_hugetlbsizes;
static size_t hugetlbsizes[10];
static int gup_fd;
@@ -78,9 +78,12 @@ struct comm_pipes {
static int setup_comm_pipes(struct comm_pipes *comm_pipes)
{
- if (pipe(comm_pipes->child_ready) < 0)
+ if (pipe(comm_pipes->child_ready) < 0) {
+ ksft_perror("pipe()");
return -errno;
+ }
if (pipe(comm_pipes->parent_ready) < 0) {
+ ksft_perror("pipe()");
close(comm_pipes->child_ready[0]);
close(comm_pipes->child_ready[1]);
return -errno;
@@ -165,7 +168,7 @@ static int child_vmsplice_memcmp_fn(char *mem, size_t size,
typedef int (*child_fn)(char *mem, size_t size, struct comm_pipes *comm_pipes);
static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,
- child_fn fn)
+ child_fn fn, bool xfail)
{
struct comm_pipes comm_pipes;
char buf;
@@ -173,13 +176,14 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,
ret = setup_comm_pipes(&comm_pipes);
if (ret) {
- ksft_test_result_fail("pipe() failed\n");
+ log_test_result(KSFT_FAIL);
return;
}
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
} else if (!ret) {
exit(fn(mem, size, &comm_pipes));
@@ -194,9 +198,18 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,
* write-faults by directly mapping pages writable.
*/
ret = mprotect(mem, size, PROT_READ);
- ret |= mprotect(mem, size, PROT_READ|PROT_WRITE);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
+ write(comm_pipes.parent_ready[1], "0", 1);
+ wait(&ret);
+ goto close_comm_pipes;
+ }
+
+ ret = mprotect(mem, size, PROT_READ|PROT_WRITE);
+ if (ret) {
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret);
goto close_comm_pipes;
@@ -213,33 +226,47 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,
else
ret = -EINVAL;
- ksft_test_result(!ret, "No leak from parent into child\n");
+ if (!ret) {
+ log_test_result(KSFT_PASS);
+ } else if (xfail) {
+ /*
+ * With hugetlb, some vmsplice() tests are currently expected to
+ * fail because (a) harder to fix and (b) nobody really cares.
+ * Flag them as expected failure for now.
+ */
+ log_test_result(KSFT_XFAIL);
+ } else {
+ log_test_result(KSFT_FAIL);
+ }
close_comm_pipes:
close_comm_pipes(&comm_pipes);
}
-static void test_cow_in_parent(char *mem, size_t size)
+static void test_cow_in_parent(char *mem, size_t size, bool is_hugetlb)
{
- do_test_cow_in_parent(mem, size, false, child_memcmp_fn);
+ do_test_cow_in_parent(mem, size, false, child_memcmp_fn, false);
}
-static void test_cow_in_parent_mprotect(char *mem, size_t size)
+static void test_cow_in_parent_mprotect(char *mem, size_t size, bool is_hugetlb)
{
- do_test_cow_in_parent(mem, size, true, child_memcmp_fn);
+ do_test_cow_in_parent(mem, size, true, child_memcmp_fn, false);
}
-static void test_vmsplice_in_child(char *mem, size_t size)
+static void test_vmsplice_in_child(char *mem, size_t size, bool is_hugetlb)
{
- do_test_cow_in_parent(mem, size, false, child_vmsplice_memcmp_fn);
+ do_test_cow_in_parent(mem, size, false, child_vmsplice_memcmp_fn,
+ is_hugetlb);
}
-static void test_vmsplice_in_child_mprotect(char *mem, size_t size)
+static void test_vmsplice_in_child_mprotect(char *mem, size_t size,
+ bool is_hugetlb)
{
- do_test_cow_in_parent(mem, size, true, child_vmsplice_memcmp_fn);
+ do_test_cow_in_parent(mem, size, true, child_vmsplice_memcmp_fn,
+ is_hugetlb);
}
static void do_test_vmsplice_in_parent(char *mem, size_t size,
- bool before_fork)
+ bool before_fork, bool xfail)
{
struct iovec iov = {
.iov_base = mem,
@@ -258,26 +285,29 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
ret = setup_comm_pipes(&comm_pipes);
if (ret) {
- ksft_test_result_fail("pipe() failed\n");
+ log_test_result(KSFT_FAIL);
goto free;
}
if (pipe(fds) < 0) {
- ksft_test_result_fail("pipe() failed\n");
+ ksft_perror("pipe() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
if (before_fork) {
transferred = vmsplice(fds[1], &iov, 1, 0);
if (transferred <= 0) {
- ksft_test_result_fail("vmsplice() failed\n");
+ ksft_print_msg("vmsplice() failed\n");
+ log_test_result(KSFT_FAIL);
goto close_pipe;
}
}
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed\n");
+ log_test_result(KSFT_FAIL);
goto close_pipe;
} else if (!ret) {
write(comm_pipes.child_ready[1], "0", 1);
@@ -291,7 +321,8 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
if (!before_fork) {
transferred = vmsplice(fds[1], &iov, 1, 0);
if (transferred <= 0) {
- ksft_test_result_fail("vmsplice() failed\n");
+ ksft_perror("vmsplice() failed");
+ log_test_result(KSFT_FAIL);
wait(&ret);
goto close_pipe;
}
@@ -300,7 +331,8 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
while (read(comm_pipes.child_ready[0], &buf, 1) != 1)
;
if (munmap(mem, size) < 0) {
- ksft_test_result_fail("munmap() failed\n");
+ ksft_perror("munmap() failed");
+ log_test_result(KSFT_FAIL);
goto close_pipe;
}
write(comm_pipes.parent_ready[1], "0", 1);
@@ -308,7 +340,8 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
/* Wait until the child is done writing. */
wait(&ret);
if (!WIFEXITED(ret)) {
- ksft_test_result_fail("wait() failed\n");
+ ksft_perror("wait() failed");
+ log_test_result(KSFT_FAIL);
goto close_pipe;
}
@@ -316,13 +349,24 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
for (total = 0; total < transferred; total += cur) {
cur = read(fds[0], new + total, transferred - total);
if (cur < 0) {
- ksft_test_result_fail("read() failed\n");
+ ksft_perror("read() failed");
+ log_test_result(KSFT_FAIL);
goto close_pipe;
}
}
- ksft_test_result(!memcmp(old, new, transferred),
- "No leak from child into parent\n");
+ if (!memcmp(old, new, transferred)) {
+ log_test_result(KSFT_PASS);
+ } else if (xfail) {
+ /*
+ * With hugetlb, some vmsplice() tests are currently expected to
+ * fail because (a) harder to fix and (b) nobody really cares.
+ * Flag them as expected failure for now.
+ */
+ log_test_result(KSFT_XFAIL);
+ } else {
+ log_test_result(KSFT_FAIL);
+ }
close_pipe:
close(fds[0]);
close(fds[1]);
@@ -333,14 +377,14 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,
free(new);
}
-static void test_vmsplice_before_fork(char *mem, size_t size)
+static void test_vmsplice_before_fork(char *mem, size_t size, bool is_hugetlb)
{
- do_test_vmsplice_in_parent(mem, size, true);
+ do_test_vmsplice_in_parent(mem, size, true, is_hugetlb);
}
-static void test_vmsplice_after_fork(char *mem, size_t size)
+static void test_vmsplice_after_fork(char *mem, size_t size, bool is_hugetlb)
{
- do_test_vmsplice_in_parent(mem, size, false);
+ do_test_vmsplice_in_parent(mem, size, false, is_hugetlb);
}
#ifdef LOCAL_CONFIG_HAVE_LIBURING
@@ -358,13 +402,14 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
ret = setup_comm_pipes(&comm_pipes);
if (ret) {
- ksft_test_result_fail("pipe() failed\n");
+ log_test_result(KSFT_FAIL);
return;
}
file = tmpfile();
if (!file) {
- ksft_test_result_fail("tmpfile() failed\n");
+ ksft_perror("tmpfile() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
fd = fileno(file);
@@ -372,14 +417,16 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
tmp = malloc(size);
if (!tmp) {
- ksft_test_result_fail("malloc() failed\n");
+ ksft_print_msg("malloc() failed\n");
+ log_test_result(KSFT_FAIL);
goto close_file;
}
/* Skip on errors, as we might just lack kernel support. */
ret = io_uring_queue_init(1, &ring, 0);
if (ret < 0) {
- ksft_test_result_skip("io_uring_queue_init() failed\n");
+ ksft_print_msg("io_uring_queue_init() failed\n");
+ log_test_result(KSFT_SKIP);
goto free_tmp;
}
@@ -394,7 +441,8 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
iov.iov_len = size;
ret = io_uring_register_buffers(&ring, &iov, 1);
if (ret) {
- ksft_test_result_skip("io_uring_register_buffers() failed\n");
+ ksft_print_msg("io_uring_register_buffers() failed\n");
+ log_test_result(KSFT_SKIP);
goto queue_exit;
}
@@ -405,7 +453,8 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
*/
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed");
+ log_test_result(KSFT_FAIL);
goto unregister_buffers;
} else if (!ret) {
write(comm_pipes.child_ready[1], "0", 1);
@@ -425,10 +474,17 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
* if the page is mapped R/O vs. R/W).
*/
ret = mprotect(mem, size, PROT_READ);
+ if (ret) {
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
+ goto unregister_buffers;
+ }
+
clear_softdirty();
- ret |= mprotect(mem, size, PROT_READ | PROT_WRITE);
+ ret = mprotect(mem, size, PROT_READ | PROT_WRITE);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto unregister_buffers;
}
}
@@ -440,25 +496,29 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
memset(mem, 0xff, size);
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
- ksft_test_result_fail("io_uring_get_sqe() failed\n");
+ ksft_print_msg("io_uring_get_sqe() failed\n");
+ log_test_result(KSFT_FAIL);
goto quit_child;
}
io_uring_prep_write_fixed(sqe, fd, mem, size, 0, 0);
ret = io_uring_submit(&ring);
if (ret < 0) {
- ksft_test_result_fail("io_uring_submit() failed\n");
+ ksft_print_msg("io_uring_submit() failed\n");
+ log_test_result(KSFT_FAIL);
goto quit_child;
}
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
- ksft_test_result_fail("io_uring_wait_cqe() failed\n");
+ ksft_print_msg("io_uring_wait_cqe() failed\n");
+ log_test_result(KSFT_FAIL);
goto quit_child;
}
if (cqe->res != size) {
- ksft_test_result_fail("write_fixed failed\n");
+ ksft_print_msg("write_fixed failed\n");
+ log_test_result(KSFT_FAIL);
goto quit_child;
}
io_uring_cqe_seen(&ring, cqe);
@@ -468,15 +528,18 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
while (total < size) {
cur = pread(fd, tmp + total, size - total, total);
if (cur < 0) {
- ksft_test_result_fail("pread() failed\n");
+ ksft_print_msg("pread() failed\n");
+ log_test_result(KSFT_FAIL);
goto quit_child;
}
total += cur;
}
/* Finally, check if we read what we expected. */
- ksft_test_result(!memcmp(mem, tmp, size),
- "Longterm R/W pin is reliable\n");
+ if (!memcmp(mem, tmp, size))
+ log_test_result(KSFT_PASS);
+ else
+ log_test_result(KSFT_FAIL);
quit_child:
if (use_fork) {
@@ -495,12 +558,12 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork)
close_comm_pipes(&comm_pipes);
}
-static void test_iouring_ro(char *mem, size_t size)
+static void test_iouring_ro(char *mem, size_t size, bool is_hugetlb)
{
do_test_iouring(mem, size, false);
}
-static void test_iouring_fork(char *mem, size_t size)
+static void test_iouring_fork(char *mem, size_t size, bool is_hugetlb)
{
do_test_iouring(mem, size, true);
}
@@ -524,19 +587,21 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
int ret;
if (gup_fd < 0) {
- ksft_test_result_skip("gup_test not available\n");
+ ksft_print_msg("gup_test not available\n");
+ log_test_result(KSFT_SKIP);
return;
}
tmp = malloc(size);
if (!tmp) {
- ksft_test_result_fail("malloc() failed\n");
+ ksft_print_msg("malloc() failed\n");
+ log_test_result(KSFT_FAIL);
return;
}
ret = setup_comm_pipes(&comm_pipes);
if (ret) {
- ksft_test_result_fail("pipe() failed\n");
+ log_test_result(KSFT_FAIL);
goto free_tmp;
}
@@ -551,7 +616,8 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
*/
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
} else if (!ret) {
write(comm_pipes.child_ready[1], "0", 1);
@@ -588,7 +654,8 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
clear_softdirty();
ret |= mprotect(mem, size, PROT_READ | PROT_WRITE);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
break;
@@ -603,9 +670,11 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_START, &args);
if (ret) {
if (errno == EINVAL)
- ksft_test_result_skip("PIN_LONGTERM_TEST_START failed\n");
+ ret = KSFT_SKIP;
else
- ksft_test_result_fail("PIN_LONGTERM_TEST_START failed\n");
+ ret = KSFT_FAIL;
+ ksft_perror("PIN_LONGTERM_TEST_START failed");
+ log_test_result(ret);
goto wait;
}
@@ -618,22 +687,26 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
*/
tmp_val = (__u64)(uintptr_t)tmp;
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_READ, &tmp_val);
- if (ret)
- ksft_test_result_fail("PIN_LONGTERM_TEST_READ failed\n");
- else
- ksft_test_result(!memcmp(mem, tmp, size),
- "Longterm R/O pin is reliable\n");
+ if (ret) {
+ ksft_perror("PIN_LONGTERM_TEST_READ failed");
+ log_test_result(KSFT_FAIL);
+ } else {
+ if (!memcmp(mem, tmp, size))
+ log_test_result(KSFT_PASS);
+ else
+ log_test_result(KSFT_FAIL);
+ }
ret = ioctl(gup_fd, PIN_LONGTERM_TEST_STOP);
if (ret)
- ksft_print_msg("[INFO] PIN_LONGTERM_TEST_STOP failed\n");
+ ksft_perror("PIN_LONGTERM_TEST_STOP failed");
wait:
switch (test) {
case RO_PIN_TEST_SHARED:
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret);
if (!WIFEXITED(ret))
- ksft_print_msg("[INFO] wait() failed\n");
+ ksft_perror("wait() failed");
break;
default:
break;
@@ -644,37 +717,41 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test,
free(tmp);
}
-static void test_ro_pin_on_shared(char *mem, size_t size)
+static void test_ro_pin_on_shared(char *mem, size_t size, bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_SHARED, false);
}
-static void test_ro_fast_pin_on_shared(char *mem, size_t size)
+static void test_ro_fast_pin_on_shared(char *mem, size_t size, bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_SHARED, true);
}
-static void test_ro_pin_on_ro_previously_shared(char *mem, size_t size)
+static void test_ro_pin_on_ro_previously_shared(char *mem, size_t size,
+ bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_PREVIOUSLY_SHARED, false);
}
-static void test_ro_fast_pin_on_ro_previously_shared(char *mem, size_t size)
+static void test_ro_fast_pin_on_ro_previously_shared(char *mem, size_t size,
+ bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_PREVIOUSLY_SHARED, true);
}
-static void test_ro_pin_on_ro_exclusive(char *mem, size_t size)
+static void test_ro_pin_on_ro_exclusive(char *mem, size_t size,
+ bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_RO_EXCLUSIVE, false);
}
-static void test_ro_fast_pin_on_ro_exclusive(char *mem, size_t size)
+static void test_ro_fast_pin_on_ro_exclusive(char *mem, size_t size,
+ bool is_hugetlb)
{
do_test_ro_pin(mem, size, RO_PIN_TEST_RO_EXCLUSIVE, true);
}
-typedef void (*test_fn)(char *mem, size_t size);
+typedef void (*test_fn)(char *mem, size_t size, bool hugetlb);
static void do_run_with_base_page(test_fn fn, bool swapout)
{
@@ -684,14 +761,16 @@ static void do_run_with_base_page(test_fn fn, bool swapout)
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
return;
}
ret = madvise(mem, pagesize, MADV_NOHUGEPAGE);
/* Ignore if not around on a kernel. */
if (ret && errno != EINVAL) {
- ksft_test_result_fail("MADV_NOHUGEPAGE failed\n");
+ ksft_perror("MADV_NOHUGEPAGE failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -701,25 +780,26 @@ static void do_run_with_base_page(test_fn fn, bool swapout)
if (swapout) {
madvise(mem, pagesize, MADV_PAGEOUT);
if (!pagemap_is_swapped(pagemap_fd, mem)) {
- ksft_test_result_skip("MADV_PAGEOUT did not work, is swap enabled?\n");
+ ksft_print_msg("MADV_PAGEOUT did not work, is swap enabled?\n");
+ log_test_result(KSFT_SKIP);
goto munmap;
}
}
- fn(mem, pagesize);
+ fn(mem, pagesize, false);
munmap:
munmap(mem, pagesize);
}
static void run_with_base_page(test_fn fn, const char *desc)
{
- ksft_print_msg("[RUN] %s ... with base page\n", desc);
+ log_test_start("%s ... with base page", desc);
do_run_with_base_page(fn, false);
}
static void run_with_base_page_swap(test_fn fn, const char *desc)
{
- ksft_print_msg("[RUN] %s ... with swapped out base page\n", desc);
+ log_test_start("%s ... with swapped out base page", desc);
do_run_with_base_page(fn, true);
}
@@ -734,7 +814,7 @@ enum thp_run {
THP_RUN_PARTIAL_SHARED,
};
-static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
+static void do_run_with_thp(test_fn fn, enum thp_run thp_run, size_t thpsize)
{
char *mem, *mmap_mem, *tmp, *mremap_mem = MAP_FAILED;
size_t size, mmap_size, mremap_size;
@@ -745,7 +825,8 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
mmap_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mmap_mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
return;
}
@@ -754,17 +835,19 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
ret = madvise(mem, thpsize, MADV_HUGEPAGE);
if (ret) {
- ksft_test_result_fail("MADV_HUGEPAGE failed\n");
+ ksft_perror("MADV_HUGEPAGE failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
/*
- * Try to populate a THP. Touch the first sub-page and test if we get
- * another sub-page populated automatically.
+ * Try to populate a THP. Touch the first sub-page and test if
+ * we get the last sub-page populated automatically.
*/
mem[0] = 0;
- if (!pagemap_is_populated(pagemap_fd, mem + pagesize)) {
- ksft_test_result_skip("Did not get a THP populated\n");
+ if (!pagemap_is_populated(pagemap_fd, mem + thpsize - pagesize)) {
+ ksft_print_msg("Did not get a THP populated\n");
+ log_test_result(KSFT_SKIP);
goto munmap;
}
memset(mem, 0, thpsize);
@@ -773,21 +856,25 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
switch (thp_run) {
case THP_RUN_PMD:
case THP_RUN_PMD_SWAPOUT:
+ assert(thpsize == pmdsize);
break;
case THP_RUN_PTE:
case THP_RUN_PTE_SWAPOUT:
/*
* Trigger PTE-mapping the THP by temporarily mapping a single
- * subpage R/O.
+ * subpage R/O. This is a noop if the THP is not pmdsize (and
+ * therefore already PTE-mapped).
*/
ret = mprotect(mem + pagesize, pagesize, PROT_READ);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
ret = mprotect(mem + pagesize, pagesize, PROT_READ | PROT_WRITE);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
break;
@@ -799,7 +886,8 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
*/
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DONTNEED);
if (ret) {
- ksft_test_result_fail("MADV_DONTNEED failed\n");
+ ksft_perror("MADV_DONTNEED failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
size = pagesize;
@@ -813,13 +901,15 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
mremap_mem = mmap(NULL, mremap_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mremap_mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
tmp = mremap(mem + mremap_size, mremap_size, mremap_size,
MREMAP_MAYMOVE | MREMAP_FIXED, mremap_mem);
if (tmp != mremap_mem) {
- ksft_test_result_fail("mremap() failed\n");
+ ksft_perror("mremap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
size = mremap_size;
@@ -832,12 +922,14 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
*/
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DONTFORK);
if (ret) {
- ksft_test_result_fail("MADV_DONTFORK failed\n");
+ ksft_perror("MADV_DONTFORK failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
} else if (!ret) {
exit(0);
@@ -846,7 +938,8 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
/* Allow for sharing all pages again. */
ret = madvise(mem + pagesize, thpsize - pagesize, MADV_DOFORK);
if (ret) {
- ksft_test_result_fail("MADV_DOFORK failed\n");
+ ksft_perror("MADV_DOFORK failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
break;
@@ -860,7 +953,8 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
case THP_RUN_SINGLE_PTE_SWAPOUT:
madvise(mem, size, MADV_PAGEOUT);
if (!range_is_swapped(mem, size)) {
- ksft_test_result_skip("MADV_PAGEOUT did not work, is swap enabled?\n");
+ ksft_print_msg("MADV_PAGEOUT did not work, is swap enabled?\n");
+ log_test_result(KSFT_SKIP);
goto munmap;
}
break;
@@ -868,59 +962,67 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run)
break;
}
- fn(mem, size);
+ fn(mem, size, false);
munmap:
munmap(mmap_mem, mmap_size);
if (mremap_mem != MAP_FAILED)
munmap(mremap_mem, mremap_size);
}
-static void run_with_thp(test_fn fn, const char *desc)
+static void run_with_thp(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PMD);
+ log_test_start("%s ... with THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PMD, size);
}
-static void run_with_thp_swap(test_fn fn, const char *desc)
+static void run_with_thp_swap(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with swapped-out THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PMD_SWAPOUT);
+ log_test_start("%s ... with swapped-out THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PMD_SWAPOUT, size);
}
-static void run_with_pte_mapped_thp(test_fn fn, const char *desc)
+static void run_with_pte_mapped_thp(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with PTE-mapped THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PTE);
+ log_test_start("%s ... with PTE-mapped THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PTE, size);
}
-static void run_with_pte_mapped_thp_swap(test_fn fn, const char *desc)
+static void run_with_pte_mapped_thp_swap(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with swapped-out, PTE-mapped THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PTE_SWAPOUT);
+ log_test_start("%s ... with swapped-out, PTE-mapped THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PTE_SWAPOUT, size);
}
-static void run_with_single_pte_of_thp(test_fn fn, const char *desc)
+static void run_with_single_pte_of_thp(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with single PTE of THP\n", desc);
- do_run_with_thp(fn, THP_RUN_SINGLE_PTE);
+ log_test_start("%s ... with single PTE of THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_SINGLE_PTE, size);
}
-static void run_with_single_pte_of_thp_swap(test_fn fn, const char *desc)
+static void run_with_single_pte_of_thp_swap(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with single PTE of swapped-out THP\n", desc);
- do_run_with_thp(fn, THP_RUN_SINGLE_PTE_SWAPOUT);
+ log_test_start("%s ... with single PTE of swapped-out THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_SINGLE_PTE_SWAPOUT, size);
}
-static void run_with_partial_mremap_thp(test_fn fn, const char *desc)
+static void run_with_partial_mremap_thp(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with partially mremap()'ed THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PARTIAL_MREMAP);
+ log_test_start("%s ... with partially mremap()'ed THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PARTIAL_MREMAP, size);
}
-static void run_with_partial_shared_thp(test_fn fn, const char *desc)
+static void run_with_partial_shared_thp(test_fn fn, const char *desc, size_t size)
{
- ksft_print_msg("[RUN] %s ... with partially shared THP\n", desc);
- do_run_with_thp(fn, THP_RUN_PARTIAL_SHARED);
+ log_test_start("%s ... with partially shared THP (%zu kB)",
+ desc, size / 1024);
+ do_run_with_thp(fn, THP_RUN_PARTIAL_SHARED, size);
}
static void run_with_hugetlb(test_fn fn, const char *desc, size_t hugetlbsize)
@@ -928,14 +1030,15 @@ static void run_with_hugetlb(test_fn fn, const char *desc, size_t hugetlbsize)
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB;
char *mem, *dummy;
- ksft_print_msg("[RUN] %s ... with hugetlb (%zu kB)\n", desc,
+ log_test_start("%s ... with hugetlb (%zu kB)", desc,
hugetlbsize / 1024);
flags |= __builtin_ctzll(hugetlbsize) << MAP_HUGE_SHIFT;
mem = mmap(NULL, hugetlbsize, PROT_READ | PROT_WRITE, flags, -1, 0);
if (mem == MAP_FAILED) {
- ksft_test_result_skip("need more free huge pages\n");
+ ksft_perror("need more free huge pages");
+ log_test_result(KSFT_SKIP);
return;
}
@@ -948,12 +1051,13 @@ static void run_with_hugetlb(test_fn fn, const char *desc, size_t hugetlbsize)
*/
dummy = mmap(NULL, hugetlbsize, PROT_READ | PROT_WRITE, flags, -1, 0);
if (dummy == MAP_FAILED) {
- ksft_test_result_skip("need more free huge pages\n");
+ ksft_perror("need more free huge pages");
+ log_test_result(KSFT_SKIP);
goto munmap;
}
munmap(dummy, hugetlbsize);
- fn(mem, hugetlbsize);
+ fn(mem, hugetlbsize, true);
munmap:
munmap(mem, hugetlbsize);
}
@@ -992,7 +1096,7 @@ static const struct test_case anon_test_cases[] = {
*/
{
"vmsplice() + unmap in child",
- test_vmsplice_in_child
+ test_vmsplice_in_child,
},
/*
* vmsplice() test, but do an additional mprotect(PROT_READ)+
@@ -1000,7 +1104,7 @@ static const struct test_case anon_test_cases[] = {
*/
{
"vmsplice() + unmap in child with mprotect() optimization",
- test_vmsplice_in_child_mprotect
+ test_vmsplice_in_child_mprotect,
},
/*
* vmsplice() [R/O GUP] in parent before fork(), unmap in parent after
@@ -1091,15 +1195,15 @@ static void run_anon_test_case(struct test_case const *test_case)
run_with_base_page(test_case->fn, test_case->desc);
run_with_base_page_swap(test_case->fn, test_case->desc);
- if (thpsize) {
- run_with_thp(test_case->fn, test_case->desc);
- run_with_thp_swap(test_case->fn, test_case->desc);
- run_with_pte_mapped_thp(test_case->fn, test_case->desc);
- run_with_pte_mapped_thp_swap(test_case->fn, test_case->desc);
- run_with_single_pte_of_thp(test_case->fn, test_case->desc);
- run_with_single_pte_of_thp_swap(test_case->fn, test_case->desc);
- run_with_partial_mremap_thp(test_case->fn, test_case->desc);
- run_with_partial_shared_thp(test_case->fn, test_case->desc);
+ if (pmdsize) {
+ run_with_thp(test_case->fn, test_case->desc, pmdsize);
+ run_with_thp_swap(test_case->fn, test_case->desc, pmdsize);
+ run_with_pte_mapped_thp(test_case->fn, test_case->desc, pmdsize);
+ run_with_pte_mapped_thp_swap(test_case->fn, test_case->desc, pmdsize);
+ run_with_single_pte_of_thp(test_case->fn, test_case->desc, pmdsize);
+ run_with_single_pte_of_thp_swap(test_case->fn, test_case->desc, pmdsize);
+ run_with_partial_mremap_thp(test_case->fn, test_case->desc, pmdsize);
+ run_with_partial_shared_thp(test_case->fn, test_case->desc, pmdsize);
}
for (i = 0; i < nr_hugetlbsizes; i++)
run_with_hugetlb(test_case->fn, test_case->desc,
@@ -1120,7 +1224,7 @@ static int tests_per_anon_test_case(void)
{
int tests = 2 + nr_hugetlbsizes;
- if (thpsize)
+ if (pmdsize)
tests += 8;
return tests;
}
@@ -1141,7 +1245,7 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
ret = setup_comm_pipes(&comm_pipes);
if (ret) {
- ksft_test_result_fail("pipe() failed\n");
+ log_test_result(KSFT_FAIL);
return;
}
@@ -1151,12 +1255,14 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
*/
ret = mprotect(mem + pagesize, pagesize, PROT_READ);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
ret = mprotect(mem + pagesize, pagesize, PROT_READ | PROT_WRITE);
if (ret) {
- ksft_test_result_fail("mprotect() failed\n");
+ ksft_perror("mprotect() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
@@ -1165,8 +1271,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
/* Collapse before actually COW-sharing the page. */
ret = madvise(mem, size, MADV_COLLAPSE);
if (ret) {
- ksft_test_result_skip("MADV_COLLAPSE failed: %s\n",
- strerror(errno));
+ ksft_perror("MADV_COLLAPSE failed");
+ log_test_result(KSFT_SKIP);
goto close_comm_pipes;
}
break;
@@ -1177,7 +1283,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
/* Don't COW-share the upper part of the THP. */
ret = madvise(mem + size / 2, size / 2, MADV_DONTFORK);
if (ret) {
- ksft_test_result_fail("MADV_DONTFORK failed\n");
+ ksft_perror("MADV_DONTFORK failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
break;
@@ -1185,7 +1292,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
/* Don't COW-share the lower part of the THP. */
ret = madvise(mem, size / 2, MADV_DONTFORK);
if (ret) {
- ksft_test_result_fail("MADV_DONTFORK failed\n");
+ ksft_perror("MADV_DONTFORK failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
}
break;
@@ -1195,7 +1303,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
ret = fork();
if (ret < 0) {
- ksft_test_result_fail("fork() failed\n");
+ ksft_perror("fork() failed");
+ log_test_result(KSFT_FAIL);
goto close_comm_pipes;
} else if (!ret) {
switch (test) {
@@ -1229,7 +1338,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
*/
ret = madvise(mem, size, MADV_DOFORK);
if (ret) {
- ksft_test_result_fail("MADV_DOFORK failed\n");
+ ksft_perror("MADV_DOFORK failed");
+ log_test_result(KSFT_FAIL);
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret);
goto close_comm_pipes;
@@ -1239,8 +1349,8 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
/* Collapse before anyone modified the COW-shared page. */
ret = madvise(mem, size, MADV_COLLAPSE);
if (ret) {
- ksft_test_result_skip("MADV_COLLAPSE failed: %s\n",
- strerror(errno));
+ ksft_perror("MADV_COLLAPSE failed");
+ log_test_result(KSFT_SKIP);
write(comm_pipes.parent_ready[1], "0", 1);
wait(&ret);
goto close_comm_pipes;
@@ -1260,28 +1370,39 @@ static void do_test_anon_thp_collapse(char *mem, size_t size,
else
ret = -EINVAL;
- ksft_test_result(!ret, "No leak from parent into child\n");
+ if (!ret)
+ log_test_result(KSFT_PASS);
+ else
+ log_test_result(KSFT_FAIL);
close_comm_pipes:
close_comm_pipes(&comm_pipes);
}
-static void test_anon_thp_collapse_unshared(char *mem, size_t size)
+static void test_anon_thp_collapse_unshared(char *mem, size_t size,
+ bool is_hugetlb)
{
+ assert(!is_hugetlb);
do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UNSHARED);
}
-static void test_anon_thp_collapse_fully_shared(char *mem, size_t size)
+static void test_anon_thp_collapse_fully_shared(char *mem, size_t size,
+ bool is_hugetlb)
{
+ assert(!is_hugetlb);
do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_FULLY_SHARED);
}
-static void test_anon_thp_collapse_lower_shared(char *mem, size_t size)
+static void test_anon_thp_collapse_lower_shared(char *mem, size_t size,
+ bool is_hugetlb)
{
+ assert(!is_hugetlb);
do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_LOWER_SHARED);
}
-static void test_anon_thp_collapse_upper_shared(char *mem, size_t size)
+static void test_anon_thp_collapse_upper_shared(char *mem, size_t size,
+ bool is_hugetlb)
{
+ assert(!is_hugetlb);
do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UPPER_SHARED);
}
@@ -1329,7 +1450,7 @@ static void run_anon_thp_test_cases(void)
{
int i;
- if (!thpsize)
+ if (!pmdsize)
return;
ksft_print_msg("[INFO] Anonymous THP tests\n");
@@ -1337,14 +1458,14 @@ static void run_anon_thp_test_cases(void)
for (i = 0; i < ARRAY_SIZE(anon_thp_test_cases); i++) {
struct test_case const *test_case = &anon_thp_test_cases[i];
- ksft_print_msg("[RUN] %s\n", test_case->desc);
- do_run_with_thp(test_case->fn, THP_RUN_PMD);
+ log_test_start("%s", test_case->desc);
+ do_run_with_thp(test_case->fn, THP_RUN_PMD, pmdsize);
}
}
static int tests_per_anon_thp_test_case(void)
{
- return thpsize ? 1 : 0;
+ return pmdsize ? 1 : 0;
}
typedef void (*non_anon_test_fn)(char *mem, const char *smem, size_t size);
@@ -1360,8 +1481,10 @@ static void test_cow(char *mem, const char *smem, size_t size)
memset(mem, 0xff, size);
/* See if we still read the old values via the other mapping. */
- ksft_test_result(!memcmp(smem, old, size),
- "Other mapping not modified\n");
+ if (!memcmp(smem, old, size))
+ log_test_result(KSFT_PASS);
+ else
+ log_test_result(KSFT_FAIL);
free(old);
}
@@ -1379,18 +1502,20 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc)
{
char *mem, *smem, tmp;
- ksft_print_msg("[RUN] %s ... with shared zeropage\n", desc);
+ log_test_start("%s ... with shared zeropage", desc);
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
return;
}
smem = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ if (smem == MAP_FAILED) {
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -1411,36 +1536,45 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc)
size_t mmap_size;
int ret;
- ksft_print_msg("[RUN] %s ... with huge zeropage\n", desc);
+ log_test_start("%s ... with huge zeropage", desc);
if (!has_huge_zeropage) {
- ksft_test_result_skip("Huge zeropage not enabled\n");
+ ksft_print_msg("Huge zeropage not enabled\n");
+ log_test_result(KSFT_SKIP);
return;
}
/* For alignment purposes, we need twice the thp size. */
- mmap_size = 2 * thpsize;
+ mmap_size = 2 * pmdsize;
mmap_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mmap_mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
return;
}
mmap_smem = mmap(NULL, mmap_size, PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mmap_smem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
/* We need a THP-aligned memory area. */
- mem = (char *)(((uintptr_t)mmap_mem + thpsize) & ~(thpsize - 1));
- smem = (char *)(((uintptr_t)mmap_smem + thpsize) & ~(thpsize - 1));
+ mem = (char *)(((uintptr_t)mmap_mem + pmdsize) & ~(pmdsize - 1));
+ smem = (char *)(((uintptr_t)mmap_smem + pmdsize) & ~(pmdsize - 1));
- ret = madvise(mem, thpsize, MADV_HUGEPAGE);
- ret |= madvise(smem, thpsize, MADV_HUGEPAGE);
- if (ret) {
- ksft_test_result_fail("MADV_HUGEPAGE failed\n");
+ ret = madvise(mem, pmdsize, MADV_HUGEPAGE);
+ if (ret != 0) {
+ ksft_perror("madvise()");
+ log_test_result(KSFT_FAIL);
+ goto munmap;
+ }
+ ret |= madvise(smem, pmdsize, MADV_HUGEPAGE);
+ if (ret != 0) {
+ ksft_perror("madvise()");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -1457,7 +1591,7 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc)
goto munmap;
}
- fn(mem, smem, thpsize);
+ fn(mem, smem, pmdsize);
munmap:
munmap(mmap_mem, mmap_size);
if (mmap_smem != MAP_FAILED)
@@ -1469,29 +1603,33 @@ static void run_with_memfd(non_anon_test_fn fn, const char *desc)
char *mem, *smem, tmp;
int fd;
- ksft_print_msg("[RUN] %s ... with memfd\n", desc);
+ log_test_start("%s ... with memfd", desc);
fd = memfd_create("test", 0);
if (fd < 0) {
- ksft_test_result_fail("memfd_create() failed\n");
+ ksft_perror("memfd_create() failed");
+ log_test_result(KSFT_FAIL);
return;
}
/* File consists of a single page filled with zeroes. */
if (fallocate(fd, 0, 0, pagesize)) {
- ksft_test_result_fail("fallocate() failed\n");
+ ksft_perror("fallocate() failed");
+ log_test_result(KSFT_FAIL);
goto close;
}
/* Create a private mapping of the memfd. */
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto close;
}
smem = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
- if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ if (smem == MAP_FAILED) {
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -1514,35 +1652,45 @@ static void run_with_tmpfile(non_anon_test_fn fn, const char *desc)
FILE *file;
int fd;
- ksft_print_msg("[RUN] %s ... with tmpfile\n", desc);
+ log_test_start("%s ... with tmpfile", desc);
file = tmpfile();
if (!file) {
- ksft_test_result_fail("tmpfile() failed\n");
+ ksft_perror("tmpfile() failed");
+ log_test_result(KSFT_FAIL);
return;
}
fd = fileno(file);
if (fd < 0) {
- ksft_test_result_skip("fileno() failed\n");
+ ksft_perror("fileno() failed");
+ log_test_result(KSFT_SKIP);
return;
}
/* File consists of a single page filled with zeroes. */
if (fallocate(fd, 0, 0, pagesize)) {
- ksft_test_result_fail("fallocate() failed\n");
+ if (errno == EOPNOTSUPP) {
+ ksft_print_msg("fallocate() not supported by filesystem\n");
+ log_test_result(KSFT_SKIP);
+ } else {
+ ksft_perror("fallocate() failed");
+ log_test_result(KSFT_FAIL);
+ }
goto close;
}
/* Create a private mapping of the memfd. */
mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto close;
}
smem = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
- if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ if (smem == MAP_FAILED) {
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -1566,20 +1714,22 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc,
char *mem, *smem, tmp;
int fd;
- ksft_print_msg("[RUN] %s ... with memfd hugetlb (%zu kB)\n", desc,
+ log_test_start("%s ... with memfd hugetlb (%zu kB)", desc,
hugetlbsize / 1024);
flags |= __builtin_ctzll(hugetlbsize) << MFD_HUGE_SHIFT;
fd = memfd_create("test", flags);
if (fd < 0) {
- ksft_test_result_skip("memfd_create() failed\n");
+ ksft_perror("memfd_create() failed");
+ log_test_result(KSFT_SKIP);
return;
}
/* File consists of a single page filled with zeroes. */
if (fallocate(fd, 0, 0, hugetlbsize)) {
- ksft_test_result_skip("need more free huge pages\n");
+ ksft_perror("need more free huge pages");
+ log_test_result(KSFT_SKIP);
goto close;
}
@@ -1587,12 +1737,14 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc,
mem = mmap(NULL, hugetlbsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd,
0);
if (mem == MAP_FAILED) {
- ksft_test_result_skip("need more free huge pages\n");
+ ksft_perror("need more free huge pages");
+ log_test_result(KSFT_SKIP);
goto close;
}
smem = mmap(NULL, hugetlbsize, PROT_READ, MAP_SHARED, fd, 0);
- if (mem == MAP_FAILED) {
- ksft_test_result_fail("mmap() failed\n");
+ if (smem == MAP_FAILED) {
+ ksft_perror("mmap() failed");
+ log_test_result(KSFT_FAIL);
goto munmap;
}
@@ -1603,7 +1755,7 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc,
fn(mem, smem, hugetlbsize);
munmap:
munmap(mem, hugetlbsize);
- if (mem != MAP_FAILED)
+ if (smem != MAP_FAILED)
munmap(smem, hugetlbsize);
close:
close(fd);
@@ -1650,7 +1802,7 @@ static void run_non_anon_test_case(struct non_anon_test_case const *test_case)
run_with_zeropage(test_case->fn, test_case->desc);
run_with_memfd(test_case->fn, test_case->desc);
run_with_tmpfile(test_case->fn, test_case->desc);
- if (thpsize)
+ if (pmdsize)
run_with_huge_zeropage(test_case->fn, test_case->desc);
for (i = 0; i < nr_hugetlbsizes; i++)
run_with_memfd_hugetlb(test_case->fn, test_case->desc,
@@ -1671,7 +1823,7 @@ static int tests_per_non_anon_test_case(void)
{
int tests = 3 + nr_hugetlbsizes;
- if (thpsize)
+ if (pmdsize)
tests += 1;
return tests;
}
@@ -1683,10 +1835,13 @@ int main(int argc, char **argv)
ksft_print_header();
pagesize = getpagesize();
- thpsize = read_pmd_pagesize();
- if (thpsize)
+ pmdsize = read_pmd_pagesize();
+ if (pmdsize) {
+ ksft_print_msg("[INFO] detected PMD size: %zu KiB\n",
+ pmdsize / 1024);
ksft_print_msg("[INFO] detected THP size: %zu KiB\n",
- thpsize / 1024);
+ pmdsize / 1024);
+ }
nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes,
ARRAY_SIZE(hugetlbsizes));
detect_huge_zeropage();
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 030667cb5533..25def60201fe 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -22,13 +22,14 @@
#include "linux/magic.h"
#include "vm_util.h"
+#include "thp_settings.h"
#define BASE_ADDR ((void *)(1UL << 30))
static unsigned long hpage_pmd_size;
static unsigned long page_size;
static int hpage_pmd_nr;
+static int anon_order;
-#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
#define PID_SMAPS "/proc/self/smaps"
#define TEST_FILE "collapse_test_file"
@@ -71,78 +72,7 @@ struct file_info {
};
static struct file_info finfo;
-
-enum thp_enabled {
- THP_ALWAYS,
- THP_MADVISE,
- THP_NEVER,
-};
-
-static const char *thp_enabled_strings[] = {
- "always",
- "madvise",
- "never",
- NULL
-};
-
-enum thp_defrag {
- THP_DEFRAG_ALWAYS,
- THP_DEFRAG_DEFER,
- THP_DEFRAG_DEFER_MADVISE,
- THP_DEFRAG_MADVISE,
- THP_DEFRAG_NEVER,
-};
-
-static const char *thp_defrag_strings[] = {
- "always",
- "defer",
- "defer+madvise",
- "madvise",
- "never",
- NULL
-};
-
-enum shmem_enabled {
- SHMEM_ALWAYS,
- SHMEM_WITHIN_SIZE,
- SHMEM_ADVISE,
- SHMEM_NEVER,
- SHMEM_DENY,
- SHMEM_FORCE,
-};
-
-static const char *shmem_enabled_strings[] = {
- "always",
- "within_size",
- "advise",
- "never",
- "deny",
- "force",
- NULL
-};
-
-struct khugepaged_settings {
- bool defrag;
- unsigned int alloc_sleep_millisecs;
- unsigned int scan_sleep_millisecs;
- unsigned int max_ptes_none;
- unsigned int max_ptes_swap;
- unsigned int max_ptes_shared;
- unsigned long pages_to_scan;
-};
-
-struct settings {
- enum thp_enabled thp_enabled;
- enum thp_defrag thp_defrag;
- enum shmem_enabled shmem_enabled;
- bool use_zero_page;
- struct khugepaged_settings khugepaged;
- unsigned long read_ahead_kb;
-};
-
-static struct settings saved_settings;
static bool skip_settings_restore;
-
static int exit_status;
static void success(const char *msg)
@@ -161,260 +91,34 @@ static void skip(const char *msg)
printf(" \e[33m%s\e[0m\n", msg);
}
-static int read_file(const char *path, char *buf, size_t buflen)
-{
- int fd;
- ssize_t numread;
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return 0;
-
- numread = read(fd, buf, buflen - 1);
- if (numread < 1) {
- close(fd);
- return 0;
- }
-
- buf[numread] = '\0';
- close(fd);
-
- return (unsigned int) numread;
-}
-
-static int write_file(const char *path, const char *buf, size_t buflen)
-{
- int fd;
- ssize_t numwritten;
-
- fd = open(path, O_WRONLY);
- if (fd == -1) {
- printf("open(%s)\n", path);
- exit(EXIT_FAILURE);
- return 0;
- }
-
- numwritten = write(fd, buf, buflen - 1);
- close(fd);
- if (numwritten < 1) {
- printf("write(%s)\n", buf);
- exit(EXIT_FAILURE);
- return 0;
- }
-
- return (unsigned int) numwritten;
-}
-
-static int read_string(const char *name, const char *strings[])
-{
- char path[PATH_MAX];
- char buf[256];
- char *c;
- int ret;
-
- ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
- if (ret >= PATH_MAX) {
- printf("%s: Pathname is too long\n", __func__);
- exit(EXIT_FAILURE);
- }
-
- if (!read_file(path, buf, sizeof(buf))) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-
- c = strchr(buf, '[');
- if (!c) {
- printf("%s: Parse failure\n", __func__);
- exit(EXIT_FAILURE);
- }
-
- c++;
- memmove(buf, c, sizeof(buf) - (c - buf));
-
- c = strchr(buf, ']');
- if (!c) {
- printf("%s: Parse failure\n", __func__);
- exit(EXIT_FAILURE);
- }
- *c = '\0';
-
- ret = 0;
- while (strings[ret]) {
- if (!strcmp(strings[ret], buf))
- return ret;
- ret++;
- }
-
- printf("Failed to parse %s\n", name);
- exit(EXIT_FAILURE);
-}
-
-static void write_string(const char *name, const char *val)
-{
- char path[PATH_MAX];
- int ret;
-
- ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
- if (ret >= PATH_MAX) {
- printf("%s: Pathname is too long\n", __func__);
- exit(EXIT_FAILURE);
- }
-
- if (!write_file(path, val, strlen(val) + 1)) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-}
-
-static const unsigned long _read_num(const char *path)
+static void restore_settings_atexit(void)
{
- char buf[21];
-
- if (read_file(path, buf, sizeof(buf)) < 0) {
- perror("read_file(read_num)");
- exit(EXIT_FAILURE);
- }
-
- return strtoul(buf, NULL, 10);
-}
-
-static const unsigned long read_num(const char *name)
-{
- char path[PATH_MAX];
- int ret;
-
- ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
- if (ret >= PATH_MAX) {
- printf("%s: Pathname is too long\n", __func__);
- exit(EXIT_FAILURE);
- }
- return _read_num(path);
-}
-
-static void _write_num(const char *path, unsigned long num)
-{
- char buf[21];
-
- sprintf(buf, "%ld", num);
- if (!write_file(path, buf, strlen(buf) + 1)) {
- perror(path);
- exit(EXIT_FAILURE);
- }
-}
-
-static void write_num(const char *name, unsigned long num)
-{
- char path[PATH_MAX];
- int ret;
-
- ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
- if (ret >= PATH_MAX) {
- printf("%s: Pathname is too long\n", __func__);
- exit(EXIT_FAILURE);
- }
- _write_num(path, num);
-}
-
-static void write_settings(struct settings *settings)
-{
- struct khugepaged_settings *khugepaged = &settings->khugepaged;
-
- write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
- write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
- write_string("shmem_enabled",
- shmem_enabled_strings[settings->shmem_enabled]);
- write_num("use_zero_page", settings->use_zero_page);
-
- write_num("khugepaged/defrag", khugepaged->defrag);
- write_num("khugepaged/alloc_sleep_millisecs",
- khugepaged->alloc_sleep_millisecs);
- write_num("khugepaged/scan_sleep_millisecs",
- khugepaged->scan_sleep_millisecs);
- write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
- write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
- write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
- write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
-
- if (file_ops && finfo.type == VMA_FILE)
- _write_num(finfo.dev_queue_read_ahead_path,
- settings->read_ahead_kb);
-}
-
-#define MAX_SETTINGS_DEPTH 4
-static struct settings settings_stack[MAX_SETTINGS_DEPTH];
-static int settings_index;
-
-static struct settings *current_settings(void)
-{
- if (!settings_index) {
- printf("Fail: No settings set");
- exit(EXIT_FAILURE);
- }
- return settings_stack + settings_index - 1;
-}
+ if (skip_settings_restore)
+ return;
-static void push_settings(struct settings *settings)
-{
- if (settings_index >= MAX_SETTINGS_DEPTH) {
- printf("Fail: Settings stack exceeded");
- exit(EXIT_FAILURE);
- }
- settings_stack[settings_index++] = *settings;
- write_settings(current_settings());
-}
+ printf("Restore THP and khugepaged settings...");
+ thp_restore_settings();
+ success("OK");
-static void pop_settings(void)
-{
- if (settings_index <= 0) {
- printf("Fail: Settings stack empty");
- exit(EXIT_FAILURE);
- }
- --settings_index;
- write_settings(current_settings());
+ skip_settings_restore = true;
}
static void restore_settings(int sig)
{
- if (skip_settings_restore)
- goto out;
-
- printf("Restore THP and khugepaged settings...");
- write_settings(&saved_settings);
- success("OK");
- if (sig)
- exit(EXIT_FAILURE);
-out:
- exit(exit_status);
+ /* exit() will invoke the restore_settings_atexit handler. */
+ exit(sig ? EXIT_FAILURE : exit_status);
}
static void save_settings(void)
{
printf("Save THP and khugepaged settings...");
- saved_settings = (struct settings) {
- .thp_enabled = read_string("enabled", thp_enabled_strings),
- .thp_defrag = read_string("defrag", thp_defrag_strings),
- .shmem_enabled =
- read_string("shmem_enabled", shmem_enabled_strings),
- .use_zero_page = read_num("use_zero_page"),
- };
- saved_settings.khugepaged = (struct khugepaged_settings) {
- .defrag = read_num("khugepaged/defrag"),
- .alloc_sleep_millisecs =
- read_num("khugepaged/alloc_sleep_millisecs"),
- .scan_sleep_millisecs =
- read_num("khugepaged/scan_sleep_millisecs"),
- .max_ptes_none = read_num("khugepaged/max_ptes_none"),
- .max_ptes_swap = read_num("khugepaged/max_ptes_swap"),
- .max_ptes_shared = read_num("khugepaged/max_ptes_shared"),
- .pages_to_scan = read_num("khugepaged/pages_to_scan"),
- };
if (file_ops && finfo.type == VMA_FILE)
- saved_settings.read_ahead_kb =
- _read_num(finfo.dev_queue_read_ahead_path);
+ thp_set_read_ahead_path(finfo.dev_queue_read_ahead_path);
+ thp_save_settings();
success("OK");
+ atexit(restore_settings_atexit);
signal(SIGTERM, restore_settings);
signal(SIGINT, restore_settings);
signal(SIGHUP, restore_settings);
@@ -793,7 +497,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
struct mem_ops *ops, bool expect)
{
int ret;
- struct settings settings = *current_settings();
+ struct thp_settings settings = *thp_current_settings();
printf("%s...", msg);
@@ -803,7 +507,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
*/
settings.thp_enabled = THP_NEVER;
settings.shmem_enabled = SHMEM_NEVER;
- push_settings(&settings);
+ thp_push_settings(&settings);
/* Clear VM_NOHUGEPAGE */
madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE);
@@ -815,7 +519,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
else
success("OK");
- pop_settings();
+ thp_pop_settings();
}
static void madvise_collapse(const char *msg, char *p, int nr_hpages,
@@ -845,13 +549,13 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages,
madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE);
/* Wait until the second full_scan completed */
- full_scans = read_num("khugepaged/full_scans") + 2;
+ full_scans = thp_read_num("khugepaged/full_scans") + 2;
printf("%s...", msg);
while (timeout--) {
if (ops->check_huge(p, nr_hpages))
break;
- if (read_num("khugepaged/full_scans") >= full_scans)
+ if (thp_read_num("khugepaged/full_scans") >= full_scans)
break;
printf(".");
usleep(TICK);
@@ -904,13 +608,18 @@ static bool is_tmpfs(struct mem_ops *ops)
return ops == &__file_ops && finfo.type == VMA_SHMEM;
}
+static bool is_anon(struct mem_ops *ops)
+{
+ return ops == &__anon_ops;
+}
+
static void alloc_at_fault(void)
{
- struct settings settings = *current_settings();
+ struct thp_settings settings = *thp_current_settings();
char *p;
settings.thp_enabled = THP_ALWAYS;
- push_settings(&settings);
+ thp_push_settings(&settings);
p = alloc_mapping(1);
*p = 1;
@@ -920,7 +629,7 @@ static void alloc_at_fault(void)
else
fail("Fail");
- pop_settings();
+ thp_pop_settings();
madvise(p, page_size, MADV_DONTNEED);
printf("Split huge PMD on MADV_DONTNEED...");
@@ -968,11 +677,12 @@ static void collapse_single_pte_entry(struct collapse_context *c, struct mem_ops
static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *ops)
{
int max_ptes_none = hpage_pmd_nr / 2;
- struct settings settings = *current_settings();
+ struct thp_settings settings = *thp_current_settings();
void *p;
+ int fault_nr_pages = is_anon(ops) ? 1 << anon_order : 1;
settings.khugepaged.max_ptes_none = max_ptes_none;
- push_settings(&settings);
+ thp_push_settings(&settings);
p = ops->setup_area(1);
@@ -983,10 +693,10 @@ static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *o
goto skip;
}
- ops->fault(p, 0, (hpage_pmd_nr - max_ptes_none - 1) * page_size);
+ ops->fault(p, 0, (hpage_pmd_nr - max_ptes_none - fault_nr_pages) * page_size);
c->collapse("Maybe collapse with max_ptes_none exceeded", p, 1,
ops, !c->enforce_pte_scan_limits);
- validate_memory(p, 0, (hpage_pmd_nr - max_ptes_none - 1) * page_size);
+ validate_memory(p, 0, (hpage_pmd_nr - max_ptes_none - fault_nr_pages) * page_size);
if (c->enforce_pte_scan_limits) {
ops->fault(p, 0, (hpage_pmd_nr - max_ptes_none) * page_size);
@@ -997,7 +707,7 @@ static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *o
}
skip:
ops->cleanup_area(p, hpage_pmd_size);
- pop_settings();
+ thp_pop_settings();
}
static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_ops *ops)
@@ -1028,7 +738,7 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op
static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *ops)
{
- int max_ptes_swap = read_num("khugepaged/max_ptes_swap");
+ int max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap");
void *p;
p = ops->setup_area(1);
@@ -1245,11 +955,11 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o
fail("Fail");
ops->fault(p, 0, page_size);
- write_num("khugepaged/max_ptes_shared", hpage_pmd_nr - 1);
+ thp_write_num("khugepaged/max_ptes_shared", hpage_pmd_nr - 1);
c->collapse("Collapse PTE table full of compound pages in child",
p, 1, ops, true);
- write_num("khugepaged/max_ptes_shared",
- current_settings()->khugepaged.max_ptes_shared);
+ thp_write_num("khugepaged/max_ptes_shared",
+ thp_current_settings()->khugepaged.max_ptes_shared);
validate_memory(p, 0, hpage_pmd_size);
ops->cleanup_area(p, hpage_pmd_size);
@@ -1270,7 +980,7 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o
static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops *ops)
{
- int max_ptes_shared = read_num("khugepaged/max_ptes_shared");
+ int max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared");
int wstatus;
void *p;
@@ -1373,7 +1083,7 @@ static void madvise_retracted_page_tables(struct collapse_context *c,
static void usage(void)
{
- fprintf(stderr, "\nUsage: ./khugepaged <test type> [dir]\n\n");
+ fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] <test type> [dir]\n\n");
fprintf(stderr, "\t<test type>\t: <context>:<mem_type>\n");
fprintf(stderr, "\t<context>\t: [all|khugepaged|madvise]\n");
fprintf(stderr, "\t<mem_type>\t: [all|anon|file|shmem]\n");
@@ -1382,15 +1092,34 @@ static void usage(void)
fprintf(stderr, "\tCONFIG_READ_ONLY_THP_FOR_FS=y\n");
fprintf(stderr, "\n\tif [dir] is a (sub)directory of a tmpfs mount, tmpfs must be\n");
fprintf(stderr, "\tmounted with huge=madvise option for khugepaged tests to work\n");
+ fprintf(stderr, "\n\tSupported Options:\n");
+ fprintf(stderr, "\t\t-h: This help message.\n");
+ fprintf(stderr, "\t\t-s: mTHP size, expressed as page order.\n");
+ fprintf(stderr, "\t\t Defaults to 0. Use this size for anon or shmem allocations.\n");
exit(1);
}
-static void parse_test_type(int argc, const char **argv)
+static void parse_test_type(int argc, char **argv)
{
+ int opt;
char *buf;
const char *token;
- if (argc == 1) {
+ while ((opt = getopt(argc, argv, "s:h")) != -1) {
+ switch (opt) {
+ case 's':
+ anon_order = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0) {
/* Backwards compatibility */
khugepaged_context = &__khugepaged_context;
madvise_context = &__madvise_context;
@@ -1398,7 +1127,7 @@ static void parse_test_type(int argc, const char **argv)
return;
}
- buf = strdup(argv[1]);
+ buf = strdup(argv[0]);
token = strsep(&buf, ":");
if (!strcmp(token, "all")) {
@@ -1432,13 +1161,16 @@ static void parse_test_type(int argc, const char **argv)
if (!file_ops)
return;
- if (argc != 3)
+ if (argc != 2)
usage();
+
+ get_finfo(argv[1]);
}
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
{
- struct settings default_settings = {
+ int hpage_pmd_order;
+ struct thp_settings default_settings = {
.thp_enabled = THP_MADVISE,
.thp_defrag = THP_DEFRAG_ALWAYS,
.shmem_enabled = SHMEM_ADVISE,
@@ -1458,10 +1190,12 @@ int main(int argc, const char **argv)
.read_ahead_kb = 0,
};
- parse_test_type(argc, argv);
+ if (!thp_is_enabled()) {
+ printf("Transparent Hugepages not available\n");
+ return KSFT_SKIP;
+ }
- if (file_ops)
- get_finfo(argv[2]);
+ parse_test_type(argc, argv);
setbuf(stdout, NULL);
@@ -1472,14 +1206,19 @@ int main(int argc, const char **argv)
exit(EXIT_FAILURE);
}
hpage_pmd_nr = hpage_pmd_size / page_size;
+ hpage_pmd_order = __builtin_ctz(hpage_pmd_nr);
default_settings.khugepaged.max_ptes_none = hpage_pmd_nr - 1;
default_settings.khugepaged.max_ptes_swap = hpage_pmd_nr / 8;
default_settings.khugepaged.max_ptes_shared = hpage_pmd_nr / 2;
default_settings.khugepaged.pages_to_scan = hpage_pmd_nr * 8;
+ default_settings.hugepages[hpage_pmd_order].enabled = THP_INHERIT;
+ default_settings.hugepages[anon_order].enabled = THP_ALWAYS;
+ default_settings.shmem_hugepages[hpage_pmd_order].enabled = SHMEM_INHERIT;
+ default_settings.shmem_hugepages[anon_order].enabled = SHMEM_ALWAYS;
save_settings();
- push_settings(&default_settings);
+ thp_push_settings(&default_settings);
alloc_at_fault();
diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c
index b748c48908d9..ecb4b1057c6b 100644
--- a/tools/testing/selftests/mm/ksm_tests.c
+++ b/tools/testing/selftests/mm/ksm_tests.c
@@ -15,6 +15,7 @@
#include "../kselftest.h"
#include <include/vdso/time64.h>
#include "vm_util.h"
+#include "thp_settings.h"
#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
#define KSM_FP(s) (KSM_SYSFS_PATH s)
@@ -202,12 +203,12 @@ static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_
void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0);
if (!map_ptr) {
- perror("mmap");
+ ksft_perror("mmap");
return NULL;
}
memset(map_ptr, data, map_size);
if (mprotect(map_ptr, map_size, prot)) {
- perror("mprotect");
+ ksft_perror("mprotect");
munmap(map_ptr, map_size);
return NULL;
}
@@ -228,11 +229,11 @@ static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout)
if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan))
return 1;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return 1;
}
if ((cur_time.tv_sec - start_time.tv_sec) > timeout) {
- printf("Scan time limit exceeded\n");
+ ksft_print_msg("Scan time limit exceeded\n");
return 1;
}
}
@@ -245,12 +246,12 @@ static int ksm_merge_pages(int merge_type, void *addr, size_t size,
{
if (merge_type == KSM_MERGE_MADVISE) {
if (madvise(addr, size, MADV_MERGEABLE)) {
- perror("madvise");
+ ksft_perror("madvise");
return 1;
}
} else if (merge_type == KSM_MERGE_PRCTL) {
if (prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0)) {
- perror("prctl");
+ ksft_perror("prctl");
return 1;
}
}
@@ -269,7 +270,7 @@ static int ksm_unmerge_pages(void *addr, size_t size,
struct timespec start_time, int timeout)
{
if (madvise(addr, size, MADV_UNMERGEABLE)) {
- perror("madvise");
+ ksft_perror("madvise");
return 1;
}
return 0;
@@ -315,8 +316,8 @@ static bool assert_ksm_pages_count(long dupl_page_count)
static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
{
if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) ||
- numa_available() ? 0 :
- ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) ||
+ (numa_available() ? 0 :
+ ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes)) ||
ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) ||
ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) ||
ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) ||
@@ -331,8 +332,8 @@ static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
static int ksm_restore(struct ksm_sysfs *ksm_sysfs)
{
if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) ||
- numa_available() ? 0 :
- ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) ||
+ (numa_available() ? 0 :
+ ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes)) ||
ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) ||
ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) ||
ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) ||
@@ -351,7 +352,7 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
struct timespec start_time;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
@@ -365,7 +366,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
/* verify that the right number of pages are merged */
if (assert_ksm_pages_count(page_count)) {
- printf("OK\n");
munmap(map_ptr, page_size * page_count);
if (merge_type == KSM_MERGE_PRCTL)
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
@@ -373,7 +373,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
}
err_out:
- printf("Not OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_FAIL;
}
@@ -385,7 +384,7 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout,
int page_count = 2;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
@@ -407,13 +406,11 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout,
/* check that unmerging was successful and 0 pages are currently merged */
if (assert_ksm_pages_count(0)) {
- printf("OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_PASS;
}
err_out:
- printf("Not OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_FAIL;
}
@@ -425,7 +422,7 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long
struct timespec start_time;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
@@ -452,12 +449,10 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long
else if (!use_zero_pages && !assert_ksm_pages_count(page_count))
goto err_out;
- printf("OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_PASS;
err_out:
- printf("Not OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_FAIL;
}
@@ -473,9 +468,9 @@ static int get_next_mem_node(int node)
mem_node = i % (max_node + 1);
node_size = numa_node_size(mem_node, NULL);
if (node_size > 0)
- break;
+ return mem_node;
}
- return mem_node;
+ return -ENODEV;
}
static int get_first_mem_node(void)
@@ -488,31 +483,33 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
{
void *numa1_map_ptr, *numa2_map_ptr;
struct timespec start_time;
+ int first_node, second_node;
int page_count = 2;
- int first_node;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
if (numa_available() < 0) {
- perror("NUMA support not enabled");
+ ksft_print_msg("NUMA support not enabled\n");
return KSFT_SKIP;
}
- if (numa_num_configured_nodes() <= 1) {
- printf("At least 2 NUMA nodes must be available\n");
+ first_node = get_first_mem_node();
+ second_node = get_next_mem_node(first_node);
+
+ if (second_node < 0) {
+ ksft_print_msg("At least 2 NUMA nodes with memory must be available\n");
return KSFT_SKIP;
}
if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
return KSFT_FAIL;
/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
- first_node = get_first_mem_node();
numa1_map_ptr = numa_alloc_onnode(page_size, first_node);
- numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node));
+ numa2_map_ptr = numa_alloc_onnode(page_size, second_node);
if (!numa1_map_ptr || !numa2_map_ptr) {
- perror("numa_alloc_onnode");
+ ksft_perror("numa_alloc_onnode");
return KSFT_FAIL;
}
@@ -537,13 +534,11 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
numa_free(numa1_map_ptr, page_size);
numa_free(numa2_map_ptr, page_size);
- printf("OK\n");
return KSFT_PASS;
err_out:
numa_free(numa1_map_ptr, page_size);
numa_free(numa2_map_ptr, page_size);
- printf("Not OK\n");
return KSFT_FAIL;
}
@@ -555,6 +550,11 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot,
unsigned long scan_time_ns;
int pagemap_fd, n_normal_pages, n_huge_pages;
+ if (!thp_is_enabled()) {
+ ksft_print_msg("Transparent Hugepages not available\n");
+ return KSFT_SKIP;
+ }
+
map_size *= MB;
size_t len = map_size;
@@ -581,36 +581,35 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot,
else
n_huge_pages++;
}
- printf("Number of normal pages: %d\n", n_normal_pages);
- printf("Number of huge pages: %d\n", n_huge_pages);
+ ksft_print_msg("Number of normal pages: %d\n", n_normal_pages);
+ ksft_print_msg("Number of huge pages: %d\n", n_huge_pages);
memset(map_ptr, '*', len);
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
- printf("Total size: %lu MiB\n", map_size / MB);
- printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+ ksft_print_msg("Total size: %lu MiB\n", map_size / MB);
+ ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC);
- printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
+ ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));
munmap(map_ptr_orig, len + HPAGE_SIZE);
return KSFT_PASS;
err_out:
- printf("Not OK\n");
munmap(map_ptr_orig, len + HPAGE_SIZE);
return KSFT_FAIL;
}
@@ -628,30 +627,29 @@ static int ksm_merge_time(int merge_type, int mapping, int prot, int timeout, si
return KSFT_FAIL;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
- printf("Total size: %lu MiB\n", map_size / MB);
- printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+ ksft_print_msg("Total size: %lu MiB\n", map_size / MB);
+ ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC);
- printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
+ ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));
munmap(map_ptr, map_size);
return KSFT_PASS;
err_out:
- printf("Not OK\n");
munmap(map_ptr, map_size);
return KSFT_FAIL;
}
@@ -668,37 +666,36 @@ static int ksm_unmerge_time(int merge_type, int mapping, int prot, int timeout,
if (!map_ptr)
return KSFT_FAIL;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
- printf("Total size: %lu MiB\n", map_size / MB);
- printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+ ksft_print_msg("Total size: %lu MiB\n", map_size / MB);
+ ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC);
- printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
+ ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));
munmap(map_ptr, map_size);
return KSFT_PASS;
err_out:
- printf("Not OK\n");
munmap(map_ptr, map_size);
return KSFT_FAIL;
}
@@ -717,24 +714,24 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size
return KSFT_FAIL;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
for (size_t i = 0; i < page_count - 1; i = i + 2)
memset(map_ptr + page_size * i, '-', 1);
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
return KSFT_FAIL;
}
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
- printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
- printf("Not merged pages:\n");
- printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
+ ksft_print_msg("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
+ ksft_print_msg("Not merged pages:\n");
+ ksft_print_msg("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
cow_time_ns % NSEC_PER_SEC);
- printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
+ ksft_print_msg("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
((double)cow_time_ns / NSEC_PER_SEC));
/* Create 2000 pairs of duplicate pages */
@@ -746,30 +743,29 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
for (size_t i = 0; i < page_count - 1; i = i + 2)
memset(map_ptr + page_size * i, '-', 1);
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
- perror("clock_gettime");
+ ksft_perror("clock_gettime");
goto err_out;
}
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
- printf("Merged pages:\n");
- printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
+ ksft_print_msg("Merged pages:\n");
+ ksft_print_msg("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
cow_time_ns % NSEC_PER_SEC);
- printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
+ ksft_print_msg("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
((double)cow_time_ns / NSEC_PER_SEC));
munmap(map_ptr, page_size * page_count);
return KSFT_PASS;
err_out:
- printf("Not OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_FAIL;
}
@@ -787,6 +783,10 @@ int main(int argc, char *argv[])
bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
long size_MB = 0;
+ const char *test_descr = "KSM merging";
+
+ ksft_print_header();
+ ksft_set_plan(1);
while ((opt = getopt(argc, argv, "dha:p:l:z:m:s:t:MUZNPCHD")) != -1) {
switch (opt) {
@@ -795,17 +795,13 @@ int main(int argc, char *argv[])
break;
case 'p':
page_count = atol(optarg);
- if (page_count <= 0) {
- printf("The number of pages must be greater than 0\n");
- return KSFT_FAIL;
- }
+ if (page_count <= 0)
+ ksft_exit_fail_msg("The number of pages must be greater than 0\n");
break;
case 'l':
ksm_scan_limit_sec = atoi(optarg);
- if (ksm_scan_limit_sec <= 0) {
- printf("Timeout value must be greater than 0\n");
- return KSFT_FAIL;
- }
+ if (ksm_scan_limit_sec <= 0)
+ ksft_exit_fail_msg("Timeout value must be greater than 0\n");
break;
case 'h':
print_help();
@@ -827,19 +823,15 @@ int main(int argc, char *argv[])
break;
case 's':
size_MB = atoi(optarg);
- if (size_MB <= 0) {
- printf("Size must be greater than 0\n");
- return KSFT_FAIL;
- }
+ if (size_MB <= 0)
+ ksft_exit_fail_msg("Size must be greater than 0\n");
break;
case 't':
{
int tmp = atoi(optarg);
- if (tmp < 0 || tmp > KSM_MERGE_LAST) {
- printf("Invalid merge type\n");
- return KSFT_FAIL;
- }
+ if (tmp < 0 || tmp > KSM_MERGE_LAST)
+ ksft_exit_fail_msg("Invalid merge type\n");
merge_type = tmp;
}
break;
@@ -867,82 +859,80 @@ int main(int argc, char *argv[])
test_name = KSM_COW_TIME;
break;
default:
- return KSFT_FAIL;
+ ksft_exit_fail_msg("Unknown option\n");
}
}
if (prot == 0)
prot = str_to_prot(KSM_PROT_STR_DEFAULT);
- if (access(KSM_SYSFS_PATH, F_OK)) {
- printf("Config KSM not enabled\n");
- return KSFT_SKIP;
- }
+ if (access(KSM_SYSFS_PATH, F_OK))
+ ksft_exit_skip("Config KSM not enabled\n");
- if (ksm_save_def(&ksm_sysfs_old)) {
- printf("Cannot save default tunables\n");
- return KSFT_FAIL;
- }
+ if (ksm_save_def(&ksm_sysfs_old))
+ ksft_exit_fail_msg("Cannot save default tunables\n");
if (ksm_write_sysfs(KSM_FP("run"), 2) ||
ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) ||
- numa_available() ? 0 :
- ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) ||
+ (numa_available() ? 0 :
+ ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1)) ||
ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
- return KSFT_FAIL;
+ ksft_exit_fail_msg("Cannot set up KSM tunables\n");
switch (test_name) {
case CHECK_KSM_MERGE:
+ test_descr = "KSM merging";
ret = check_ksm_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
ksm_scan_limit_sec, page_size);
break;
case CHECK_KSM_UNMERGE:
+ test_descr = "KSM unmerging";
ret = check_ksm_unmerge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, page_size);
break;
case CHECK_KSM_ZERO_PAGE_MERGE:
+ test_descr = "KSM zero page merging";
ret = check_ksm_zero_page_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
page_count, ksm_scan_limit_sec, use_zero_pages,
page_size);
break;
case CHECK_KSM_NUMA_MERGE:
+ test_descr = "KSM NUMA merging";
ret = check_ksm_numa_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, merge_across_nodes, page_size);
break;
case KSM_MERGE_TIME:
- if (size_MB == 0) {
- printf("Option '-s' is required.\n");
- return KSFT_FAIL;
- }
+ if (size_MB == 0)
+ ksft_exit_fail_msg("Option '-s' is required\n");
+ test_descr = "KSM merge time";
ret = ksm_merge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, size_MB);
break;
case KSM_MERGE_TIME_HUGE_PAGES:
- if (size_MB == 0) {
- printf("Option '-s' is required.\n");
- return KSFT_FAIL;
- }
+ if (size_MB == 0)
+ ksft_exit_fail_msg("Option '-s' is required\n");
+ test_descr = "KSM merge time with huge pages";
ret = ksm_merge_hugepages_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, size_MB);
break;
case KSM_UNMERGE_TIME:
- if (size_MB == 0) {
- printf("Option '-s' is required.\n");
- return KSFT_FAIL;
- }
+ if (size_MB == 0)
+ ksft_exit_fail_msg("Option '-s' is required\n");
+ test_descr = "KSM unmerge time";
ret = ksm_unmerge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, size_MB);
break;
case KSM_COW_TIME:
+ test_descr = "KSM COW time";
ret = ksm_cow_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
ksm_scan_limit_sec, page_size);
break;
}
- if (ksm_restore(&ksm_sysfs_old)) {
- printf("Cannot restore default tunables\n");
- return KSFT_FAIL;
- }
+ if (ksm_restore(&ksm_sysfs_old))
+ ksft_print_msg("Cannot restore default tunables\n");
+
+ ksft_test_result_report(ret, "%s\n", test_descr);
- return ret;
+ ksft_finished();
}
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index 877ee32d04c8..0faeead0113b 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -5,6 +5,8 @@
*/
#include "../kselftest_harness.h"
+#include "thp_settings.h"
+
#include <strings.h>
#include <pthread.h>
#include <numa.h>
@@ -181,6 +183,9 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
uint64_t *ptr;
int i;
+ if (!thp_is_enabled())
+ SKIP(return, "Transparent Hugepages not available");
+
if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
SKIP(return, "Not enough threads or NUMA nodes available");
@@ -200,4 +205,106 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
}
+/*
+ * migration test with shared anon THP page
+ */
+
+TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME)
+{
+ pid_t pid;
+ uint64_t *ptr;
+ int i;
+
+ if (!thp_is_enabled())
+ SKIP(return, "Transparent Hugepages not available");
+
+ if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+ SKIP(return, "Not enough threads or NUMA nodes available");
+
+ ptr = mmap(NULL, 2 * TWOMEG, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+
+ ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
+ ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
+
+ memset(ptr, 0xde, TWOMEG);
+ for (i = 0; i < self->nthreads - 1; i++) {
+ pid = fork();
+ if (!pid) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ /* Parent may have died before prctl so check now. */
+ if (getppid() == 1)
+ kill(getpid(), SIGHUP);
+ access_mem(ptr);
+ } else {
+ self->pids[i] = pid;
+ }
+ }
+
+ ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+ for (i = 0; i < self->nthreads - 1; i++)
+ ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
+}
+
+/*
+ * migration test with private anon hugetlb page
+ */
+TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME)
+{
+ uint64_t *ptr;
+ int i;
+
+ if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+ SKIP(return, "Not enough threads or NUMA nodes available");
+
+ ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+
+ memset(ptr, 0xde, TWOMEG);
+ for (i = 0; i < self->nthreads - 1; i++)
+ if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
+ perror("Couldn't create thread");
+
+ ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+ for (i = 0; i < self->nthreads - 1; i++)
+ ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
+}
+
+/*
+ * migration test with shared anon hugetlb page
+ */
+TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME)
+{
+ pid_t pid;
+ uint64_t *ptr;
+ int i;
+
+ if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+ SKIP(return, "Not enough threads or NUMA nodes available");
+
+ ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+
+ memset(ptr, 0xde, TWOMEG);
+ for (i = 0; i < self->nthreads - 1; i++) {
+ pid = fork();
+ if (!pid) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ /* Parent may have died before prctl so check now. */
+ if (getppid() == 1)
+ kill(getpid(), SIGHUP);
+ access_mem(ptr);
+ } else {
+ self->pids[i] = pid;
+ }
+ }
+
+ ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+ for (i = 0; i < self->nthreads - 1; i++)
+ ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index d7b2c9d07eec..8ec99d704d06 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -377,6 +377,8 @@ CATEGORY="cow" run_test ./cow
CATEGORY="thp" run_test ./khugepaged
+CATEGORY="thp" run_test ./khugepaged -s 2
+
CATEGORY="thp" run_test ./transhuge-stress -d 20
CATEGORY="thp" run_test ./split_huge_page_test
diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c
index 7095b95d19ae..bfc2cb06af4e 100644
--- a/tools/testing/selftests/mm/soft-dirty.c
+++ b/tools/testing/selftests/mm/soft-dirty.c
@@ -6,8 +6,10 @@
#include <stdint.h>
#include <malloc.h>
#include <sys/mman.h>
+
#include "../kselftest.h"
#include "vm_util.h"
+#include "thp_settings.h"
#define PAGEMAP_FILE_PATH "/proc/self/pagemap"
#define TEST_ITERATIONS 10000
@@ -78,8 +80,13 @@ static void test_hugepage(int pagemap_fd, int pagesize)
{
char *map;
int i, ret;
- size_t hpage_len = read_pmd_pagesize();
+ if (!thp_is_enabled()) {
+ ksft_test_result_skip("Transparent Hugepages not available\n");
+ return;
+ }
+
+ size_t hpage_len = read_pmd_pagesize();
if (!hpage_len)
ksft_exit_fail_msg("Reading PMD pagesize failed");
diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/thp_settings.c
new file mode 100644
index 000000000000..38f70155bfbf
--- /dev/null
+++ b/tools/testing/selftests/mm/thp_settings.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "thp_settings.h"
+
+#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
+#define MAX_SETTINGS_DEPTH 4
+static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
+static int settings_index;
+static struct thp_settings saved_settings;
+static char dev_queue_read_ahead_path[PATH_MAX];
+
+static const char * const thp_enabled_strings[] = {
+ "never",
+ "always",
+ "inherit",
+ "madvise",
+ NULL
+};
+
+static const char * const thp_defrag_strings[] = {
+ "always",
+ "defer",
+ "defer+madvise",
+ "madvise",
+ "never",
+ NULL
+};
+
+static const char * const shmem_enabled_strings[] = {
+ "never",
+ "always",
+ "within_size",
+ "advise",
+ "inherit",
+ "deny",
+ "force",
+ NULL
+};
+
+int read_file(const char *path, char *buf, size_t buflen)
+{
+ int fd;
+ ssize_t numread;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ close(fd);
+ return 0;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return (unsigned int) numread;
+}
+
+int write_file(const char *path, const char *buf, size_t buflen)
+{
+ int fd;
+ ssize_t numwritten;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1) {
+ printf("open(%s)\n", path);
+ exit(EXIT_FAILURE);
+ return 0;
+ }
+
+ numwritten = write(fd, buf, buflen - 1);
+ close(fd);
+ if (numwritten < 1) {
+ printf("write(%s)\n", buf);
+ exit(EXIT_FAILURE);
+ return 0;
+ }
+
+ return (unsigned int) numwritten;
+}
+
+const unsigned long read_num(const char *path)
+{
+ char buf[21];
+
+ if (read_file(path, buf, sizeof(buf)) < 0) {
+ perror("read_file()");
+ exit(EXIT_FAILURE);
+ }
+
+ return strtoul(buf, NULL, 10);
+}
+
+void write_num(const char *path, unsigned long num)
+{
+ char buf[21];
+
+ sprintf(buf, "%ld", num);
+ if (!write_file(path, buf, strlen(buf) + 1)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int thp_read_string(const char *name, const char * const strings[])
+{
+ char path[PATH_MAX];
+ char buf[256];
+ char *c;
+ int ret;
+
+ ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
+ if (ret >= PATH_MAX) {
+ printf("%s: Pathname is too long\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!read_file(path, buf, sizeof(buf))) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ c = strchr(buf, '[');
+ if (!c) {
+ printf("%s: Parse failure\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+
+ c++;
+ memmove(buf, c, sizeof(buf) - (c - buf));
+
+ c = strchr(buf, ']');
+ if (!c) {
+ printf("%s: Parse failure\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+ *c = '\0';
+
+ ret = 0;
+ while (strings[ret]) {
+ if (!strcmp(strings[ret], buf))
+ return ret;
+ ret++;
+ }
+
+ printf("Failed to parse %s\n", name);
+ exit(EXIT_FAILURE);
+}
+
+void thp_write_string(const char *name, const char *val)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
+ if (ret >= PATH_MAX) {
+ printf("%s: Pathname is too long\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!write_file(path, val, strlen(val) + 1)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+}
+
+const unsigned long thp_read_num(const char *name)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
+ if (ret >= PATH_MAX) {
+ printf("%s: Pathname is too long\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+ return read_num(path);
+}
+
+void thp_write_num(const char *name, unsigned long num)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
+ if (ret >= PATH_MAX) {
+ printf("%s: Pathname is too long\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+ write_num(path, num);
+}
+
+void thp_read_settings(struct thp_settings *settings)
+{
+ unsigned long orders = thp_supported_orders();
+ unsigned long shmem_orders = thp_shmem_supported_orders();
+ char path[PATH_MAX];
+ int i;
+
+ *settings = (struct thp_settings) {
+ .thp_enabled = thp_read_string("enabled", thp_enabled_strings),
+ .thp_defrag = thp_read_string("defrag", thp_defrag_strings),
+ .shmem_enabled =
+ thp_read_string("shmem_enabled", shmem_enabled_strings),
+ .use_zero_page = thp_read_num("use_zero_page"),
+ };
+ settings->khugepaged = (struct khugepaged_settings) {
+ .defrag = thp_read_num("khugepaged/defrag"),
+ .alloc_sleep_millisecs =
+ thp_read_num("khugepaged/alloc_sleep_millisecs"),
+ .scan_sleep_millisecs =
+ thp_read_num("khugepaged/scan_sleep_millisecs"),
+ .max_ptes_none = thp_read_num("khugepaged/max_ptes_none"),
+ .max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap"),
+ .max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"),
+ .pages_to_scan = thp_read_num("khugepaged/pages_to_scan"),
+ };
+ if (dev_queue_read_ahead_path[0])
+ settings->read_ahead_kb = read_num(dev_queue_read_ahead_path);
+
+ for (i = 0; i < NR_ORDERS; i++) {
+ if (!((1 << i) & orders)) {
+ settings->hugepages[i].enabled = THP_NEVER;
+ continue;
+ }
+ snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
+ (getpagesize() >> 10) << i);
+ settings->hugepages[i].enabled =
+ thp_read_string(path, thp_enabled_strings);
+ }
+
+ for (i = 0; i < NR_ORDERS; i++) {
+ if (!((1 << i) & shmem_orders)) {
+ settings->shmem_hugepages[i].enabled = SHMEM_NEVER;
+ continue;
+ }
+ snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled",
+ (getpagesize() >> 10) << i);
+ settings->shmem_hugepages[i].enabled =
+ thp_read_string(path, shmem_enabled_strings);
+ }
+}
+
+void thp_write_settings(struct thp_settings *settings)
+{
+ struct khugepaged_settings *khugepaged = &settings->khugepaged;
+ unsigned long orders = thp_supported_orders();
+ unsigned long shmem_orders = thp_shmem_supported_orders();
+ char path[PATH_MAX];
+ int enabled;
+ int i;
+
+ thp_write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
+ thp_write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
+ thp_write_string("shmem_enabled",
+ shmem_enabled_strings[settings->shmem_enabled]);
+ thp_write_num("use_zero_page", settings->use_zero_page);
+
+ thp_write_num("khugepaged/defrag", khugepaged->defrag);
+ thp_write_num("khugepaged/alloc_sleep_millisecs",
+ khugepaged->alloc_sleep_millisecs);
+ thp_write_num("khugepaged/scan_sleep_millisecs",
+ khugepaged->scan_sleep_millisecs);
+ thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
+ thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
+ thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
+ thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
+
+ if (dev_queue_read_ahead_path[0])
+ write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
+
+ for (i = 0; i < NR_ORDERS; i++) {
+ if (!((1 << i) & orders))
+ continue;
+ snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
+ (getpagesize() >> 10) << i);
+ enabled = settings->hugepages[i].enabled;
+ thp_write_string(path, thp_enabled_strings[enabled]);
+ }
+
+ for (i = 0; i < NR_ORDERS; i++) {
+ if (!((1 << i) & shmem_orders))
+ continue;
+ snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled",
+ (getpagesize() >> 10) << i);
+ enabled = settings->shmem_hugepages[i].enabled;
+ thp_write_string(path, shmem_enabled_strings[enabled]);
+ }
+}
+
+struct thp_settings *thp_current_settings(void)
+{
+ if (!settings_index) {
+ printf("Fail: No settings set");
+ exit(EXIT_FAILURE);
+ }
+ return settings_stack + settings_index - 1;
+}
+
+void thp_push_settings(struct thp_settings *settings)
+{
+ if (settings_index >= MAX_SETTINGS_DEPTH) {
+ printf("Fail: Settings stack exceeded");
+ exit(EXIT_FAILURE);
+ }
+ settings_stack[settings_index++] = *settings;
+ thp_write_settings(thp_current_settings());
+}
+
+void thp_pop_settings(void)
+{
+ if (settings_index <= 0) {
+ printf("Fail: Settings stack empty");
+ exit(EXIT_FAILURE);
+ }
+ --settings_index;
+ thp_write_settings(thp_current_settings());
+}
+
+void thp_restore_settings(void)
+{
+ thp_write_settings(&saved_settings);
+}
+
+void thp_save_settings(void)
+{
+ thp_read_settings(&saved_settings);
+}
+
+void thp_set_read_ahead_path(char *path)
+{
+ if (!path) {
+ dev_queue_read_ahead_path[0] = '\0';
+ return;
+ }
+
+ strncpy(dev_queue_read_ahead_path, path,
+ sizeof(dev_queue_read_ahead_path));
+ dev_queue_read_ahead_path[sizeof(dev_queue_read_ahead_path) - 1] = '\0';
+}
+
+static unsigned long __thp_supported_orders(bool is_shmem)
+{
+ unsigned long orders = 0;
+ char path[PATH_MAX];
+ char buf[256];
+ int ret, i;
+ char anon_dir[] = "enabled";
+ char shmem_dir[] = "shmem_enabled";
+
+ for (i = 0; i < NR_ORDERS; i++) {
+ ret = snprintf(path, PATH_MAX, THP_SYSFS "hugepages-%ukB/%s",
+ (getpagesize() >> 10) << i, is_shmem ? shmem_dir : anon_dir);
+ if (ret >= PATH_MAX) {
+ printf("%s: Pathname is too long\n", __func__);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret)
+ orders |= 1UL << i;
+ }
+
+ return orders;
+}
+
+unsigned long thp_supported_orders(void)
+{
+ return __thp_supported_orders(false);
+}
+
+unsigned long thp_shmem_supported_orders(void)
+{
+ return __thp_supported_orders(true);
+}
+
+bool thp_is_enabled(void)
+{
+ if (access(THP_SYSFS, F_OK) != 0)
+ return false;
+
+ int mode = thp_read_string("enabled", thp_enabled_strings);
+
+ /* THP is considered enabled if it's either "always" or "madvise" */
+ return mode == 1 || mode == 3;
+}
diff --git a/tools/testing/selftests/mm/thp_settings.h b/tools/testing/selftests/mm/thp_settings.h
new file mode 100644
index 000000000000..079a58a662b0
--- /dev/null
+++ b/tools/testing/selftests/mm/thp_settings.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __THP_SETTINGS_H__
+#define __THP_SETTINGS_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+enum thp_enabled {
+ THP_NEVER,
+ THP_ALWAYS,
+ THP_INHERIT,
+ THP_MADVISE,
+};
+
+enum thp_defrag {
+ THP_DEFRAG_ALWAYS,
+ THP_DEFRAG_DEFER,
+ THP_DEFRAG_DEFER_MADVISE,
+ THP_DEFRAG_MADVISE,
+ THP_DEFRAG_NEVER,
+};
+
+enum shmem_enabled {
+ SHMEM_NEVER,
+ SHMEM_ALWAYS,
+ SHMEM_WITHIN_SIZE,
+ SHMEM_ADVISE,
+ SHMEM_INHERIT,
+ SHMEM_DENY,
+ SHMEM_FORCE,
+};
+
+#define NR_ORDERS 20
+
+struct hugepages_settings {
+ enum thp_enabled enabled;
+};
+
+struct khugepaged_settings {
+ bool defrag;
+ unsigned int alloc_sleep_millisecs;
+ unsigned int scan_sleep_millisecs;
+ unsigned int max_ptes_none;
+ unsigned int max_ptes_swap;
+ unsigned int max_ptes_shared;
+ unsigned long pages_to_scan;
+};
+
+struct shmem_hugepages_settings {
+ enum shmem_enabled enabled;
+};
+
+struct thp_settings {
+ enum thp_enabled thp_enabled;
+ enum thp_defrag thp_defrag;
+ enum shmem_enabled shmem_enabled;
+ bool use_zero_page;
+ struct khugepaged_settings khugepaged;
+ unsigned long read_ahead_kb;
+ struct hugepages_settings hugepages[NR_ORDERS];
+ struct shmem_hugepages_settings shmem_hugepages[NR_ORDERS];
+};
+
+int read_file(const char *path, char *buf, size_t buflen);
+int write_file(const char *path, const char *buf, size_t buflen);
+const unsigned long read_num(const char *path);
+void write_num(const char *path, unsigned long num);
+
+int thp_read_string(const char *name, const char * const strings[]);
+void thp_write_string(const char *name, const char *val);
+const unsigned long thp_read_num(const char *name);
+void thp_write_num(const char *name, unsigned long num);
+
+void thp_write_settings(struct thp_settings *settings);
+void thp_read_settings(struct thp_settings *settings);
+struct thp_settings *thp_current_settings(void);
+void thp_push_settings(struct thp_settings *settings);
+void thp_pop_settings(void);
+void thp_restore_settings(void);
+void thp_save_settings(void);
+
+void thp_set_read_ahead_path(char *path);
+unsigned long thp_supported_orders(void);
+unsigned long thp_shmem_supported_orders(void);
+
+bool thp_is_enabled(void);
+
+#endif /* __THP_SETTINGS_H__ */
diff --git a/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h b/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h
index d3133587d996..5a29d6bec51f 100644
--- a/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h
+++ b/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h
@@ -8,13 +8,11 @@
#ifndef _RSEQ_X86_THREAD_POINTER
#define _RSEQ_X86_THREAD_POINTER
-#include <features.h>
-
#ifdef __cplusplus
extern "C" {
#endif
-#if __GNUC_PREREQ (11, 1)
+#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1)
static inline void *rseq_thread_pointer(void)
{
return __builtin_thread_pointer();
diff --git a/tools/testing/selftests/timers/leap-a-day.c b/tools/testing/selftests/timers/leap-a-day.c
index 23eb398c8140..23d9ed05af65 100644
--- a/tools/testing/selftests/timers/leap-a-day.c
+++ b/tools/testing/selftests/timers/leap-a-day.c
@@ -9,16 +9,19 @@
* kernel's leap-second behavior, as well as how well applications
* handle the leap-second discontinuity.
*
- * Usage: leap-a-day [-s] [-i <num>]
+ * Usage: leap-a-day [-w] [-i <num>] [-t]
*
* Options:
- * -s: Each iteration, set the date to 10 seconds before midnight GMT.
- * This speeds up the number of leapsecond transitions tested,
- * but because it calls settimeofday frequently, advancing the
- * time by 24 hours every ~16 seconds, it may cause application
- * disruption.
+ * -w: Only set the leap-second flag and wait for the leap second
+ * each iteration, instead of advancing the time. By default the
+ * date is set to 10 seconds before midnight GMT, which speeds up
+ * the number of leapsecond transitions tested, but because it
+ * calls settimeofday frequently, advancing the time by 24 hours
+ * every ~16 seconds, it may cause application disruption.
*
- * -i: Number of iterations to run (default: infinite)
+ * -i: Number of iterations to run (-1 = infinite, default: 10)
+ *
+ * -t: Print TAI time.
*
* Other notes: Disabling NTP prior to running this is advised, as the two
* may conflict in their commands to the kernel.
@@ -186,7 +189,7 @@ int main(int argc, char **argv)
int opt;
/* Process arguments */
- while ((opt = getopt(argc, argv, "sti:")) != -1) {
+ while ((opt = getopt(argc, argv, "wti:")) != -1) {
switch (opt) {
case 'w':
printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n");
diff --git a/tools/testing/selftests/zram/zram_lib.sh b/tools/testing/selftests/zram/zram_lib.sh
index 21ec1966de76..0d44d83888f9 100755
--- a/tools/testing/selftests/zram/zram_lib.sh
+++ b/tools/testing/selftests/zram/zram_lib.sh
@@ -37,7 +37,7 @@ kernel_gte()
if [ $kernel_major -gt $major ]; then
return 0
- elif [[ $kernel_major -eq $major && $kernel_minor -ge $minor ]]; then
+ elif [ $kernel_major -eq $major ] && [ $kernel_minor -ge $minor ]; then
return 0
fi
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-14 11:58 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 11:58 Linux 6.6.157 Greg Kroah-Hartman
2026-09-14 11:58 ` Greg Kroah-Hartman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®