summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:37:28 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:37:28 +0200
commit5eccd39d8efa3bc8d557be50f202bbf023837eed (patch)
tree6f1589cd863481a7dfdace2b9a692957dc94afbe
parent8fb649f3a174efaa618928cbe0737bf78b456eed (diff)
parent5015d0d945b3d3f2b038d2667880d5762f7d9437 (diff)
downloadlinux-stable-linux-rolling-stable.tar.gz
linux-stable-linux-rolling-stable.zip
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/ABI/testing/sysfs-bus-nvdimm3
-rw-r--r--Documentation/admin-guide/blockdev/zoned_loop.rst9
-rw-r--r--Makefile15
-rw-r--r--arch/alpha/include/uapi/asm/fpu.h8
-rw-r--r--arch/alpha/kernel/pci-sysfs.c6
-rw-r--r--arch/alpha/kernel/sys_marvel.c25
-rw-r--r--arch/alpha/kernel/traps.c6
-rw-r--r--arch/alpha/math-emu/math.c88
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm64/boot/dts/qcom/kodiak.dtsi9
-rw-r--r--arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/px30-cobra.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts18
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts12
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts23
-rw-r--r--arch/arm64/kernel/compat_alignment.c4
-rw-r--r--arch/arm64/kernel/proton-pack.c9
-rw-r--r--arch/arm64/kvm/vgic/vgic-v2.c3
-rw-r--r--arch/openrisc/kernel/entry.S43
-rw-r--r--arch/powerpc/kernel/pci-common.c9
-rw-r--r--arch/powerpc/platforms/powermac/low_i2c.c2
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c23
-rw-r--r--arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts4
-rw-r--r--arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts4
-rw-r--r--arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts4
-rw-r--r--arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts4
-rw-r--r--arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts4
-rw-r--r--arch/riscv/include/asm/acpi.h19
-rw-r--r--arch/riscv/kernel/unaligned_access_speed.c19
-rw-r--r--arch/riscv/kvm/vcpu_pmu.c14
-rw-r--r--arch/s390/include/asm/percpu.h4
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c215
-rw-r--r--arch/x86/coco/tdx/tdx.c10
-rw-r--r--arch/x86/include/asm/barrier.h2
-rw-r--r--arch/x86/include/asm/insn-eval.h36
-rw-r--r--arch/x86/kvm/emulate.c26
-rw-r--r--arch/x86/net/bpf_jit_comp.c2
-rw-r--r--block/bio.c56
-rw-r--r--block/blk-map.c2
-rw-r--r--block/fops.c2
-rw-r--r--block/genhd.c1
-rw-r--r--drivers/accel/rocket/rocket_job.c32
-rw-r--r--drivers/acpi/acpi_platform.c45
-rw-r--r--drivers/acpi/acpi_tad.c50
-rw-r--r--drivers/acpi/apei/erst.c2
-rw-r--r--drivers/acpi/apei/ghes.c52
-rw-r--r--drivers/acpi/cppc_acpi.c24
-rw-r--r--drivers/acpi/pfr_update.c45
-rw-r--r--drivers/ata/libata-scsi.c28
-rw-r--r--drivers/auxdisplay/charlcd.c21
-rw-r--r--drivers/base/arch_numa.c12
-rw-r--r--drivers/base/power/main.c1
-rw-r--r--drivers/base/property.c19
-rw-r--r--drivers/block/zloop.c3
-rw-r--r--drivers/block/zram/backend_deflate.c10
-rw-r--r--drivers/block/zram/zram_drv.c14
-rw-r--r--drivers/bluetooth/btrtl.c13
-rw-r--r--drivers/bluetooth/btusb.c14
-rw-r--r--drivers/bluetooth/hci_bcm.c1
-rw-r--r--drivers/bluetooth/hci_bcm4377.c1
-rw-r--r--drivers/bluetooth/hci_h5.c4
-rw-r--r--drivers/bluetooth/hci_intel.c1
-rw-r--r--drivers/bluetooth/hci_ldisc.c2
-rw-r--r--drivers/bluetooth/hci_serdev.c2
-rw-r--r--drivers/cdx/cdx.c7
-rw-r--r--drivers/char/hw_random/stm32-rng.c10
-rw-r--r--drivers/char/ipmi/ipmb_dev_int.c5
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c4
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c6
-rw-r--r--drivers/clocksource/timer-nxp-pit.c4
-rw-r--r--drivers/clocksource/timer-sun4i.c2
-rw-r--r--drivers/cpufreq/apple-soc-cpufreq.c36
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c2
-rw-r--r--drivers/crypto/atmel-ecc.c46
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_main.c27
-rw-r--r--drivers/cxl/core/features.c8
-rw-r--r--drivers/cxl/core/mbox.c8
-rw-r--r--drivers/cxl/core/mce.c27
-rw-r--r--drivers/cxl/core/pmem.c10
-rw-r--r--drivers/cxl/core/ras.c1
-rw-r--r--drivers/cxl/core/ras_rch.c27
-rw-r--r--drivers/cxl/core/region.c43
-rw-r--r--drivers/cxl/cxl.h11
-rw-r--r--drivers/cxl/cxlmem.h2
-rw-r--r--drivers/cxl/pci.c1
-rw-r--r--drivers/cxl/pmem.c2
-rw-r--r--drivers/fpga/altera-cvp.c10
-rw-r--r--drivers/fpga/stratix10-soc.c21
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c79
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c28
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c11
-rw-r--r--drivers/hid/hid-apple.c18
-rw-r--r--drivers/hid/hid-corsair-void.c17
-rw-r--r--drivers/hid/hid-mcp2221.c17
-rw-r--r--drivers/hid/hid-picolcd_debugfs.c9
-rw-r--r--drivers/hid/hid-roccat.c13
-rw-r--r--drivers/hid/hid-sensor-custom.c9
-rw-r--r--drivers/hid/hid-sensor-hub.c42
-rw-r--r--drivers/hid/hid-sony.c4
-rw-r--r--drivers/hid/hid-universal-pidff.c4
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c1
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c10
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h1
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c2
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c19
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h2
-rw-r--r--drivers/hsi/controllers/omap_ssi_core.c6
-rw-r--r--drivers/hwmon/max6621.c8
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-sysfs.c15
-rw-r--r--drivers/hwtracing/ptt/hisi_ptt.c20
-rw-r--r--drivers/i2c/busses/i2c-mxs.c5
-rw-r--r--drivers/i3c/device.c2
-rw-r--r--drivers/i3c/internals.h5
-rw-r--r--drivers/i3c/master.c15
-rw-r--r--drivers/i3c/master/adi-i3c-master.c15
-rw-r--r--drivers/i3c/master/renesas-i3c.c138
-rw-r--r--drivers/i3c/master/svc-i3c-master.c10
-rw-r--r--drivers/infiniband/core/ib_core_uverbs.c3
-rw-r--r--drivers/infiniband/core/ucma.c14
-rw-r--r--drivers/infiniband/core/uverbs_main.c1
-rw-r--r--drivers/infiniband/core/uverbs_std_types_mr.c6
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c6
-rw-r--r--drivers/infiniband/hw/ionic/ionic_hw_stats.c95
-rw-r--r--drivers/infiniband/hw/ionic/ionic_ibdev.h11
-rw-r--r--drivers/infiniband/hw/ionic/ionic_lif_cfg.c2
-rw-r--r--drivers/interconnect/core.c7
-rw-r--r--drivers/iommu/amd/ppr.c3
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c24
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c65
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h3
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c8
-rw-r--r--drivers/iommu/generic_pt/iommu_pt.h2
-rw-r--r--drivers/iommu/intel/dmar.c15
-rw-r--r--drivers/iommu/intel/iommu.c8
-rw-r--r--drivers/iommu/intel/iommu.h2
-rw-r--r--drivers/iommu/iommu-sva.c2
-rw-r--r--drivers/iommu/iommu.c2
-rw-r--r--drivers/iommu/iommufd/device.c4
-rw-r--r--drivers/iommu/iommufd/ioas.c2
-rw-r--r--drivers/iommu/iommufd/selftest.c31
-rw-r--r--drivers/iommu/msm_iommu.c10
-rw-r--r--drivers/mailbox/qcom-ipcc.c17
-rw-r--r--drivers/md/dm-bufio.c10
-rw-r--r--drivers/md/dm-era-target.c5
-rw-r--r--drivers/md/dm-integrity.c32
-rw-r--r--drivers/md/dm-io.c96
-rw-r--r--drivers/md/dm-kcopyd.c10
-rw-r--r--drivers/md/dm-log-userspace-base.c1
-rw-r--r--drivers/md/dm-log.c4
-rw-r--r--drivers/md/dm-pcache/cache.c18
-rw-r--r--drivers/md/dm-pcache/cache.h21
-rw-r--r--drivers/md/dm-pcache/cache_dev.c22
-rw-r--r--drivers/md/dm-pcache/cache_gc.c10
-rw-r--r--drivers/md/dm-pcache/cache_key.c32
-rw-r--r--drivers/md/dm-pcache/cache_segment.c12
-rw-r--r--drivers/md/dm-pcache/cache_writeback.c10
-rw-r--r--drivers/md/dm-pcache/dm_pcache.c4
-rw-r--r--drivers/md/dm-raid1.c26
-rw-r--r--drivers/md/dm-snap-persistent.c4
-rw-r--r--drivers/md/dm-stats.c6
-rw-r--r--drivers/md/dm-switch.c2
-rw-r--r--drivers/md/dm-verity-target.c2
-rw-r--r--drivers/md/dm-writecache.c14
-rw-r--r--drivers/md/md.c7
-rw-r--r--drivers/md/persistent-data/dm-array.c53
-rw-r--r--drivers/md/raid10.c2
-rw-r--r--drivers/media/cec/platform/stm32/stm32-cec.c3
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c14
-rw-r--r--drivers/mfd/cgbc-core.c4
-rw-r--r--drivers/mfd/qnap-mcu.c13
-rw-r--r--drivers/mfd/sm501.c2
-rw-r--r--drivers/misc/nsm.c2
-rw-r--r--drivers/mmc/host/via-sdmmc.c12
-rw-r--r--drivers/net/dsa/realtek/rtl83xx.c4
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c17
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c4
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c5
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c4
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede.h8
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c1
-rw-r--r--drivers/net/ethernet/renesas/ravb.h3
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c9
-rw-r--r--drivers/net/ethernet/renesas/ravb_ptp.c44
-rw-r--r--drivers/net/ipa/ipa_modem.c18
-rw-r--r--drivers/net/ntb_netdev.c38
-rw-r--r--drivers/net/phy/phylink.c2
-rw-r--r--drivers/net/slip/slip.c11
-rw-r--r--drivers/net/thunderbolt/main.c12
-rw-r--r--drivers/net/tun.c21
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_ioctl.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/usb.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c26
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c9
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c2
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c2
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/core.c19
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.c4
-rw-r--r--drivers/net/wireless/realtek/rtw88/tx.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h1
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c3
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.c13
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.h1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851be.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852ae.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852be.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bte.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852ce.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922ae.c1
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922de.c1
-rw-r--r--drivers/ntb/ntb_transport.c38
-rw-r--r--drivers/nvdimm/label.c2
-rw-r--r--drivers/nvme/host/core.c2
-rw-r--r--drivers/nvme/host/fc.c2
-rw-r--r--drivers/nvme/host/pci.c1
-rw-r--r--drivers/nvme/host/tcp.c45
-rw-r--r--drivers/of/base.c2
-rw-r--r--drivers/pci/controller/dwc/pci-meson.c2
-rw-r--r--drivers/pci/controller/pci-hyperv.c1
-rw-r--r--drivers/pci/controller/plda/pcie-plda-host.c52
-rw-r--r--drivers/pci/controller/plda/pcie-starfive.c13
-rw-r--r--drivers/pci/msi/msi.c10
-rw-r--r--drivers/pci/pci-sysfs.c42
-rw-r--r--drivers/pci/pcie/aer.c29
-rw-r--r--drivers/pci/proc.c16
-rw-r--r--drivers/pci/quirks.c9
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c21
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c2
-rw-r--r--drivers/platform/chrome/cros_ec_sensorhub_ring.c19
-rw-r--r--drivers/platform/x86/amd/pmc/mp1_stb.c66
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c6
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c1
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.c18
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.h8
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c15
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/int-attributes.c6
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c14
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c14
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c4
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/string-attributes.c6
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c4
-rw-r--r--drivers/platform/x86/intel/int1092/intel_sar.c32
-rw-r--r--drivers/platform/x86/intel/ishtp_eclite.c5
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c80
-rw-r--r--drivers/platform/x86/lenovo/think-lmi.c19
-rw-r--r--drivers/platform/x86/lenovo/ymc.c8
-rw-r--r--drivers/platform/x86/panasonic-laptop.c11
-rw-r--r--drivers/power/supply/bq24257_charger.c16
-rw-r--r--drivers/power/supply/bq256xx_charger.c29
-rw-r--r--drivers/power/supply/bq25890_charger.c12
-rw-r--r--drivers/power/supply/charger-manager.c54
-rw-r--r--drivers/power/supply/cros_usbpd-charger.c17
-rw-r--r--drivers/power/supply/lp8727_charger.c4
-rw-r--r--drivers/power/supply/lp8788-charger.c2
-rw-r--r--drivers/power/supply/max17040_battery.c32
-rw-r--r--drivers/power/supply/pf1550-charger.c2
-rw-r--r--drivers/power/supply/qcom_battmgr.c9
-rw-r--r--drivers/power/supply/rt9455_charger.c21
-rw-r--r--drivers/power/supply/twl4030_charger.c12
-rw-r--r--drivers/power/supply/ucs1002_power.c6
-rw-r--r--drivers/ptp/ptp_vmclock.c6
-rw-r--r--drivers/rapidio/devices/rio_mport_cdev.c10
-rw-r--r--drivers/regulator/as3722-regulator.c1
-rw-r--r--drivers/regulator/max8998.c1
-rw-r--r--drivers/regulator/qcom-refgen-regulator.c4
-rw-r--r--drivers/remoteproc/mtk_scp.c9
-rw-r--r--drivers/rpmsg/qcom_glink_smem.c7
-rw-r--r--drivers/s390/block/dasd.c6
-rw-r--r--drivers/s390/block/dasd_eckd.c40
-rw-r--r--drivers/s390/block/dasd_erp.c3
-rw-r--r--drivers/scsi/fnic/fip.c2
-rw-r--r--drivers/scsi/scsi_lib.c2
-rw-r--r--drivers/staging/greybus/hid.c2
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.c1
-rw-r--r--drivers/staging/media/tegra-video/vi.c9
-rw-r--r--drivers/staging/media/tegra-video/vip.c10
-rw-r--r--drivers/tty/serial/imx.c20
-rw-r--r--drivers/usb/dwc2/gadget.c11
-rw-r--r--drivers/usb/dwc3/gadget.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c14
-rw-r--r--drivers/usb/gadget/function/f_midi2.c2
-rw-r--r--drivers/usb/gadget/function/f_tcm.c23
-rw-r--r--drivers/usb/gadget/function/f_uvc.c7
-rw-r--r--drivers/usb/gadget/function/u_audio.c24
-rw-r--r--drivers/usb/gadget/function/uvc_video.c2
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c20
-rw-r--r--drivers/usb/gadget/udc/snps_udc_plat.c5
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c52
-rw-r--r--drivers/usb/phy/phy-fsl-usb.h6
-rw-r--r--drivers/usb/typec/altmodes/thunderbolt.c2
-rw-r--r--drivers/usb/typec/hd3ss3220.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c12
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c3
-rw-r--r--drivers/video/backlight/aw99706.c10
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c4
-rw-r--r--drivers/video/fbdev/pvr2fb.c4
-rw-r--r--drivers/video/fbdev/ssd1307fb.c72
-rw-r--r--drivers/video/fbdev/uvesafb.c2
-rw-r--r--drivers/video/sticore.c22
-rw-r--r--drivers/w1/slaves/w1_ds28e17.c8
-rw-r--r--drivers/xen/balloon.c29
-rw-r--r--fs/backing-file.c2
-rw-r--r--fs/btrfs/direct-io.c19
-rw-r--r--fs/btrfs/extent_io.c31
-rw-r--r--fs/btrfs/extent_io.h5
-rw-r--r--fs/btrfs/inode.c23
-rw-r--r--fs/btrfs/ioctl.c23
-rw-r--r--fs/btrfs/relocation.c31
-rw-r--r--fs/buffer.c3
-rw-r--r--fs/ceph/addr.c11
-rw-r--r--fs/ceph/caps.c74
-rw-r--r--fs/ceph/dir.c10
-rw-r--r--fs/ceph/export.c26
-rw-r--r--fs/ceph/mds_client.c73
-rw-r--r--fs/ceph/mds_client.h1
-rw-r--r--fs/ceph/mdsmap.c11
-rw-r--r--fs/ceph/super.h14
-rw-r--r--fs/ceph/xattr.c1
-rw-r--r--fs/debugfs/file.c3
-rw-r--r--fs/ecryptfs/crypto.c2
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h3
-rw-r--r--fs/ecryptfs/keystore.c45
-rw-r--r--fs/ecryptfs/messaging.c11
-rw-r--r--fs/ecryptfs/miscdev.c5
-rw-r--r--fs/ecryptfs/super.c7
-rw-r--r--fs/efivarfs/super.c30
-rw-r--r--fs/erofs/zutil.c2
-rw-r--r--fs/ext2/inode.c7
-rw-r--r--fs/ext2/xattr.c4
-rw-r--r--fs/fat/misc.c4
-rw-r--r--fs/fuse/dev.c8
-rw-r--r--fs/fuse/dev.h2
-rw-r--r--fs/fuse/dev_uring.c38
-rw-r--r--fs/fuse/dev_uring_i.h5
-rw-r--r--fs/fuse/inode.c4
-rw-r--r--fs/iomap/bio.c2
-rw-r--r--fs/iomap/direct-io.c1
-rw-r--r--fs/jbd2/checkpoint.c28
-rw-r--r--fs/lockd/clntproc.c3
-rw-r--r--fs/lockd/lockd.h2
-rw-r--r--fs/lockd/svc.c4
-rw-r--r--fs/lockd/svc4proc.c7
-rw-r--r--fs/lockd/svcproc.c4
-rw-r--r--fs/lockd/svcsubs.c109
-rw-r--r--fs/nfs/callback.c4
-rw-r--r--fs/nfs/nfs4client.c17
-rw-r--r--fs/nfs/pnfs.c3
-rw-r--r--fs/nfs_common/nfslocalio.c16
-rw-r--r--fs/nfsd/filecache.c143
-rw-r--r--fs/nfsd/flexfilelayoutxdr.c20
-rw-r--r--fs/nfsd/localio.c8
-rw-r--r--fs/nfsd/lockd.c6
-rw-r--r--fs/nfsd/netns.h35
-rw-r--r--fs/nfsd/nfs2acl.c52
-rw-r--r--fs/nfsd/nfs3acl.c17
-rw-r--r--fs/nfsd/nfs3proc.c40
-rw-r--r--fs/nfsd/nfs4callback.c113
-rw-r--r--fs/nfsd/nfs4layouts.c39
-rw-r--r--fs/nfsd/nfs4proc.c223
-rw-r--r--fs/nfsd/nfs4recover.c48
-rw-r--r--fs/nfsd/nfs4state.c395
-rw-r--r--fs/nfsd/nfs4xdr.c40
-rw-r--r--fs/nfsd/nfscache.c6
-rw-r--r--fs/nfsd/nfsctl.c156
-rw-r--r--fs/nfsd/nfsfh.c12
-rw-r--r--fs/nfsd/nfsproc.c7
-rw-r--r--fs/nfsd/nfssvc.c49
-rw-r--r--fs/nfsd/nfsxdr.c32
-rw-r--r--fs/nfsd/state.h4
-rw-r--r--fs/nfsd/trace.h18
-rw-r--r--fs/nfsd/vfs.c39
-rw-r--r--fs/nilfs2/bmap.c2
-rw-r--r--fs/nilfs2/bmap.h2
-rw-r--r--fs/nilfs2/btree.c39
-rw-r--r--fs/nilfs2/direct.c4
-rw-r--r--fs/notify/fanotify/fanotify.c3
-rw-r--r--fs/notify/fanotify/fanotify.h6
-rw-r--r--fs/notify/fanotify/fanotify_user.c17
-rw-r--r--fs/notify/inotify/inotify_user.c15
-rw-r--r--fs/ntfs/attrib.c24
-rw-r--r--fs/ntfs/lcnalloc.c7
-rw-r--r--fs/ntfs/runlist.c48
-rw-r--r--fs/ntfs/super.c12
-rw-r--r--fs/ntfs3/frecord.c9
-rw-r--r--fs/ntfs3/fslog.c34
-rw-r--r--fs/ocfs2/cluster/heartbeat.c148
-rw-r--r--fs/ocfs2/cluster/nodemanager.c6
-rw-r--r--fs/ocfs2/cluster/nodemanager.h1
-rw-r--r--fs/ocfs2/dir.c47
-rw-r--r--fs/ocfs2/dlm/dlmmaster.c6
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c9
-rw-r--r--fs/ocfs2/refcounttree.c47
-rw-r--r--fs/ocfs2/suballoc.c2
-rw-r--r--fs/ocfs2/xattr.c5
-rw-r--r--fs/orangefs/devorangefs-req.c1
-rw-r--r--fs/orangefs/orangefs-debugfs.c1
-rw-r--r--fs/overlayfs/dir.c1
-rw-r--r--fs/pidfs.c43
-rw-r--r--fs/smb/client/cifsfs.c15
-rw-r--r--fs/smb/client/cifsfs.h1
-rw-r--r--fs/smb/client/cifssmb.c6
-rw-r--r--fs/smb/client/dfs_cache.c33
-rw-r--r--fs/smb/client/file.c1
-rw-r--r--fs/smb/client/inode.c56
-rw-r--r--fs/smb/client/smb1misc.c3
-rw-r--r--fs/smb/client/smb1ops.c2
-rw-r--r--fs/smb/client/smb1transport.c35
-rw-r--r--fs/smb/client/smb2file.c5
-rw-r--r--fs/smb/client/smb2inode.c6
-rw-r--r--fs/smb/client/smb2ops.c9
-rw-r--r--fs/smb/client/trace.h3
-rw-r--r--fs/smb/server/vfs_cache.c2
-rw-r--r--fs/tracefs/event_inode.c7
-rw-r--r--fs/ubifs/auth.c2
-rw-r--r--fs/udf/inode.c2
-rw-r--r--fs/udf/partition.c2
-rw-r--r--include/linux/bio.h2
-rw-r--r--include/linux/compaction.h3
-rw-r--r--include/linux/dm-io.h6
-rw-r--r--include/linux/entry-common.h3
-rw-r--r--include/linux/font.h4
-rw-r--r--include/linux/hugetlb.h7
-rw-r--r--include/linux/list.h15
-rw-r--r--include/linux/lockd/bind.h12
-rw-r--r--include/linux/sunrpc/bc_xprt.h5
-rw-r--r--include/linux/sunrpc/svc_rdma_pcl.h2
-rw-r--r--include/linux/uio.h10
-rw-r--r--include/linux/usb/tcpci.h1
-rw-r--r--include/linux/vmalloc.h2
-rw-r--r--include/net/ip_tunnels.h11
-rw-r--r--include/net/sctp/structs.h2
-rw-r--r--include/uapi/linux/landlock.h1
-rw-r--r--io_uring/query.c3
-rw-r--r--io_uring/waitid.c33
-rw-r--r--kernel/auditfilter.c6
-rw-r--r--kernel/bpf/bloom_filter.c19
-rw-r--r--kernel/bpf/stackmap.c3
-rw-r--r--kernel/dma/contiguous.c16
-rw-r--r--kernel/liveupdate/kexec_handover.c2
-rw-r--r--kernel/module/kallsyms.c24
-rw-r--r--kernel/module/main.c14
-rw-r--r--kernel/params.c14
-rw-r--r--kernel/pid.c31
-rw-r--r--kernel/reboot.c29
-rw-r--r--kernel/sched/core.c49
-rw-r--r--kernel/sched/cpufreq_schedutil.c15
-rw-r--r--kernel/sched/ext/ext.c342
-rw-r--r--kernel/sched/sched.h5
-rw-r--r--kernel/signal.c4
-rw-r--r--kernel/taskstats.c3
-rw-r--r--kernel/time/itimer.c2
-rw-r--r--kernel/time/timekeeping.c8
-rw-r--r--kernel/time/timer.c1
-rw-r--r--kernel/trace/ring_buffer.c198
-rw-r--r--kernel/trace/simple_ring_buffer.c4
-rw-r--r--kernel/trace/trace.c6
-rw-r--r--kernel/trace/trace_events.c2
-rw-r--r--kernel/trace/trace_events_hist.c4
-rw-r--r--kernel/trace/trace_events_user.c3
-rw-r--r--lib/iov_iter.c9
-rw-r--r--lib/ucs2_string.c2
-rw-r--r--lib/xarray.c3
-rw-r--r--mm/compaction.c98
-rw-r--r--mm/gup.c10
-rw-r--r--mm/huge_memory.c24
-rw-r--r--mm/hugetlb.c38
-rw-r--r--mm/hugetlb_vmemmap.c23
-rw-r--r--mm/internal.h10
-rw-r--r--mm/kasan/quarantine.c7
-rw-r--r--mm/kmemleak.c77
-rw-r--r--mm/madvise.c4
-rw-r--r--mm/memcontrol-v1.c6
-rw-r--r--mm/memcontrol.c13
-rw-r--r--mm/mempolicy.c29
-rw-r--r--mm/migrate.c8
-rw-r--r--mm/migrate_device.c13
-rw-r--r--mm/mm_init.c10
-rw-r--r--mm/page_alloc.c76
-rw-r--r--mm/page_owner.c10
-rw-r--r--mm/page_vma_mapped.c8
-rw-r--r--mm/pagewalk.c6
-rw-r--r--mm/rmap.c31
-rw-r--r--mm/slub.c78
-rw-r--r--mm/sparse-vmemmap.c4
-rw-r--r--mm/swapfile.c15
-rw-r--r--mm/vmalloc.c58
-rw-r--r--mm/vmscan.c40
-rw-r--r--mm/zsmalloc.c11
-rw-r--r--mm/zswap.c13
-rw-r--r--net/bluetooth/eir.c3
-rw-r--r--net/bluetooth/hci_conn.c3
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_event.c7
-rw-r--r--net/bluetooth/hci_sync.c2
-rw-r--r--net/bluetooth/iso.c8
-rw-r--r--net/bluetooth/rfcomm/core.c8
-rw-r--r--net/ceph/osd_client.c30
-rw-r--r--net/ceph/osdmap.c2
-rw-r--r--net/core/skbuff.c5
-rw-r--r--net/core/xdp.c4
-rw-r--r--net/ipv4/ip_tunnel.c2
-rw-r--r--net/ipv4/ipip.c2
-rw-r--r--net/ipv4/ipmr.c3
-rw-r--r--net/ipv6/exthdrs.c26
-rw-r--r--net/ipv6/ip6_gre.c14
-rw-r--r--net/ipv6/ip6_input.c1
-rw-r--r--net/ipv6/ip6_tunnel.c22
-rw-r--r--net/ipv6/route.c2
-rw-r--r--net/ipv6/seg6_local.c9
-rw-r--r--net/ipv6/sit.c2
-rw-r--r--net/iucv/af_iucv.c2
-rw-r--r--net/l2tp/l2tp_netlink.c16
-rw-r--r--net/mctp/route.c19
-rw-r--r--net/mpls/af_mpls.c2
-rw-r--r--net/mptcp/syncookies.c1
-rw-r--r--net/openvswitch/conntrack.c128
-rw-r--r--net/openvswitch/conntrack.h6
-rw-r--r--net/openvswitch/datapath.c58
-rw-r--r--net/openvswitch/datapath.h8
-rw-r--r--net/sched/sch_generic.c2
-rw-r--r--net/sctp/inqueue.c7
-rw-r--r--net/sctp/sm_sideeffect.c4
-rw-r--r--net/sctp/stream.c48
-rw-r--r--net/smc/af_smc.c3
-rw-r--r--net/smc/smc.h6
-rw-r--r--net/smc/smc_core.c27
-rw-r--r--net/smc/smc_llc.c122
-rw-r--r--net/smc/smc_rx.c11
-rw-r--r--net/smc/smc_tx.h6
-rw-r--r--net/smc/smc_wr.c6
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c6
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c3
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c13
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_upcall.c6
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_upcall.h1
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.c15
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c8
-rw-r--r--net/sunrpc/backchannel_rqst.c38
-rw-r--r--net/sunrpc/cache.c7
-rw-r--r--net/sunrpc/clnt.c22
-rw-r--r--net/sunrpc/sunrpc_syms.c1
-rw-r--r--net/sunrpc/svc.c93
-rw-r--r--net/sunrpc/xdr.c2
-rw-r--r--net/sunrpc/xprtrdma/ib_client.c24
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_pcl.c2
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c21
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c52
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c47
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c69
-rw-r--r--net/sunrpc/xprtsock.c12
-rw-r--r--net/vmw_vsock/virtio_transport.c2
-rw-r--r--rust/Makefile6
-rw-r--r--rust/kernel/bug.rs34
-rw-r--r--rust/kernel/devres.rs25
-rw-r--r--rust/kernel/dma.rs6
-rw-r--r--rust/kernel/fmt.rs9
-rw-r--r--rust/kernel/list.rs2
-rw-r--r--rust/kernel/num/bounded.rs31
-rw-r--r--rust/kernel/time.rs17
-rw-r--r--scripts/Makefile.build3
-rwxr-xr-xscripts/rust_is_available.sh14
-rw-r--r--scripts/rust_is_available_bindgen_libclang_22.h5
-rwxr-xr-xscripts/rust_is_available_test.py30
-rw-r--r--security/apparmor/include/cred.h6
-rw-r--r--security/apparmor/include/label.h6
-rw-r--r--security/apparmor/include/task.h15
-rw-r--r--security/apparmor/task.c27
-rw-r--r--security/keys/trusted-keys/trusted_tpm1.c2
-rw-r--r--security/landlock/errata/abi-1.h23
-rw-r--r--security/landlock/fs.c41
-rw-r--r--security/smack/smack_lsm.c2
-rw-r--r--sound/core/ump_convert.c1
-rw-r--r--sound/drivers/aloop.c6
-rw-r--r--sound/drivers/mpu401/mpu401.c6
-rw-r--r--sound/drivers/mts64.c6
-rw-r--r--sound/drivers/portman2x4.c6
-rw-r--r--sound/drivers/serial-u16550.c6
-rw-r--r--sound/drivers/virmidi.c6
-rw-r--r--sound/hda/codecs/realtek/alc269.c7
-rw-r--r--sound/hda/codecs/realtek/alc662.c1
-rw-r--r--sound/hda/core/ext/stream.c4
-rw-r--r--sound/pci/pcxhr/pcxhr.c14
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c7
-rw-r--r--sound/soc/codecs/aw88261.c6
-rw-r--r--sound/soc/codecs/aw88261.h6
-rw-r--r--sound/soc/tegra/tegra210_admaif.c208
-rw-r--r--sound/soc/tegra/tegra210_i2s.c24
-rw-r--r--sound/soc/tegra/tegra210_i2s.h1
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c42
-rw-r--r--sound/soc/tegra/tegra210_mixer.c10
-rw-r--r--sound/usb/6fire/comm.c9
-rw-r--r--sound/usb/bcd2000/bcd2000.c11
-rw-r--r--sound/usb/fcp.c4
-rw-r--r--tools/include/linux/compiler.h2
-rw-r--r--tools/objtool/check.c2
-rw-r--r--tools/sched_ext/include/scx/compat.bpf.h12
-rw-r--r--tools/sched_ext/include/scx/enum_defs.autogen.h1
-rw-r--r--tools/sched_ext/include/scx/enums.autogen.bpf.h3
-rw-r--r--tools/sched_ext/include/scx/enums.autogen.h1
-rw-r--r--tools/sched_ext/scx_qmap.bpf.c2
-rwxr-xr-xtools/testing/selftests/mm/run_vmtests.sh2
620 files changed, 7822 insertions, 3112 deletions
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/admin-guide/blockdev/zoned_loop.rst b/Documentation/admin-guide/blockdev/zoned_loop.rst
index f4f1f3121bf9..95974425ca05 100644
--- a/Documentation/admin-guide/blockdev/zoned_loop.rst
+++ b/Documentation/admin-guide/blockdev/zoned_loop.rst
@@ -30,11 +30,10 @@ indicates the position of the write pointer of the zone.
When resetting a sequential zone, its backing file size is truncated to zero.
Conversely, for a zone finish operation, the backing file is truncated to the
-zone size. With this, the maximum capacity of a zloop zoned block device created
-can be larger configured to be larger than the storage space available on the
-backing file system. Of course, for such configuration, writing more data than
-the storage space available on the backing file system will result in write
-errors.
+zone capacity. With this, a zloop zoned block device can be configured with a
+larger capacity than the storage space available on the backing file system. Of
+course, for such configuration, writing more data than the storage space
+available on the backing file system will result in write errors.
The zoned loop block device driver implements a complete zone transition state
machine. That is, zones can be empty, implicitly opened, explicitly opened,
diff --git a/Makefile b/Makefile
index 150b1c1d7bed..91d6e74db4dc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 2
-SUBLEVEL = 3
+SUBLEVEL = 4
EXTRAVERSION =
NAME = Baby Opossum Posse
@@ -1083,6 +1083,16 @@ endif
export CC_FLAGS_SCS
endif
+ifdef CONFIG_RUST_INLINE_HELPERS
+# `rustc` normally emits traps for unreachable paths during code generation.
+# With inline helpers, Clang performs code generation from the linked bitcode
+# instead, so request the same behavior explicitly. Otherwise `objtool` may
+# follow an impossible Rust path into the next function.
+CC_FLAGS_RUST_INLINE_HELPERS := -mllvm -trap-unreachable \
+ -mllvm -no-trap-after-noreturn
+export CC_FLAGS_RUST_INLINE_HELPERS
+endif
+
ifdef CONFIG_LTO_CLANG
ifdef CONFIG_LTO_CLANG_FULL
CC_FLAGS_LTO := -flto
@@ -1118,7 +1128,8 @@ endif
ifdef CONFIG_RUST
# Always pass -Zsanitizer-cfi-normalize-integers as CONFIG_RUST selects
# CONFIG_CFI_ICALL_NORMALIZE_INTEGERS.
- RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers
+ # Disable function merging as LLVM incorrectly merges functions with different KCFI types.
+ RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zmerge-functions=disabled
KBUILD_RUSTFLAGS += $(RUSTC_FLAGS_CFI)
export RUSTC_FLAGS_CFI
endif
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 94dbc470cd6c..7050f0f7fe3d 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -224,17 +224,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 7004397937cf..7cd20e5f9ee0 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -166,12 +166,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
@@ -185,7 +185,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 68d420bfd3c0..e3f2df3729e3 100644
--- a/arch/alpha/math-emu/math.c
+++ b/arch/alpha/math-emu/math.c
@@ -52,13 +52,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)
{
@@ -86,7 +86,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.
*
@@ -95,7 +110,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);
@@ -300,12 +315,56 @@ done:
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;
@@ -387,9 +446,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 9187240a02db..b8aa3fcce107 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -98,7 +98,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/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
index fa540d8c2615..ba907760fa6d 100644
--- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
+++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
@@ -191,9 +191,12 @@
qcom,vmid = <QCOM_SCM_VMID_MSS_MSA>;
};
- adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap@9cb80000 {
- reg = <0x0 0x9cb80000 0x0 0x800000>;
- no-map;
+ adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x80000000 0x0 0x80000000>;
+ reusable;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x800000>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
index 0f23eaef01f2..b290f0f4c0e1 100644
--- a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
+++ b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
@@ -151,7 +151,7 @@
interrupts-extended = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>;
- irq-gpios = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>;
+ irq-gpios = <&tlmm 80 GPIO_ACTIVE_LOW>;
reset-gpios = <&tlmm 71 GPIO_ACTIVE_HIGH>;
AVDD28-supply = <&ts_vdd_supply>;
VDDIO-supply = <&ts_vddio_supply>;
diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi
index db291730130c..d6de4da02dcd 100644
--- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi
+++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi
@@ -589,6 +589,7 @@
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-always-on;
};
vreg_l13b_3p0: ldo13 {
@@ -610,6 +611,7 @@
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-always-on;
};
};
diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
index add917af5de7..f14f9eca7d34 100644
--- a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi
@@ -35,7 +35,7 @@
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>;
};
gpio-leds {
diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
index 192791993f05..02200de695d3 100644
--- a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
@@ -33,7 +33,7 @@
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>;
};
gpio-leds {
diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
index 973b4c5880e2..29794216592d 100644
--- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi
@@ -26,7 +26,7 @@
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-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index 8d26bd9b7500..d46cdfe3f784 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -734,12 +734,6 @@
};
};
- wifi {
- wifi_host_wake_l: wifi-host-wake-l {
- rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>;
- };
- };
-
wireless-bluetooth {
bt_wake_pin: bt-wake-pin {
rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
@@ -766,19 +760,7 @@
pinctrl-names = "default";
pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
sd-uhs-sdr104;
- #address-cells = <1>;
- #size-cells = <0>;
status = "okay";
-
- brcmf: wifi@1 {
- compatible = "brcm,bcm4329-fmac";
- reg = <1>;
- interrupt-parent = <&gpio4>;
- interrupts = <RK_PD0 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "host-wake";
- pinctrl-names = "default";
- pinctrl-0 = <&wifi_host_wake_l>;
- };
};
&pwm0 {
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 4f2831097624..f6d62ee51f1b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
@@ -136,6 +136,18 @@
<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/rockchip/rk3588s-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts
index d534d662c40f..99853880aaac 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts
@@ -23,16 +23,19 @@
compatible = "simple-audio-card";
pinctrl-names = "default";
pinctrl-0 = <&hp_detect>;
+ simple-audio-card,aux-devs = <&headphones_amp>;
simple-audio-card,name = "rockchip,es8388";
- simple-audio-card,bitclock-master = <&masterdai>;
+ simple-audio-card,bitclock-master = <&cpudai>;
simple-audio-card,format = "i2s";
- simple-audio-card,frame-master = <&masterdai>;
+ simple-audio-card,frame-master = <&cpudai>;
simple-audio-card,hp-det-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>;
simple-audio-card,mclk-fs = <256>;
simple-audio-card,pin-switches = "Headphones";
simple-audio-card,routing =
- "Headphones", "LOUT1",
- "Headphones", "ROUT1",
+ "Headphones", "Headphone Amp OUTL",
+ "Headphones", "Headphone Amp OUTR",
+ "Headphone Amp INL", "LOUT2",
+ "Headphone Amp INR", "ROUT2",
"LINPUT1", "Microphone Jack",
"RINPUT1", "Microphone Jack",
"LINPUT2", "Onboard Microphone",
@@ -47,11 +50,17 @@
system-clock-frequency = <12288000>;
};
- simple-audio-card,cpu {
+ cpudai: simple-audio-card,cpu {
sound-dai = <&i2s0_8ch>;
};
};
+ headphones_amp: audio-amplifier-headphones {
+ compatible = "simple-audio-amplifier";
+ enable-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
+ sound-name-prefix = "Headphone Amp";
+ };
+
chosen {
stdout-path = "serial2:1500000n8";
};
@@ -327,12 +336,12 @@
es8388: audio-codec@11 {
compatible = "everest,es8388", "everest,es8328";
reg = <0x11>;
- clocks = <&cru I2S1_8CH_MCLKOUT>;
+ clocks = <&cru I2S0_8CH_MCLKOUT>;
AVDD-supply = <&vcc_3v3_s0>;
DVDD-supply = <&vcc_1v8_s0>;
HPVDD-supply = <&vcc_3v3_s0>;
PVDD-supply = <&vcc_3v3_s0>;
- assigned-clocks = <&cru I2S1_8CH_MCLKOUT>;
+ assigned-clocks = <&cru I2S0_8CH_MCLKOUT>;
assigned-clock-rates = <12288000>;
#sound-dai-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/proton-pack.c b/arch/arm64/kernel/proton-pack.c
index 7bb6553fec08..3bcf86154d95 100644
--- a/arch/arm64/kernel/proton-pack.c
+++ b/arch/arm64/kernel/proton-pack.c
@@ -1023,6 +1023,11 @@ static int __init parse_spectre_bhb_param(char *str)
}
early_param("nospectre_bhb", parse_spectre_bhb_param);
+static bool spectre_bhb_mitigations_off(void)
+{
+ return __nospectre_bhb || cpu_mitigations_off();
+}
+
void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry)
{
bp_hardening_cb_t cpu_cb;
@@ -1036,6 +1041,8 @@ void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry)
/* No point mitigating Spectre-BHB alone. */
} else if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) {
/* Do nothing */
+ } else if (spectre_bhb_mitigations_off()) {
+ /* Mitigation disabled on the command line */
} else if (supports_ecbhb(SCOPE_LOCAL_CPU)) {
state = SPECTRE_MITIGATED;
set_bit(BHB_HW, &system_bhb_mitigations);
@@ -1201,6 +1208,6 @@ void spectre_print_disabled_mitigations(void)
if (spectre_v4_mitigations_off())
pr_info("spectre-v4 %s", spectre_disabled_suffix);
- if (__nospectre_bhb || cpu_mitigations_off())
+ if (spectre_bhb_mitigations_off())
pr_info("spectre-bhb %s", spectre_disabled_suffix);
}
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index cafa3cb32bda..7182f63fc938 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -170,8 +170,9 @@ void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
/* Make sure we're in the same context as LR handling */
local_irq_save(flags);
+ /* Guest-supplied INTID: out of range yields no irq, so ignore it */
irq = vgic_get_vcpu_irq(vcpu, val);
- if (WARN_ON_ONCE(!irq))
+ if (!irq)
goto out;
/* See the corresponding v3 code for the rationale */
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index c7e90b09645e..18e68680471e 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -1223,15 +1223,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/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 3c4ca90e2ab7..4fc52c21fe5d 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -626,19 +626,14 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
return -ENXIO;
addr = hose->io_base_virt + port;
- /* WARNING: The generic code is idiotic. It gets passed a pointer
- * to what can be a 1, 2 or 4 byte quantity and always reads that
- * as a u32, which means that we have to correct the location of
- * the data read within those 32 bits for size 1 and 2
- */
switch(size) {
case 1:
- out_8(addr, val >> 24);
+ out_8(addr, val);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- out_le16(addr, val >> 16);
+ out_le16(addr, val);
return 2;
case 4:
if (port & 3)
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index da72a30ab865..973f58771d96 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1471,7 +1471,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 3e1f915fe4f6..272e9aab66d4 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -812,18 +812,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);
@@ -832,6 +825,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/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
index 72f77e9edd63..d2abda5f5383 100644
--- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
@@ -204,14 +204,14 @@
regulators {
buck1 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts
index 2fc8d6533786..c800153077a8 100644
--- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts
@@ -211,14 +211,14 @@
regulators {
buck1 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
index 0d809e4ad3b1..96623454116e 100644
--- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
@@ -180,14 +180,14 @@
regulators {
buck1 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts
index b13a8d6a2670..564a48c70b5d 100644
--- a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts
@@ -126,14 +126,14 @@
regulators {
buck1 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts
index 7c49bce427f3..b907a1396b21 100644
--- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts
@@ -164,14 +164,14 @@
regulators {
buck1 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <3450000>;
+ regulator-max-microvolt = <1050000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
index 26ab37c171bc..f4db04f5bbe7 100644
--- a/arch/riscv/include/asm/acpi.h
+++ b/arch/riscv/include/asm/acpi.h
@@ -12,6 +12,8 @@
#ifndef _ASM_ACPI_H
#define _ASM_ACPI_H
+#include <linux/cpuidle.h>
+
/* Basic configuration for ACPI */
#ifdef CONFIG_ACPI
@@ -67,6 +69,23 @@ int acpi_get_riscv_isa(struct acpi_table_header *table,
void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
u32 *cboz_size, u32 *cbop_size);
+
+/*
+ * RISC-V Functional Fixed Hardware Specification Version v1.0.1,
+ * Chapter 3.1.2, Table 4: Arch. Context Lost Flags
+ */
+#define RISCV_LPI_HART_TIMER_CTXT_LOST BIT(0)
+
+static inline unsigned int arch_get_idle_state_flags(u32 arch_flags)
+{
+ if (arch_flags & RISCV_LPI_HART_TIMER_CTXT_LOST)
+ return CPUIDLE_FLAG_TIMER_STOP;
+
+ return 0;
+}
+
+#define arch_get_idle_state_flags arch_get_idle_state_flags
+
#else
static inline void acpi_init_rintc_map(void) { }
static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)
diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c
index 36201613d2ca..67f1a0371659 100644
--- a/arch/riscv/kernel/unaligned_access_speed.c
+++ b/arch/riscv/kernel/unaligned_access_speed.c
@@ -6,7 +6,6 @@
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/jump_label.h>
-#include <linux/kthread.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/types.h>
@@ -288,18 +287,9 @@ free:
__free_pages(page, MISALIGNED_BUFFER_ORDER);
}
-/* Measure unaligned access speed on all CPUs present at boot in parallel. */
-static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
-{
- schedule_on_each_cpu(check_vector_unaligned_access);
- riscv_hwprobe_complete_async_probe();
-
- return 0;
-}
#else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */
-static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+static void check_vector_unaligned_access(struct work_struct *work __always_unused)
{
- return 0;
}
#endif
@@ -387,12 +377,7 @@ static int __init check_unaligned_access_all_cpus(void)
per_cpu(vector_misaligned_access, cpu) = unaligned_vector_speed_param;
} else if (!check_vector_unaligned_access_emulated_all_cpus() &&
IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) {
- riscv_hwprobe_register_async_probe();
- if (IS_ERR(kthread_run(vec_check_unaligned_access_speed_all_cpus,
- NULL, "vec_check_unaligned_access_speed_all_cpus"))) {
- pr_warn("Failed to create vec_unalign_check kthread\n");
- riscv_hwprobe_complete_async_probe();
- }
+ schedule_on_each_cpu(check_vector_unaligned_access);
}
/*
diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c
index 2025b664961c..a7f948410d53 100644
--- a/arch/riscv/kvm/vcpu_pmu.c
+++ b/arch/riscv/kvm/vcpu_pmu.c
@@ -12,7 +12,9 @@
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <linux/nospec.h>
+#include <linux/overflow.h>
#include <linux/perf/riscv_pmu.h>
+#include <linux/slab.h>
#include <asm/csr.h>
#include <asm/kvm_isa.h>
#include <asm/kvm_vcpu_sbi.h>
@@ -479,13 +481,14 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
unsigned long flags, struct kvm_vcpu_sbi_return *retdata)
{
struct riscv_pmu_event_info *einfo = NULL;
- int shmem_size = num_events * sizeof(*einfo);
+ size_t shmem_size;
gpa_t shmem;
u32 eidx, etype;
u64 econfig;
int ret;
- if (flags != 0 || (saddr_low & (SZ_16 - 1) || num_events == 0)) {
+ if (flags != 0 || (saddr_low & (SZ_16 - 1)) || num_events == 0 ||
+ check_mul_overflow(num_events, sizeof(*einfo), &shmem_size)) {
ret = SBI_ERR_INVALID_PARAM;
goto out;
}
@@ -500,7 +503,8 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
}
}
- einfo = kzalloc(shmem_size, GFP_KERNEL);
+ einfo = kvcalloc(num_events, sizeof(*einfo),
+ GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
if (!einfo) {
ret = SBI_ERR_FAILURE;
goto out;
@@ -512,7 +516,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
goto free_mem;
}
- for (int i = 0; i < num_events; i++) {
+ for (unsigned long i = 0; i < num_events; i++) {
eidx = einfo[i].event_idx;
etype = kvm_pmu_get_perf_event_type(eidx);
econfig = kvm_pmu_get_perf_event_config(eidx, einfo[i].event_data);
@@ -525,7 +529,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
ret = SBI_ERR_INVALID_ADDRESS;
free_mem:
- kfree(einfo);
+ kvfree(einfo);
out:
retdata->err_val = ret;
diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h
index 1d955dd0defa..feb58acf87d6 100644
--- a/arch/s390/include/asm/percpu.h
+++ b/arch/s390/include/asm/percpu.h
@@ -107,8 +107,8 @@
" .endif\n" \
".endr\n" \
".endm\n" \
- ALTERNATIVE("GEN_MVIY " __stringify(disp) " " __stringify(reg) "\n", \
- "GEN_MVIY " __stringify(dispalt) " " __stringify(reg) "\n", \
+ ALTERNATIVE("GEN_MVIY " disp ", " reg "\n", \
+ "GEN_MVIY " dispalt ", " reg "\n", \
ALT_FEATURE(MFEATURE_LOWCORE)) \
".purgem GEN_MVIY\n"
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index 2076ac22e2c4..2a8d4cc1d6a2 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -110,6 +110,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;
@@ -118,13 +119,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);
@@ -167,12 +170,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",
@@ -186,17 +191,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 {
@@ -206,20 +211,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;
/*
@@ -227,28 +235,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;
@@ -256,12 +265,12 @@ static int cpum_cf_alloc_cpu(int cpu)
cpuhw = kzalloc_obj(*cpuhw);
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) {
/*
@@ -269,10 +278,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;
}
@@ -284,39 +291,70 @@ unlock:
* 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 */
@@ -1090,53 +1128,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;
}
@@ -1183,7 +1235,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))
@@ -1225,11 +1277,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);
@@ -1385,6 +1450,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) {
@@ -1395,9 +1461,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;
}
@@ -1416,15 +1483,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/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index 29b6f1ed59ec..f904a636d449 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -694,8 +694,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
@@ -703,18 +703,16 @@ static bool handle_in(struct pt_regs *regs, int size, int port)
* "TDG.VP.VMCALL<Instruction.IO>".
*/
success = !__tdx_hypercall(&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(&regs->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/include/asm/barrier.h b/arch/x86/include/asm/barrier.h
index db70832232d4..3e78bb76153c 100644
--- a/arch/x86/include/asm/barrier.h
+++ b/arch/x86/include/asm/barrier.h
@@ -17,7 +17,7 @@
#define rmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \
X86_FEATURE_XMM2) ::: "memory", "cc")
#define wmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \
- X86_FEATURE_XMM2) ::: "memory", "cc")
+ X86_FEATURE_XMM) ::: "memory", "cc")
#else
#define __mb() asm volatile("mfence":::"memory")
#define __rmb() asm volatile("lfence":::"memory")
diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index 4733e9064ee5..ae05647a0afb 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)
@@ -46,4 +47,39 @@ enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
bool insn_is_nop(struct insn *insn);
+/*
+ * 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/kvm/emulate.c b/arch/x86/kvm/emulate.c
index b566ab5c7515..c6dcb5ac48af 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>
#include <asm/text-patching.h>
@@ -439,25 +440,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;
@@ -505,7 +487,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)
@@ -1767,7 +1749,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)
@@ -2008,7 +1990,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/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index de7515ea1bea..276d076d2993 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -1887,7 +1887,7 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
EMIT_mov(dst_reg, src_reg);
#ifdef CONFIG_SMP
/* add <dst>, gs:[<off>] */
- EMIT2(0x65, add_1mod(0x48, dst_reg));
+ EMIT2(0x65, add_2mod(0x48, 0, dst_reg));
EMIT3(0x03, add_2reg(0x04, 0, dst_reg), 0x25);
EMIT((u32)(unsigned long)&this_cpu_off, 4);
#endif
diff --git a/block/bio.c b/block/bio.c
index 6a2f6fc3413e..5018a6fc2f36 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1221,10 +1221,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
return 0;
}
+#ifdef CONFIG_DEBUG_KERNEL
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+ unsigned mem_align_mask)
+{
+ struct bvec_iter iter;
+ struct bio_vec bv;
+
+ /*
+ * Correct callers never break the alignment requirements, so this
+ * exhaustive check is only paid for in debug builds.
+ */
+ for_each_mp_bvec(bv, bio->bi_io_vec, iter, bio->bi_iter)
+ if ((bv.bv_offset | bv.bv_len) & mem_align_mask)
+ return false;
+ return true;
+}
+#else
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+ unsigned mem_align_mask)
+{
+ /*
+ * We forward the bio_vec as-is, so ITER_BVEC callers must provide
+ * segments already aligned to the device's DMA alignment. The only
+ * unchecked user-controllable offset that reaches here is an io_uring
+ * registered buffer where just the first segment can be unaligned
+ * (the rest is virtually contiguous), so checking only that one is
+ * sufficient to know if the entire vector is valid.
+ */
+ return !(mp_bvec_iter_offset(bio->bi_io_vec, bio->bi_iter) &
+ mem_align_mask);
+}
+#endif
+
/**
* bio_iov_iter_get_pages - add user or kernel pages to a bio
* @bio: bio to add pages to
* @iter: iov iterator describing the region to be added
+ * @mem_align_mask: the mask the source address and length must be aligned to,
+ * 0 for no requirement
* @len_align_mask: the mask to align the total size to, 0 for any length
*
* This takes either an iterator pointing to user memory, or one pointing to
@@ -1243,7 +1278,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
* is returned only if 0 pages could be pinned.
*/
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
- unsigned len_align_mask)
+ unsigned mem_align_mask, unsigned len_align_mask)
{
iov_iter_extraction_t flags = 0;
@@ -1252,6 +1287,10 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
if (iov_iter_is_bvec(iter)) {
bio_iov_bvec_set(bio, iter);
+
+ if (!bio_iov_bvec_aligned(bio, mem_align_mask))
+ return -EINVAL;
+
iov_iter_advance(iter, bio->bi_iter.bi_size);
return 0;
}
@@ -1266,8 +1305,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec,
BIO_MAX_SIZE - bio->bi_iter.bi_size,
- &bio->bi_vcnt, bio->bi_max_vecs, flags);
+ &bio->bi_vcnt, bio->bi_max_vecs,
+ mem_align_mask, flags);
if (ret <= 0) {
+ /*
+ * A misaligned vector fails the whole I/O. Release any
+ * pages pinned by earlier iterations before returning
+ * since this bio won't be submitted to release them.
+ */
+ if (ret == -EINVAL) {
+ bio_release_pages(bio, false);
+ bio_clear_flag(bio, BIO_PAGE_PINNED);
+ bio->bi_vcnt = 0;
+ }
if (!bio->bi_vcnt)
return ret;
break;
@@ -1380,7 +1430,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
do {
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
- &bio->bi_vcnt, bio->bi_max_vecs - 1, 0);
+ &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0);
if (ret <= 0) {
if (!bio->bi_vcnt)
goto out_folio_put;
diff --git a/block/blk-map.c b/block/blk-map.c
index d1d6bbe0ecf1..615d29bb840e 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -274,7 +274,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
* No alignment requirements on our part to support arbitrary
* passthrough commands.
*/
- ret = bio_iov_iter_get_pages(bio, iter, 0);
+ ret = bio_iov_iter_get_pages(bio, iter, 0, 0);
if (ret)
goto out_put;
ret = blk_rq_append_bio(rq, bio);
diff --git a/block/fops.c b/block/fops.c
index 15783a6180de..a84450d79b3c 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -46,7 +46,7 @@ static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
static inline int blkdev_iov_iter_get_pages(struct bio *bio,
struct iov_iter *iter, struct block_device *bdev)
{
- return bio_iov_iter_get_pages(bio, iter,
+ return bio_iov_iter_get_pages(bio, iter, bdev_dma_alignment(bdev),
bdev_logical_block_size(bdev) - 1);
}
diff --git a/block/genhd.c b/block/genhd.c
index e8ce0cabf392..f1990c7cdfb9 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -681,6 +681,7 @@ static bool __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/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index ac51bff39833..3141f210fcd1 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -8,6 +8,7 @@
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
+#include <linux/overflow.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -102,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev,
fail:
kvfree(rjob->tasks);
+ rjob->tasks = NULL;
return ret;
}
@@ -188,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job)
struct rocket_device *rdev = job->rdev;
struct drm_gem_object **bos;
struct ww_acquire_ctx acquire_ctx;
+ u32 bo_count;
int ret = 0;
- bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
- GFP_KERNEL);
+ if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
+ return -EINVAL;
+
+ bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
+ if (!bos)
+ return -ENOMEM;
memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
- ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+ ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
if (ret)
goto err;
@@ -220,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job)
rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
err_unlock:
- drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+ drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
err:
kvfree(bos);
@@ -310,13 +317,13 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
dma_fence_put(job->done_fence);
job->done_fence = dma_fence_get(fence);
- ret = pm_runtime_get_sync(core->dev);
+ ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
- return fence;
+ goto err_put_fences;
ret = iommu_attach_group(job->domain->domain, core->iommu_group);
if (ret < 0)
- return fence;
+ goto err_put_pm;
scoped_guard(mutex, &core->job_lock) {
core->in_flight_job = job;
@@ -324,6 +331,14 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
}
return fence;
+
+err_put_pm:
+ pm_runtime_put(core->dev);
+err_put_fences:
+ dma_fence_put(job->done_fence);
+ job->done_fence = NULL;
+ dma_fence_put(fence);
+ return ERR_PTR(ret);
}
static void rocket_job_handle_irq(struct rocket_core *core)
@@ -549,6 +564,7 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
kref_init(&rjob->refcount);
rjob->rdev = rdev;
+ rjob->domain = rocket_iommu_domain_get(file_priv);
ret = drm_sched_job_init(&rjob->base,
&file_priv->sched_entity,
@@ -574,8 +590,6 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
rjob->out_bo_count = job->out_bo_handle_count;
- rjob->domain = rocket_iommu_domain_get(file_priv);
-
ret = rocket_job_push(rjob);
if (ret)
goto out_cleanup_job;
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index a09636a4168e..e3c77a65f61c 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -12,6 +12,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
@@ -71,6 +72,42 @@ static struct notifier_block acpi_platform_notifier = {
.notifier_call = acpi_platform_device_remove_notify,
};
+static unsigned int acpi_platform_adjust_resources(struct acpi_device *adev,
+ struct resource *new_res,
+ struct resource *resources,
+ unsigned int count)
+{
+ unsigned int i;
+
+ if (!(new_res->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+ return count;
+
+ for (i = 0; i < count; ) {
+ struct resource *res = &resources[i];
+
+ /*
+ * Look for overlaps of resources of the same type that would
+ * cause resource insertion to fail down the road.
+ */
+ if (__resource_contains_unbound(res, new_res) ||
+ __resource_contains_unbound(new_res, res) ||
+ resource_type(new_res) != resource_type(res) ||
+ !resource_union(new_res, res, new_res)) {
+ i++;
+ continue;
+ }
+
+ dev_info(&adev->dev, "%pR expanded due to overlap\n", new_res);
+ /*
+ * Eliminate the previously processed resource that overlapped
+ * with the new one because it is not necessary any more.
+ */
+ memmove(res, res + 1, (--count - i) * sizeof(*res));
+ }
+
+ return count;
+}
+
static void acpi_platform_fill_resource(struct acpi_device *adev,
const struct resource *src, struct resource *dest)
{
@@ -151,10 +188,14 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
return ERR_PTR(-ENOMEM);
}
count = 0;
- list_for_each_entry(rentry, &resource_list, node)
+ list_for_each_entry(rentry, &resource_list, node) {
+ count = acpi_platform_adjust_resources(adev,
+ rentry->res,
+ resources,
+ count);
acpi_platform_fill_resource(adev, rentry->res,
&resources[count++]);
-
+ }
acpi_dev_free_resource_list(&resource_list);
}
}
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index fc43df083738..45459a4cafae 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/rtc.h>
@@ -86,6 +87,8 @@ static bool acpi_tad_rt_is_invalid(struct acpi_tad_rt *rt)
rt->daylight > 3;
}
+static DEFINE_MUTEX(acpi_tad_aml_lock);
+
static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
{
acpi_handle handle = ACPI_HANDLE(dev);
@@ -113,6 +116,8 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
if (PM_RUNTIME_ACQUIRE_ERR(&pm))
return -ENXIO;
+ guard(mutex)(&acpi_tad_aml_lock);
+
status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval);
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -124,30 +129,27 @@ static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt)
{
acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };
- union acpi_object *out_obj;
- struct acpi_tad_rt *data;
acpi_status status;
int ret = -EIO;
- status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
- if (ACPI_FAILURE(status))
- goto out_free;
-
- out_obj = output.pointer;
- if (out_obj->type != ACPI_TYPE_BUFFER)
- goto out_free;
-
- if (out_obj->buffer.length != sizeof(*rt))
- goto out_free;
-
- data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);
- if (!data->valid)
- goto out_free;
-
- memcpy(rt, data, sizeof(*rt));
- ret = 0;
+ guard(mutex)(&acpi_tad_aml_lock);
-out_free:
+ status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
+ if (ACPI_SUCCESS(status)) {
+ union acpi_object *out_obj;
+
+ out_obj = output.pointer;
+ if (out_obj->type == ACPI_TYPE_BUFFER &&
+ out_obj->buffer.length == sizeof(*rt)) {
+ struct acpi_tad_rt *data;
+
+ data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);
+ if (data->valid) {
+ memcpy(rt, data, sizeof(*rt));
+ ret = 0;
+ }
+ }
+ }
ACPI_FREE(output.pointer);
return ret;
}
@@ -193,6 +195,8 @@ static int __acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
args[0].integer.value = timer_id;
args[1].integer.value = value;
+ guard(mutex)(&acpi_tad_aml_lock);
+
status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -215,6 +219,8 @@ static int __acpi_tad_wake_read(struct device *dev, char *method, u32 timer_id,
args[0].integer.value = timer_id;
+ guard(mutex)(&acpi_tad_aml_lock);
+
status = acpi_evaluate_integer(handle, method, &arg_list, retval);
if (ACPI_FAILURE(status))
return -EIO;
@@ -416,6 +422,8 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
if (PM_RUNTIME_ACQUIRE_ERR(&pm))
return -ENXIO;
+ guard(mutex)(&acpi_tad_aml_lock);
+
status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -456,6 +464,8 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
if (PM_RUNTIME_ACQUIRE_ERR(&pm))
return -ENXIO;
+ guard(mutex)(&acpi_tad_aml_lock);
+
status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
if (ACPI_FAILURE(status))
return -EIO;
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index bf65e3461531..15ab797641cf 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -108,7 +108,7 @@ static inline u64 erst_get_timeout(void)
if (erst_erange.attr & ERST_RANGE_SLOW) {
timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >>
- ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC;
+ ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_USEC;
if (timeout < FIRMWARE_TIMEOUT)
timeout = FIRMWARE_TIMEOUT;
}
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 3236a3ce79d6..c40df9a3b37c 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -576,7 +576,7 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
return false;
p = (char *)(err + 1);
- length -= sizeof(err);
+ length -= sizeof(*err);
for (i = 0; i < err->err_info_num; i++) {
struct cper_arm_err_info *err_info;
@@ -749,7 +749,7 @@ static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
CXL_CPER_PROT_ERR_FIFO_DEPTH);
/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
-static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
+static DEFINE_RAW_SPINLOCK(cxl_cper_prot_err_work_lock);
struct work_struct *cxl_cper_prot_err_work;
static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
@@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
if (cxl_cper_sec_prot_err_valid(prot_err))
return;
- guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+ guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
if (!cxl_cper_prot_err_work)
return;
@@ -780,10 +780,11 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
int cxl_cper_register_prot_err_work(struct work_struct *work)
{
- if (cxl_cper_prot_err_work)
- return -EINVAL;
+ guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
- guard(spinlock)(&cxl_cper_prot_err_work_lock);
+ if (WARN_ONCE(cxl_cper_prot_err_work,
+ "CPER-CXL kfifo consumer already registered\n"))
+ return -EINVAL;
cxl_cper_prot_err_work = work;
return 0;
}
@@ -791,11 +792,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
int cxl_cper_unregister_prot_err_work(struct work_struct *work)
{
- if (cxl_cper_prot_err_work != work)
- return -EINVAL;
+ scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) {
+ if (WARN_ONCE(cxl_cper_prot_err_work != work,
+ "CPER-CXL kfifo consumer mismatch on unregister\n"))
+ return -EINVAL;
+ cxl_cper_prot_err_work = NULL;
+ }
+
+ cancel_work_sync(work);
+
+ /* Discard stale entries so they are not replayed on next module load */
+ kfifo_reset(&cxl_cper_prot_err_fifo);
- guard(spinlock)(&cxl_cper_prot_err_work_lock);
- cxl_cper_prot_err_work = NULL;
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
@@ -811,7 +819,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
/* Synchronize schedule_work() with cxl_cper_work changes */
-static DEFINE_SPINLOCK(cxl_cper_work_lock);
+static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock);
struct work_struct *cxl_cper_work;
static void cxl_cper_post_event(enum cxl_event_type event_type,
@@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
return;
}
- guard(spinlock_irqsave)(&cxl_cper_work_lock);
+ guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
if (!cxl_cper_work)
return;
@@ -849,10 +857,11 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
int cxl_cper_register_work(struct work_struct *work)
{
- if (cxl_cper_work)
+ guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
+ if (WARN_ONCE(cxl_cper_work,
+ "CXL CPER kfifo consumer already registered\n"))
return -EINVAL;
- guard(spinlock)(&cxl_cper_work_lock);
cxl_cper_work = work;
return 0;
}
@@ -860,11 +869,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
int cxl_cper_unregister_work(struct work_struct *work)
{
- if (cxl_cper_work != work)
- return -EINVAL;
+ scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) {
+ if (WARN_ONCE(cxl_cper_work != work,
+ "CXL CPER kfifo consumer mismatch on unregister\n"))
+ return -EINVAL;
+ cxl_cper_work = NULL;
+ }
+
+ cancel_work_sync(work);
+
+ /* Discard stale entries so they are not replayed on next module load */
+ kfifo_reset(&cxl_cper_fifo);
- guard(spinlock)(&cxl_cper_work_lock);
- cxl_cper_work = NULL;
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 53d09ca98f06..becb7e442b30 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1316,15 +1316,30 @@ static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
return cpc_write(cpu, reg, val);
}
+static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc)
+{
+ return cpc_desc->version < CPPC_V4_REV;
+}
+
/**
* cppc_get_desired_perf - Get the desired performance register value.
* @cpunum: CPU from which to get desired performance.
* @desired_perf: Return address.
*
- * Return: 0 for success, -EIO otherwise.
+ * Return: 0 for success, -EOPNOTSUPP for _CPC revision 4 or later, and a
+ * negative errno otherwise.
*/
int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+
+ if (!cpc_desc)
+ return -ENODEV;
+
+ /* _CPC revision 4 no longer specifies Desired Performance as readable. */
+ if (!cppc_desired_perf_readable(cpc_desc))
+ return -EOPNOTSUPP;
+
return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf);
}
EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
@@ -1830,12 +1845,14 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 0;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cppc_pcc_data *pcc_ss_data = NULL;
+ bool read_desired_perf;
int ret = 0, regs_in_pcc = 0;
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpu);
return -ENODEV;
}
+ read_desired_perf = cppc_desired_perf_readable(cpc_desc);
if (!perf_ctrls) {
pr_debug("Invalid perf_ctrls pointer\n");
@@ -1849,7 +1866,8 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
/* Are any of the regs PCC ?*/
- if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
+ if ((read_desired_perf && CPC_IN_PCC(desired_perf_reg)) ||
+ CPC_IN_PCC(min_perf_reg) ||
CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg) ||
CPC_IN_PCC(auto_sel_reg)) {
if (pcc_ss_id < 0) {
@@ -1881,7 +1899,7 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
}
perf_ctrls->min_perf = min;
- if (CPC_SUPPORTED(desired_perf_reg)) {
+ if (read_desired_perf && CPC_SUPPORTED(desired_perf_reg)) {
ret = cpc_read(cpu, desired_perf_reg, &desired_perf);
if (ret)
goto out_err;
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index 6283105bb0e8..9afd2c52fdbd 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,
@@ -150,7 +150,9 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
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, "Query cap Error Status:%d\n",
@@ -158,29 +160,30 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
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/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index d2160ee7ca7d..f0bb4068d14d 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3545,17 +3545,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);
@@ -3590,13 +3586,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;
@@ -3640,13 +3634,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)) {
@@ -3672,6 +3666,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 09020bb8ad15..ae2692ec3963 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -595,6 +595,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;
@@ -654,8 +664,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);
@@ -665,16 +677,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 c99f2ab105e5..a36c939b8e90 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/power/main.c b/drivers/base/power/main.c
index f71467f6ada4..4ed2e9b7e99b 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -2234,6 +2234,7 @@ unlock:
if (ret < 0) {
suspend_report_result(dev, callback, ret);
+ pm_runtime_unblock(dev);
pm_runtime_put(dev);
return ret;
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index e08eadd66f4f..e0f570f48bc3 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -807,18 +807,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/block/zloop.c b/drivers/block/zloop.c
index 55eeb6aac0ea..1333fae05ac5 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -478,7 +478,8 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
zone->cond == BLK_ZONE_COND_FULL)
goto unlock;
- if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
+ if (vfs_truncate(&zone->file->f_path,
+ zlo->zone_capacity << SECTOR_SHIFT)) {
set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
ret = -EIO;
goto unlock;
diff --git a/drivers/block/zram/backend_deflate.c b/drivers/block/zram/backend_deflate.c
index f92a52a720d1..b3f7d08b49d9 100644
--- a/drivers/block/zram/backend_deflate.c
+++ b/drivers/block/zram/backend_deflate.c
@@ -24,8 +24,16 @@ static int deflate_setup_params(struct zcomp_params *params)
{
if (params->level == ZCOMP_PARAM_NOT_SET)
params->level = Z_DEFAULT_COMPRESSION;
- if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET)
+ if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET) {
params->deflate.winbits = DEFLATE_DEF_WINBITS;
+ } else {
+ s32 wb = params->deflate.winbits;
+
+ if ((wb < -15 || wb > -9) && (wb < 9 || wb > 15)) {
+ pr_err("invalid deflate winbits: %d\n", wb);
+ return -EINVAL;
+ }
+ }
return 0;
}
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index ace65c586072..f173f4c54b59 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1244,8 +1244,8 @@ static ssize_t writeback_store(struct device *dev,
const char *buf, size_t len)
{
struct zram *zram = dev_to_zram(dev);
- u64 nr_pages = zram->disksize >> PAGE_SHIFT;
- unsigned long lo = 0, hi = nr_pages;
+ u64 nr_pages;
+ unsigned long lo = 0, hi;
struct zram_pp_ctl *pp_ctl = NULL;
struct zram_wb_ctl *wb_ctl = NULL;
char *args, *param, *val;
@@ -1259,6 +1259,9 @@ static ssize_t writeback_store(struct device *dev,
if (!zram->backing_dev)
return -ENODEV;
+ nr_pages = zram->disksize >> PAGE_SHIFT;
+ hi = nr_pages;
+
pp_ctl = init_pp_ctl();
if (!pp_ctl)
return -ENOMEM;
@@ -1549,7 +1552,7 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
char *kbuf;
ssize_t index, written = 0;
struct zram *zram = file->private_data;
- unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
+ unsigned long nr_pages;
kbuf = kvmalloc(count, GFP_KERNEL);
if (!kbuf)
@@ -1561,6 +1564,8 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
return -EINVAL;
}
+ nr_pages = zram->disksize >> PAGE_SHIFT;
+
for (index = *ppos; index < nr_pages; index++) {
int copied;
@@ -2836,6 +2841,7 @@ static void zram_destroy_comps(struct zram *zram)
zram->comp_algs[prio] = NULL;
zram_comp_params_reset(zram);
+ comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
}
static void zram_reset_device(struct zram *zram)
@@ -2853,8 +2859,6 @@ static void zram_reset_device(struct zram *zram)
zram_destroy_comps(zram);
memset(&zram->stats, 0, sizeof(zram->stats));
reset_bdev(zram);
-
- comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
}
static ssize_t disksize_store(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 7f54d2d2d13a..03fa9409e3ee 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -1343,19 +1343,6 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
if (!btrtl_dev->ic_info)
return;
- switch (btrtl_dev->project_id) {
- case CHIP_ID_8761B:
- /* RTL8761B/BU reports HCI version 5.1 but does not support
- * the LE Extended Scan commands (Opcode 0x2042), causing
- * repeated -EBUSY failures when BlueZ attempts extended
- * scanning while a connection is active.
- */
- hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
- break;
- default:
- break;
- }
-
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8703B:
/* 8723CS reports two pages for local ext features,
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 184e95c1625e..91b22ddc9f5d 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -67,6 +67,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
#define BTUSB_ACTIONS_SEMI BIT(27)
#define BTUSB_BARROT BIT(28)
+#define BTUSB_BROKEN_EXT_SCAN BIT(29)
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -615,6 +616,10 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x0489, 0xe130), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ /* Realtek 8761BU Bluetooth devices */
+ { USB_DEVICE(0x0bda, 0xa728), .driver_info = BTUSB_REALTEK |
+ BTUSB_BROKEN_EXT_SCAN },
+
/* Realtek Bluetooth devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_REALTEK },
@@ -850,6 +855,12 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x37ad, 0x0600), .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 },
@@ -4366,6 +4377,9 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_INVALID_LE_STATES)
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
+ if (id->driver_info & BTUSB_BROKEN_EXT_SCAN)
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
+
if (id->driver_info & BTUSB_DIGIANSWER) {
data->cmdreq_type = USB_TYPE_VENDOR;
hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 1a4fc3882fd2..cc419e1960de 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -547,6 +547,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_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 925d0a635945..66d49b471544 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -2490,6 +2490,7 @@ static const struct bcm4377_hw bcm4377_hw_variants[] = {
.has_bar0_core2_window2 = true,
.broken_mws_transport_config = true,
.broken_le_coded = true,
+ .broken_le_ext_adv_report_phy = true,
.send_calibration = bcm4378_send_calibration,
.send_ptb = bcm4378_send_ptb,
},
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 93cdde981840..a5e7e9b0ea74 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -1023,8 +1023,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 c31105b91e47..1919547ff2a1 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -345,6 +345,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 2ad42c3bbaac..27644d2e53f8 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -451,7 +451,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 593d9cefbbf9..d2eaf2f12aa2 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/cdx/cdx.c b/drivers/cdx/cdx.c
index d3d230247262..a118a89a7779 100644
--- a/drivers/cdx/cdx.c
+++ b/drivers/cdx/cdx.c
@@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
sysfs_bin_attr_init(res_attr);
- cdx_dev->res_attr[num] = res_attr;
sprintf(res_attr_name, "resource%d", num);
res_attr->mmap = cdx_mmap_resource;
@@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
res_attr->size = cdx_resource_len(cdx_dev, num);
res_attr->private = (void *)(unsigned long)num;
ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr);
- if (ret)
+ if (ret) {
kfree(res_attr);
+ return ret;
+ }
+
+ cdx_dev->res_attr[num] = res_attr;
return ret;
}
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index 9a8c00586ab0..f5bfe54c01dc 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -368,11 +368,6 @@ static int stm32_rng_init(struct hwrng *rng)
return 0;
}
-static void stm32_rng_remove(struct platform_device *ofdev)
-{
- pm_runtime_disable(&ofdev->dev);
-}
-
static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev)
{
struct stm32_rng_private *priv = dev_get_drvdata(dev);
@@ -590,7 +585,9 @@ static int stm32_rng_probe(struct platform_device *ofdev)
pm_runtime_set_autosuspend_delay(dev, 100);
pm_runtime_use_autosuspend(dev);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
return devm_hwrng_register(dev, &priv->rng);
}
@@ -602,7 +599,6 @@ static struct platform_driver stm32_rng_driver = {
.of_match_table = stm32_rng_match,
},
.probe = stm32_rng_probe,
- .remove = stm32_rng_remove,
};
module_platform_driver(stm32_rng_driver);
diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c
index 680ff15c30ab..e4c50d9ae3e1 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 ab4c85f3d6fe..0330d8412a0e 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -1391,6 +1391,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
}
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
+ synchronize_rcu();
while (rcvrs) {
rcvr = rcvrs;
rcvrs = rcvr->next;
@@ -3740,6 +3741,7 @@ int ipmi_add_smi(struct module *owner,
sysfs_attr_init(&intf->maintenance_mode_devattr.attr);
rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr);
if (rv) {
+ device_remove_file(intf->si_dev, &intf->nr_msgs_devattr);
device_remove_file(intf->si_dev, &intf->nr_users_devattr);
goto out_err_bmc_reg;
}
@@ -3757,12 +3759,14 @@ 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:
list_del(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex);
mutex_unlock(&smi_watchers_mutex);
+ cancel_work_sync(&intf->smi_work);
kref_put(&intf->refcount, intf_free);
return rv;
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index d44903b29929..aa3673002f3a 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -182,8 +182,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/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c
index bc5157e2ba57..2f70d1d5e21b 100644
--- a/drivers/clocksource/timer-nxp-pit.c
+++ b/drivers/clocksource/timer-nxp-pit.c
@@ -328,8 +328,10 @@ static int pit_timer_init(struct device_node *np)
if (pit_instances == max_pit_instances) {
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "PIT timer:starting",
pit_clockevent_starting_cpu, NULL);
- if (ret < 0)
+ if (ret < 0) {
+ pit_clockevent_per_cpu_exit(pit, pit_instances);
goto out_pit_clocksource_unregister;
+ }
}
return 0;
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/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c
index 638e5bf72185..3f64f266e695 100644
--- a/drivers/cpufreq/apple-soc-cpufreq.c
+++ b/drivers/cpufreq/apple-soc-cpufreq.c
@@ -249,21 +249,19 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
return -ENODEV;
}
- ret = dev_pm_opp_of_add_table(cpu_dev);
- if (ret < 0) {
- dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
- return ret;
- }
+ priv = kzalloc_obj(*priv);
+ if (!priv)
+ return -ENOMEM;
ret = apple_soc_cpufreq_find_cluster(policy, &reg_base, &info);
if (ret) {
dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret);
- return ret;
+ goto out_free_priv;
}
- ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
- if (ret) {
- dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret);
+ ret = dev_pm_opp_of_cpumask_add_table(policy->cpus);
+ if (ret < 0) {
+ dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
goto out_iounmap;
}
@@ -271,19 +269,13 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
- goto out_free_opp;
- }
-
- priv = kzalloc_obj(*priv);
- if (!priv) {
- ret = -ENOMEM;
- goto out_free_opp;
+ goto out_free_table;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
- goto out_free_priv;
+ goto out_free_table;
}
/* Get OPP levels (p-state indexes) and stash them in driver_data */
@@ -318,12 +310,12 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-out_free_priv:
- kfree(priv);
-out_free_opp:
- dev_pm_opp_remove_all_dynamic(cpu_dev);
+out_free_table:
+ dev_pm_opp_of_cpumask_remove_table(policy->cpus);
out_iounmap:
iounmap(reg_base);
+out_free_priv:
+ kfree(priv);
return ret;
}
@@ -332,7 +324,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy)
struct apple_cpu_priv *priv = policy->driver_data;
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
- dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
+ dev_pm_opp_of_cpumask_remove_table(policy->cpus);
iounmap(priv->reg_base);
kfree(priv);
}
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 6fe0e972952a..80893844353c 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -836,7 +836,7 @@ out_invalid_counters:
* value first as some platforms may update the actual delivered perf
* there; if failed, resort to the cached desired perf.
*/
- if (cppc_get_desired_perf(cpu, &delivered_perf))
+ if (cppc_get_desired_perf(cpu, &delivered_perf) || !delivered_perf)
delivered_perf = cpu_data->perf_ctrls.desired_perf;
return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf);
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 9da9dd6585df..4dc34c5bc0f6 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,7 +54,7 @@ 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 */
+ /* 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 */
@@ -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,10 +82,9 @@ 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;
+ ctx->do_fallback = false;
if (crypto_ecdh_decode_key(buf, len, &params) < 0) {
dev_err(&ctx->client->dev, "crypto_ecdh_decode_key failed\n");
@@ -94,33 +92,25 @@ 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);
+ ret = crypto_kpp_set_secret(ctx->fallback, buf, len);
+ ctx->do_fallback = !ret;
+ return ret;
}
cmd = kmalloc_obj(*cmd);
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;
- ctx->do_fallback = false;
-
atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2);
ret = atmel_i2c_send_receive(ctx->client, cmd);
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,7 +139,7 @@ static int atmel_ecdh_generate_public_key(struct kpp_request *req)
if (!ctx->public_key)
return -EINVAL;
- /* might want less than we've got */
+ /* 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 */
@@ -175,7 +165,7 @@ 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 */
+ /* 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/intel/iaa/iaa_crypto_main.c b/drivers/crypto/intel/iaa/iaa_crypto_main.c
index 904d9413ba18..cdfe00464fa3 100644
--- a/drivers/crypto/intel/iaa/iaa_crypto_main.c
+++ b/drivers/crypto/intel/iaa/iaa_crypto_main.c
@@ -1058,13 +1058,17 @@ static void iaa_desc_complete(struct idxd_desc *idxd_desc,
pr_warn("%s: falling back to deflate-generic decompress, "
"analytics error code %x\n", __func__,
idxd_desc->iax_completion->error_code);
+ dma_unmap_sg(dev, ctx->req->dst, sg_nents(ctx->req->dst),
+ DMA_FROM_DEVICE);
+ dma_unmap_sg(dev, ctx->req->src, 1, DMA_TO_DEVICE);
+
ret = deflate_generic_decompress(ctx->req);
if (ret) {
dev_dbg(dev, "%s: deflate-generic failed ret=%d\n",
__func__, ret);
err = -EIO;
- goto err;
}
+ goto out;
} else {
err = -EIO;
goto err;
@@ -1447,19 +1451,9 @@ static int iaa_decompress(struct crypto_tfm *tfm, struct acomp_req *req,
ret = check_completion(dev, idxd_desc->iax_completion, false, false);
if (ret) {
dev_dbg(dev, "%s: check_completion failed ret=%d\n", __func__, ret);
- if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) {
- pr_warn("%s: falling back to deflate-generic decompress, "
- "analytics error code %x\n", __func__,
- idxd_desc->iax_completion->error_code);
- ret = deflate_generic_decompress(req);
- if (ret) {
- dev_dbg(dev, "%s: deflate-generic failed ret=%d\n",
- __func__, ret);
- goto err;
- }
- } else {
- goto err;
- }
+ if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR)
+ ret = -EAGAIN;
+ goto err;
} else {
req->dlen = idxd_desc->iax_completion->output_size;
}
@@ -1647,13 +1641,16 @@ static int iaa_comp_adecompress(struct acomp_req *req)
if (ret == -EINPROGRESS)
return ret;
- if (ret != 0)
+ if (ret != 0 && ret != -EAGAIN)
dev_dbg(dev, "asynchronous decompress failed ret=%d\n", ret);
dma_unmap_sg(dev, req->dst, 1, DMA_FROM_DEVICE);
dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE);
iaa_wq_put(wq);
+ if (ret == -EAGAIN)
+ ret = deflate_generic_decompress(req);
+
return ret;
}
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
index 85185af46b72..738a89863ee8 100644
--- a/drivers/cxl/core/features.c
+++ b/drivers/cxl/core/features.c
@@ -649,7 +649,13 @@ static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev);
struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds);
const struct fwctl_rpc_cxl *rpc_in = in;
- u16 opcode = rpc_in->opcode;
+ u16 opcode;
+
+ if (in_len < sizeof(rpc_in->hdr) ||
+ rpc_in->op_size > in_len - sizeof(rpc_in->hdr))
+ return ERR_PTR(-EINVAL);
+
+ opcode = rpc_in->opcode;
if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode))
return ERR_PTR(-EINVAL);
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 7c6c5b7450a5..1fa1f78565e3 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -11,7 +11,6 @@
#include "core.h"
#include "trace.h"
-#include "mce.h"
static bool cxl_raw_allow_all;
@@ -1526,7 +1525,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
u16 dvsec)
{
struct cxl_memdev_state *mds;
- int rc;
mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial,
dvsec, struct cxl_memdev_state, cxlds,
@@ -1538,12 +1536,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
mutex_init(&mds->event.log_lock);
- rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier);
- if (rc == -EOPNOTSUPP)
- dev_warn(dev, "CXL MCE unsupported\n");
- else if (rc)
- return ERR_PTR(rc);
-
return mds;
}
EXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, "CXL");
diff --git a/drivers/cxl/core/mce.c b/drivers/cxl/core/mce.c
index ff8d078c6ca1..65fed913b221 100644
--- a/drivers/cxl/core/mce.c
+++ b/drivers/cxl/core/mce.c
@@ -4,16 +4,16 @@
#include <linux/notifier.h>
#include <linux/set_memory.h>
#include <asm/mce.h>
-#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
#include "mce.h"
static int cxl_handle_mce(struct notifier_block *nb, unsigned long val,
void *data)
{
- struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state,
- mce_notifier);
- struct cxl_memdev *cxlmd = mds->cxlds.cxlmd;
- struct cxl_port *endpoint = cxlmd->endpoint;
+ struct cxl_region *cxlr = container_of(nb, struct cxl_region,
+ mce_notifier);
+ struct cxl_region_params *p = &cxlr->params;
struct mce *mce = data;
u64 spa, spa_alias;
unsigned long pfn;
@@ -21,26 +21,25 @@ static int cxl_handle_mce(struct notifier_block *nb, unsigned long val,
if (!mce || !mce_usable_address(mce))
return NOTIFY_DONE;
- if (!endpoint)
- return NOTIFY_DONE;
-
spa = mce->addr & MCI_ADDR_PHYSADDR;
- pfn = spa >> PAGE_SHIFT;
- if (!pfn_valid(pfn))
+ if (!cxl_resource_contains_addr(p->res, spa))
return NOTIFY_DONE;
- spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa);
- if (spa_alias == ~0ULL)
- return NOTIFY_DONE;
+ if (spa >= p->res->start + p->cache_size)
+ spa_alias = spa - p->cache_size;
+ else
+ spa_alias = spa + p->cache_size;
pfn = spa_alias >> PAGE_SHIFT;
+ if (!pfn_valid(pfn))
+ return NOTIFY_DONE;
/*
* Take down the aliased memory page. The original memory page flagged
* by the MCE will be taken cared of by the standard MCE handler.
*/
- dev_emerg(mds->cxlds.dev, "Offlining aliased SPA address0: %#llx\n",
+ dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n",
spa_alias);
if (!memory_failure(pfn, 0))
set_mce_nospec(pfn);
diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c
index 68462e38a977..5a3bb7e8a1f1 100644
--- a/drivers/cxl/core/pmem.c
+++ b/drivers/cxl/core/pmem.c
@@ -219,12 +219,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/ras.c b/drivers/cxl/core/ras.c
index 99fb00949c2f..bc74d4848132 100644
--- a/drivers/cxl/core/ras.c
+++ b/drivers/cxl/core/ras.c
@@ -137,7 +137,6 @@ int cxl_ras_init(void)
void cxl_ras_exit(void)
{
cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work);
- cancel_work_sync(&cxl_cper_prot_err_work);
}
static void cxl_dport_map_ras(struct cxl_dport *dport)
diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
index 0a8b3b9b6388..e0e01aa5eba6 100644
--- a/drivers/cxl/core/ras_rch.c
+++ b/drivers/cxl/core/ras_rch.c
@@ -58,13 +58,28 @@ void cxl_disable_rch_root_ints(struct cxl_dport *dport)
static bool cxl_rch_get_aer_info(void __iomem *aer_base,
struct aer_capability_regs *aer_regs)
{
- int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32);
+ /*
+ * Bound the copy to the physically-defined AER registers (header
+ * through the 16-byte Header Log). struct aer_capability_regs is a
+ * software layout whose embedded struct pcie_tlp_log is larger than
+ * the on-wire AER capability; copying sizeof(*aer_regs) would
+ * over-read the RCRB-mapped MMIO block.
+ */
+ int read_cnt = (PCI_ERR_HEADER_LOG + 16) / sizeof(u32);
u32 *aer_regs_buf = (u32 *)aer_regs;
int n;
if (!aer_base)
return false;
+ /*
+ * Zero the destination so the software-only tail fields
+ * (e.g. header_log.header_len) are deterministic rather than
+ * left as uninitialized stack, which could drive a bogus loop
+ * length in pcie_print_tlp_log().
+ */
+ memset(aer_regs, 0, sizeof(*aer_regs));
+
/* Use readl() to guarantee 32-bit accesses */
for (n = 0; n < read_cnt; n++)
aer_regs_buf[n] = readl(aer_base + n * sizeof(u32));
@@ -79,11 +94,11 @@ static bool cxl_rch_get_aer_info(void __iomem *aer_base,
static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
int *severity)
{
- if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
- if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
- *severity = AER_FATAL;
- else
- *severity = AER_NONFATAL;
+ u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask;
+
+ if (uncor_status) {
+ *severity = (uncor_status & aer_regs->uncor_severity) ?
+ AER_FATAL : AER_NONFATAL;
return true;
}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1e211542b6b6..578622240401 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -15,6 +15,7 @@
#include <cxlmem.h>
#include <cxl.h>
#include "core.h"
+#include "mce.h"
/**
* DOC: cxl core region
@@ -3859,34 +3860,6 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
}
EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, "CXL");
-u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
-{
- struct cxl_region_ref *iter;
- unsigned long index;
-
- if (!endpoint)
- return ~0ULL;
-
- guard(rwsem_write)(&cxl_rwsem.region);
-
- xa_for_each(&endpoint->regions, index, iter) {
- struct cxl_region_params *p = &iter->region->params;
-
- if (cxl_resource_contains_addr(p->res, spa)) {
- if (!p->cache_size)
- return ~0ULL;
-
- if (spa >= p->res->start + p->cache_size)
- return spa - p->cache_size;
-
- return spa + p->cache_size;
- }
- }
-
- return ~0ULL;
-}
-EXPORT_SYMBOL_NS_GPL(cxl_port_get_spa_cache_alias, "CXL");
-
static int is_system_ram(struct resource *res, void *arg)
{
struct cxl_region *cxlr = arg;
@@ -4217,6 +4190,20 @@ static int cxl_region_probe(struct device *dev)
if (rc)
return rc;
+ /*
+ * Regions fronted by an extended linear cache need the MCE notifier to
+ * offline the aliased page on a memory error.
+ */
+ if (p->cache_size) {
+ rc = devm_cxl_register_mce_notifier(&cxlr->dev,
+ &cxlr->mce_notifier);
+ if (rc == -EOPNOTSUPP)
+ dev_warn(&cxlr->dev,
+ "CONFIG_CXL_MCE disabled, MCE notifier not registered\n");
+ else if (rc)
+ return rc;
+ }
+
rc = cxl_region_setup_poison(cxlr);
if (rc)
return rc;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index c0e5308e4d1b..cab8ce39f465 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -478,6 +478,7 @@ struct cxl_region_params {
* @coord: QoS access coordinates for the region
* @node_notifier: notifier for setting the access coordinates to node
* @adist_notifier: notifier for calculating the abstract distance of node
+ * @mce_notifier: notifier for MCE
*/
struct cxl_region {
struct device dev;
@@ -493,6 +494,7 @@ struct cxl_region {
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
struct notifier_block node_notifier;
struct notifier_block adist_notifier;
+ struct notifier_block mce_notifier;
};
struct cxl_nvdimm_bridge {
@@ -503,7 +505,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
enum {
CXL_NVD_F_INVALIDATED = 0,
@@ -870,7 +873,6 @@ bool is_cxl_pmem_region(struct device *dev);
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
-u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
bool cxl_region_contains_resource(const struct resource *res);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
@@ -889,11 +891,6 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
{
return NULL;
}
-static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
- u64 spa)
-{
- return 0;
-}
static inline bool cxl_region_contains_resource(const struct resource *res)
{
return false;
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index ed419d0c59f2..c288e624c1f6 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -431,7 +431,6 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
* @poison: poison driver state info
* @security: security driver state info
* @fw: firmware upload / activation state
- * @mce_notifier: MCE notifier
*
* See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for
* details on capacity parameters.
@@ -451,7 +450,6 @@ struct cxl_memdev_state {
struct cxl_poison_state poison;
struct cxl_security_state security;
struct cxl_fw_state fw;
- struct notifier_block mce_notifier;
};
static inline struct cxl_memdev_state *
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 267c679b0b3c..7c6faee7f85e 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1083,7 +1083,6 @@ static int __init cxl_pci_driver_init(void)
static void __exit cxl_pci_driver_exit(void)
{
cxl_cper_unregister_work(&cxl_cper_work);
- cancel_work_sync(&cxl_cper_work);
pci_unregister_driver(&cxl_pci_driver);
}
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index 261dff7ced9f..a9f50281875d 100644
--- a/drivers/cxl/pmem.c
+++ b/drivers/cxl/pmem.c
@@ -52,7 +52,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/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..070c025ee215 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 */
@@ -261,7 +262,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++)
@@ -270,9 +271,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 0a295ccf1644..b8ec2e6f615f 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/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 941c1a312824..9320e49e4428 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7846,6 +7846,39 @@ amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
return status;
}
+/*
+ * Apple Studio Display exposes two SST DP links for a 2x1 tiled panel.
+ * The primary tile advertises the full 5120x2880 mode (with DSC on the
+ * bandwidth-sufficient link) while the secondary carries a per-tile
+ * 2560x2880 timing on a insufficient bandwidth link. Hide the secondary
+ * connector from userspace so compositors configure a single 5K stream
+ * on the primary link only.
+ */
+static bool amdgpu_dm_hide_secondary_tile_from_userspace(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+ if (!aconnector->dc_sink)
+ return false;
+
+ if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile)
+ return false;
+
+ drm_edid_connector_update(connector, aconnector->drm_edid);
+
+ if (!connector->has_tile)
+ return false;
+
+ if (!connector->tile_h_loc && !connector->tile_v_loc)
+ return false;
+
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] hiding secondary Apple Studio Display tile from userspace\n",
+ connector->base.id, connector->name);
+
+ return true;
+}
+
/**
* amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
*
@@ -7889,6 +7922,9 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
(!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
return amdgpu_dm_connector_poll(aconnector, force);
+ if (amdgpu_dm_hide_secondary_tile_from_userspace(connector))
+ return connector_status_disconnected;
+
return (aconnector->dc_sink ? connector_status_connected :
connector_status_disconnected);
}
@@ -9080,6 +9116,47 @@ static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
}
}
+/*
+ * The Apple Studio Display primary tile advertises both the full 5120x2880
+ * mode and the per-tile 2560x2880 timing. As the secondary tile is hidden from
+ * userspace (see amdgpu_dm_hide_secondary_tile_from_userspace()), drop the
+ * per-tile timing from the primary connector so compositors only pick the full
+ * 5K mode.
+ */
+static void amdgpu_dm_prune_primary_tile_modes(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ struct drm_display_mode *mode, *t;
+
+ if (!aconnector->dc_sink)
+ return;
+
+ if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile)
+ return;
+
+ if (!connector->has_tile)
+ return;
+
+ /* Only prune the per-tile timing from the primary tile. */
+ if (connector->tile_h_loc || connector->tile_v_loc)
+ return;
+
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
+ if (mode->hdisplay != connector->tile_h_size ||
+ mode->vdisplay != connector->tile_v_size)
+ continue;
+
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] pruning per-tile %dx%d timing from primary Apple Studio Display tile\n",
+ connector->base.id, connector->name,
+ mode->hdisplay, mode->vdisplay);
+
+ list_del(&mode->head);
+ drm_mode_destroy(connector->dev, mode);
+ aconnector->num_modes--;
+ }
+}
+
static void amdgpu_set_panel_orientation(struct drm_connector *connector)
{
struct drm_encoder *encoder;
@@ -9122,6 +9199,8 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
amdgpu_dm_connector->num_modes =
drm_edid_connector_add_modes(connector);
+ amdgpu_dm_prune_primary_tile_modes(connector);
+
/* sorting the probed modes before calling function
* amdgpu_dm_get_native_mode() since EDID can have
* more than one preferred mode. The modes that are
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
index 4c164ae4a4f9..8304e8897a3e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
@@ -535,15 +535,27 @@ static bool enable_assr(void *handle, struct dc_link *link)
static void update_config(void *handle, struct cp_psp_stream_config *config)
{
struct hdcp_workqueue *hdcp_work = handle;
- struct amdgpu_dm_connector *aconnector = config->dm_stream_ctx;
- int link_index = aconnector->dc_link->link_index;
- unsigned int conn_index = aconnector->base.index;
- struct mod_hdcp_display *display = &hdcp_work[link_index].display;
- struct mod_hdcp_link *link = &hdcp_work[link_index].link;
- struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+ struct amdgpu_dm_connector *aconnector;
+ const struct dc *dc;
+ int link_index;
+ unsigned int conn_index;
+ struct mod_hdcp_display *display;
+ struct mod_hdcp_link *link;
+ struct hdcp_workqueue *hdcp_w;
struct dc_sink *sink = NULL;
bool link_is_hdcp14 = false;
- const struct dc *dc = aconnector->dc_link->dc;
+
+ aconnector = config->dm_stream_ctx;
+ if (!aconnector || !aconnector->dc_link)
+ return;
+
+ link_index = aconnector->dc_link->link_index;
+ display = &hdcp_work[link_index].display;
+ link = &hdcp_work[link_index].link;
+ hdcp_w = &hdcp_work[link_index];
+
+ conn_index = aconnector->base.index;
+ dc = aconnector->dc_link->dc;
if (config->dpms_off) {
hdcp_remove_display(hdcp_work, link_index, aconnector);
@@ -581,7 +593,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
link->dp.mst_enabled = config->mst_enabled;
link->dp.dp2_enabled = config->dp2_enabled;
link->dp.usb4_enabled = config->usb4_enabled;
- if (aconnector->dc_sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
+ if (sink && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
link->hdmi.frl_enabled = config->frl_enabled;
display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
link->adjust.auth_delay = 2;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 6be7f6edd0b2..41aa8f9bb152 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -134,6 +134,17 @@ static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id);
link->wa_flags.skip_phy_ssc_reduction = true;
break;
+ /*
+ * Workaround for Apple Studio Display which exposes a 2x1 tiled panel
+ * over two SST DP links. Hide the secondary tile from userspace so
+ * compositors drive a single 5K stream on the primary link only.
+ */
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE3A):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE42):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE46):
+ drm_dbg_driver(dev, "Hiding secondary tile on panel id %X\n", panel_id);
+ edid_caps->panel_patch.disable_second_tile = true;
+ break;
default:
return;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index 4ed1efa17270..6482e275e63b 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -187,6 +187,7 @@ struct dc_panel_patch {
unsigned int skip_audio_sab_check;
unsigned int mst_start_top_delay;
unsigned int remove_sink_ext_caps;
+ bool disable_second_tile;
unsigned int disable_colorimetry;
uint8_t blankstream_before_otg_off;
bool oled_optimize_display_on;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 20dba02d6175..c5a24dff4b69 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -313,11 +313,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/hid/hid-apple.c b/drivers/hid/hid-apple.c
index bf7dd0fbf249..a5f232cc2b66 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -91,6 +91,9 @@ struct apple_sc_backlight {
struct hid_device *hdev;
};
+/* T2 VHCI re-enumerates the internal keyboard across system resume. */
+static int apple_backlight_resume_brightness = -1;
+
struct apple_backlight_config_report {
u8 report_id;
u8 version;
@@ -825,6 +828,7 @@ static int apple_backlight_led_set(struct led_classdev *led_cdev,
static int apple_backlight_init(struct hid_device *hdev)
{
int ret;
+ int brightness;
struct apple_sc *asc = hid_get_drvdata(hdev);
struct apple_backlight_config_report *rep;
@@ -860,13 +864,20 @@ static int apple_backlight_init(struct hid_device *hdev)
asc->backlight->cdev.name = "apple::kbd_backlight";
asc->backlight->cdev.max_brightness = rep->backlight_on_max;
asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set;
- asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME;
+ /* VHCI re-enumeration restores the cached brightness in the next probe. */
+
+ brightness = READ_ONCE(apple_backlight_resume_brightness);
+ if (brightness < 0)
+ brightness = LED_OFF;
+ else
+ brightness = min_t(int, brightness, rep->backlight_on_max);
- ret = apple_backlight_set(hdev, 0, 0);
+ ret = apple_backlight_set(hdev, brightness, 0);
if (ret < 0) {
hid_err(hdev, "backlight set request failed: %d\n", ret);
goto cleanup_and_exit;
}
+ asc->backlight->cdev.brightness = brightness;
ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev);
@@ -999,6 +1010,9 @@ static void apple_remove(struct hid_device *hdev)
if (asc->quirks & APPLE_RDESC_BATTERY)
timer_delete_sync(&asc->battery_timer);
+ if (asc->backlight)
+ WRITE_ONCE(apple_backlight_resume_brightness,
+ asc->backlight->cdev.brightness);
hid_hw_stop(hdev);
}
diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c
index 5e9a5b8f7f16..071a663a6c26 100644
--- a/drivers/hid/hid-corsair-void.c
+++ b/drivers/hid/hid-corsair-void.c
@@ -92,6 +92,9 @@
#define CORSAIR_VOID_STATUS_REPORT_ID 0x64
#define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66
+#define CORSAIR_VOID_STATUS_REPORT_SIZE 5
+#define CORSAIR_VOID_FIRMWARE_REPORT_SIZE 5
+
#define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1
#define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21
#define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200
@@ -742,6 +745,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev,
/* Description of packets are documented at the top of this file */
if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) {
+ if (size < CORSAIR_VOID_STATUS_REPORT_SIZE) {
+ hid_warn_ratelimited(hid_dev,
+ "unexpected status report of size %d",
+ size);
+ return 1;
+ }
+
drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]);
drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) ||
drvdata->is_wired;
@@ -750,6 +760,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev,
FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]),
data[3], data[4]);
} else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) {
+ if (size < CORSAIR_VOID_FIRMWARE_REPORT_SIZE) {
+ hid_warn_ratelimited(hid_dev,
+ "unexpected firmware report of size %d",
+ size);
+ return 1;
+ }
+
drvdata->fw_receiver_major = data[1];
drvdata->fw_receiver_minor = data[2];
drvdata->fw_headset_major = data[3];
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index e4ddd8e9293b..d52ce3531ab7 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -343,7 +343,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4);
if (ret)
- return ret;
+ goto out;
mcp->rxbuf_idx = 0;
@@ -365,7 +365,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
} else {
usleep_range(980, 1000);
mcp_cancel_last_cmd(mcp);
- return ret;
+ goto out;
}
} else {
retries = 0;
@@ -375,6 +375,10 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
usleep_range(980, 1000);
ret = mcp_chk_last_cmd_status_free_bus(mcp);
+out:
+ mcp->rxbuf = NULL;
+ mcp->rxbuf_size = 0;
+
return ret;
}
@@ -861,6 +865,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:
@@ -926,6 +933,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];
@@ -1049,6 +1060,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-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
index 085847a92e07..1f7dfe60e9ba 100644
--- a/drivers/hid/hid-picolcd_debugfs.c
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -99,6 +99,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-roccat.c b/drivers/hid/hid-roccat.c
index d6fff53d4ee7..4f15eb951039 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 6b0da2e0e1c9..c2b425afd951 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -609,7 +609,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 ==
@@ -621,6 +621,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 34f710c465b8..6470a290ebfc 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/hid-sony.c b/drivers/hid/hid-sony.c
index e75246d29e16..77ab30303080 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -2424,7 +2424,9 @@ static void sony_remove(struct hid_device *hdev)
struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
- timer_delete_sync(&sc->ghl_poke_timer);
+ /* poison, not kill: a pending timer must not re-submit during teardown */
+ usb_poison_urb(sc->ghl_urb);
+ timer_shutdown_sync(&sc->ghl_poke_timer);
usb_free_urb(sc->ghl_urb);
}
diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c
index 549dac555d40..60180467a0bf 100644
--- a/drivers/hid/hid-universal-pidff.c
+++ b/drivers/hid/hid-universal-pidff.c
@@ -104,12 +104,14 @@ static int universal_pidff_probe(struct hid_device *hdev,
error = init_function(hdev, id->driver_data);
if (error) {
hid_warn(hdev, "Error initialising force feedback\n");
- goto err;
+ goto err_stop;
}
hid_info(hdev, "Universal pidff driver loaded successfully!");
return 0;
+err_stop:
+ hid_hw_stop(hdev);
err:
return error;
}
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
index 46d3e9a01999..4126c2408d98 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -774,6 +774,7 @@ static void quicki2c_remove(struct pci_dev *pdev)
quicki2c_hid_remove(qcdev);
quicki2c_dma_deinit(qcdev);
+ pm_runtime_dont_use_autosuspend(qcdev->dev);
pm_runtime_get_noresume(qcdev->dev);
quicki2c_dev_deinit(qcdev);
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
index 4ae2e1718b30..5d35edf2b6d8 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
@@ -555,7 +555,14 @@ static int quickspi_alloc_report_buf(struct quickspi_device *qsdev)
max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len),
le16_to_cpu(qsdev->dev_desc.max_input_len));
- qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL);
+ /*
+ * write_cmd_to_txdma() writes the output report header ahead of the
+ * content in this buffer, so it has to hold both.
+ */
+ qsdev->report_buf_size = HIDSPI_OUTPUT_REPORT_SIZE(max_report_len);
+
+ qsdev->report_buf = devm_kzalloc(qsdev->dev, qsdev->report_buf_size,
+ GFP_KERNEL);
if (!qsdev->report_buf)
return -ENOMEM;
@@ -713,6 +720,7 @@ static void quickspi_remove(struct pci_dev *pdev)
quickspi_hid_remove(qsdev);
quickspi_dma_deinit(qsdev);
+ pm_runtime_dont_use_autosuspend(qsdev->dev);
pm_runtime_get_noresume(qsdev->dev);
quickspi_dev_deinit(qsdev);
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
index bf5e18f5a5f4..0ed964bfe3dd 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
@@ -157,6 +157,7 @@ struct quickspi_device {
u8 *report_descriptor;
u8 *input_buf;
u8 *report_buf;
+ u32 report_buf_size;
u32 report_len;
wait_queue_head_t reset_ack_wq;
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
index 91d5807b4a83..a60a0a7f16aa 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
@@ -61,7 +61,7 @@ static int quickspi_hid_raw_request(struct hid_device *hid,
switch (reqtype) {
case HID_REQ_GET_REPORT:
- ret = quickspi_get_report(qsdev, rtype, reportnum, buf);
+ ret = quickspi_get_report(qsdev, rtype, reportnum, buf, len);
break;
case HID_REQ_SET_REPORT:
ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len);
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
index cb19057f1191..847c5ec55569 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
@@ -30,6 +30,9 @@ static int write_cmd_to_txdma(struct quickspi_device *qsdev,
write_buf = (struct output_report *)qsdev->report_buf;
+ if (HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len) > qsdev->report_buf_size)
+ return -EINVAL;
+
write_buf->output_hdr.report_type = report_type;
write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len);
write_buf->output_hdr.content_id = report_id;
@@ -342,10 +345,12 @@ int reset_tic(struct quickspi_device *qsdev)
}
int quickspi_get_report(struct quickspi_device *qsdev,
- u8 report_type, unsigned int report_id, void *buf)
+ u8 report_type, unsigned int report_id, void *buf,
+ u32 buf_len)
{
int rep_type;
int ret;
+ u32 report_len;
if (report_type == HID_INPUT_REPORT) {
rep_type = GET_INPUT_REPORT;
@@ -372,9 +377,17 @@ int quickspi_get_report(struct quickspi_device *qsdev,
}
qsdev->get_report_cmpl = false;
- memcpy(buf, qsdev->report_buf, qsdev->report_len);
+ /* quickspi_handle_input_data() updates this from IRQ context. */
+ report_len = READ_ONCE(qsdev->report_len);
+ if (report_len > buf_len) {
+ dev_err_once(qsdev->dev, "Get report response too big, %u vs %u\n",
+ report_len, buf_len);
+ return -EINVAL;
+ }
+
+ memcpy(buf, qsdev->report_buf, report_len);
- return qsdev->report_len;
+ return report_len;
}
int quickspi_set_report(struct quickspi_device *qsdev,
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
index 775e29c1ed13..8a2338bee808 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
@@ -12,7 +12,7 @@ struct quickspi_device;
void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len);
int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type,
- unsigned int report_id, void *buf);
+ unsigned int report_id, void *buf, u32 buf_len);
int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type,
unsigned int report_id, void *buf, u32 buf_len);
int quickspi_get_report_descriptor(struct quickspi_device *qsdev);
diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c
index eeacc427fd65..47826315d7d6 100644
--- a/drivers/hsi/controllers/omap_ssi_core.c
+++ b/drivers/hsi/controllers/omap_ssi_core.c
@@ -502,6 +502,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/max6621.c b/drivers/hwmon/max6621.c
index e86ec6d237ca..1a644d787143 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/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 762109307b86..b3c67e96a82a 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event);
static ssize_t cntr_val_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int i, ret = 0;
u32 val;
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!coresight_get_mode(drvdata->csdev)) {
spin_lock(&drvdata->spinlock);
- for (i = 0; i < drvdata->nr_cntr; i++)
- ret += sprintf(buf, "counter %d: %x\n",
- i, config->cntr_val[i]);
+ val = config->cntr_val[config->cntr_idx];
spin_unlock(&drvdata->spinlock);
- return ret;
- }
-
- for (i = 0; i < drvdata->nr_cntr; i++) {
- val = etm_readl(drvdata, ETMCNTVRn(i));
- ret += sprintf(buf, "counter %d: %x\n", i, val);
+ } else {
+ val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx));
}
- return ret;
+ return sysfs_emit(buf, "%#x\n", val);
}
static ssize_t cntr_val_store(struct device *dev,
diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c
index 94c371c49135..b5d851281fbf 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)
@@ -202,14 +202,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;
@@ -234,6 +238,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-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 4e07babea9c3..eee4fdcd9df3 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -839,7 +839,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
}
/* Setup the DMA */
- i2c->dmach = dma_request_chan(dev, "rx-tx");
+ i2c->dmach = devm_dma_request_chan(dev, "rx-tx");
if (IS_ERR(i2c->dmach)) {
return dev_err_probe(dev, PTR_ERR(i2c->dmach),
"Failed to request dma\n");
@@ -877,9 +877,6 @@ static void mxs_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c->adapter);
- if (i2c->dmach)
- dma_release_channel(i2c->dmach);
-
writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET);
}
diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index 101eaa77de68..a3778282e84c 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -309,7 +309,7 @@ EXPORT_SYMBOL_GPL(i3c_device_match_id);
*/
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
{
- return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR);
+ return i3c_bus_to_i3c_master(dev->bus)->this->info.hdr_cap | BIT(I3C_SDR);
}
EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode);
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index 0f1f3f766623..86a36b951e0d 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -72,4 +72,9 @@ static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
}
}
+static inline struct i3c_master_controller *i3c_bus_to_i3c_master(struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
#endif /* I3C_INTERNAL_H */
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index f1be38a640ca..b51b159fadee 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -97,12 +97,6 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus)
up_read(&bus->lock);
}
-static struct i3c_master_controller *
-i3c_bus_to_i3c_master(struct i3c_bus *i3cbus)
-{
- return container_of(i3cbus, struct i3c_master_controller, bus);
-}
-
static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
{
return container_of(dev, struct i3c_master_controller, dev);
@@ -2977,11 +2971,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/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
index 047081c9f064..e29aac286957 100644
--- a/drivers/i3c/master/adi-i3c-master.c
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -964,17 +964,9 @@ static int adi_i3c_master_probe(struct platform_device *pdev)
writel(0x00, master->regs + REG_ENABLE);
writel(0x00, master->regs + REG_IRQ_MASK);
- ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
- dev_name(&pdev->dev), master);
- if (ret)
- return ret;
-
platform_set_drvdata(pdev, master);
master->free_rr_slots = GENMASK(ADI_MAX_DEVS, 1);
-
- writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
-
spin_lock_init(&master->ibi.lock);
master->ibi.num_slots = 15;
master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
@@ -986,6 +978,13 @@ static int adi_i3c_master_probe(struct platform_device *pdev)
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);
+ ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
+ dev_name(&pdev->dev), master);
+ if (ret)
+ return ret;
+
+ writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
+
return i3c_master_register(&master->base, &pdev->dev,
&adi_i3c_master_ops, false);
}
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index f39c449922ca..c459e40fd5ff 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -260,11 +260,11 @@ struct renesas_i3c {
u32 dyn_addr;
u32 i2c_STDBR;
u32 i3c_STDBR;
+ u32 extbr;
unsigned long rate;
u8 addrs[RENESAS_I3C_MAX_DEVS];
struct renesas_i3c_xferqueue xferqueue;
void __iomem *regs;
- u32 *DATBASn;
struct clk_bulk_data *clks;
struct reset_control *presetn;
struct reset_control *tresetn;
@@ -433,6 +433,21 @@ static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c
}
}
+static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c)
+{
+ /* Disable all the interrupts. */
+ renesas_writel(i3c->regs, BIE, 0);
+ renesas_writel(i3c->regs, NTIE, 0);
+
+ /* Clear normal transfer status flags. */
+ renesas_writel(i3c->regs, NTST, 0);
+
+ /* Clear bus status flags. */
+ renesas_writel(i3c->regs, BST, 0);
+ /* Read back registers to confirm writes have fully propagated. */
+ renesas_readl(i3c->regs, BST);
+}
+
static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
{
unsigned long time_left;
@@ -607,10 +622,9 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
/* Extended Bit Rate setting */
- renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
- EXTBR_EBRHO(od_high_ticks) |
- EXTBR_EBRLP(pp_low_ticks) |
- EXTBR_EBRHP(pp_high_ticks));
+ i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) |
+ EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks);
+ renesas_writel(i3c->regs, EXTBR, i3c->extbr);
renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
i3c->refclk_div = cks;
@@ -689,7 +703,11 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
renesas_i3c_wait_xfer(i3c, xfer);
- newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
+ if (cmd->rx_count >= i3c->maxdevs)
+ newdevs = 0;
+ else
+ newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
+
newdevs &= ~olddevs;
for (pos = 0; pos < i3c->maxdevs; pos++) {
@@ -892,10 +910,26 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ int pos;
+
+ pos = renesas_i3c_get_free_pos(i3c);
+
+ if (data->index > pos && pos >= 0) {
+ renesas_writel(i3c->regs, DATBAS(data->index), 0);
+ i3c->addrs[data->index] = 0;
+ i3c->free_pos |= BIT(data->index);
+
+ data->index = pos;
+ i3c->free_pos &= ~BIT(data->index);
+ }
i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
dev->info.static_addr;
+ renesas_writel(i3c->regs, DATBAS(data->index),
+ DATBAS_DVSTAD(dev->info.static_addr) |
+ datbas_dvdyad_with_parity(i3c->addrs[data->index]));
+
return 0;
}
@@ -905,6 +939,8 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct renesas_i3c *i3c = to_renesas_i3c(m);
+ renesas_writel(i3c->regs, DATBAS(data->index), 0);
+
i3c_dev_set_master_data(dev, NULL);
i3c->addrs[data->index] = 0;
i3c->free_pos |= BIT(data->index);
@@ -1014,6 +1050,11 @@ static irqreturn_t renesas_i3c_tx_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1054,6 +1095,11 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
/* Clear the Respone Queue Full status flag*/
@@ -1138,6 +1184,11 @@ static irqreturn_t renesas_i3c_tend_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1184,6 +1235,11 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1234,15 +1290,13 @@ static irqreturn_t renesas_i3c_stop_isr(int irq, void *data)
struct renesas_i3c_xfer *xfer;
scoped_guard(spinlock, &i3c->xferqueue.lock) {
- xfer = i3c->xferqueue.cur;
-
- /* read back registers to confirm writes have fully propagated */
- renesas_writel(i3c->regs, BST, 0);
- renesas_readl(i3c->regs, BST);
- renesas_writel(i3c->regs, BIE, 0);
- renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
+ xfer = i3c->xferqueue.cur;
+ if (!xfer)
+ return IRQ_HANDLED;
+
xfer->ret = 0;
complete(&xfer->comp);
}
@@ -1259,6 +1313,11 @@ static irqreturn_t renesas_i3c_start_isr(int irq, void *data)
scoped_guard(spinlock, &i3c->xferqueue.lock) {
xfer = i3c->xferqueue.cur;
+ if (!xfer) {
+ renesas_i3c_irqs_mask_and_clear_locked(i3c);
+ return IRQ_HANDLED;
+ }
+
cmd = xfer->cmds;
if (xfer->is_i2c_xfer) {
@@ -1365,12 +1424,6 @@ static int renesas_i3c_probe(struct platform_device *pdev)
i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
- /* Allocate dynamic Device Address Table backup. */
- i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs,
- GFP_KERNEL);
- if (!i3c->DATBASn)
- return -ENOMEM;
-
return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
}
@@ -1381,17 +1434,13 @@ static void renesas_i3c_remove(struct platform_device *pdev)
i3c_master_unregister(&i3c->base);
}
-static int renesas_i3c_suspend_noirq(struct device *dev)
+static int renesas_i3c_suspend(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
- int i, ret;
+ int ret;
i2c_mark_adapter_suspended(&i3c->base.i2c);
- /* Store Device Address Table values. */
- for (i = 0; i < i3c->maxdevs; i++)
- i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i));
-
ret = reset_control_assert(i3c->presetn);
if (ret)
goto err_mark_resumed;
@@ -1412,50 +1461,61 @@ err_mark_resumed:
return ret;
}
-static int renesas_i3c_resume_noirq(struct device *dev)
+static int renesas_i3c_resume(struct device *dev)
{
struct renesas_i3c *i3c = dev_get_drvdata(dev);
- int i, ret;
+ int ret;
- ret = reset_control_deassert(i3c->presetn);
+ ret = reset_control_deassert(i3c->tresetn);
if (ret)
return ret;
- ret = reset_control_deassert(i3c->tresetn);
+ ret = reset_control_deassert(i3c->presetn);
if (ret)
- goto err_presetn;
+ goto err_tresetn;
ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
if (ret)
- goto err_tresetn;
+ goto err_presetn;
+
+ ret = renesas_i3c_reset(i3c);
+ if (ret)
+ goto err_clks_disable;
/* Re-store I3C registers value. */
+ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+ renesas_writel(i3c->regs, EXTBR, i3c->extbr);
renesas_writel(i3c->regs, REFCKCTL,
REFCKCTL_IREFCKS(i3c->refclk_div));
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
MSDVAD_MDYAD(i3c->dyn_addr));
- /* Restore Device Address Table values. */
- for (i = 0; i < i3c->maxdevs; i++)
- renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]);
-
/* I3C hw init. */
renesas_i3c_hw_init(i3c);
+ ret = i3c_master_do_daa_ext(&i3c->base, true);
+ if (ret)
+ dev_err(dev, "DAA failed on resume, ret=%d", ret);
+
i2c_mark_adapter_resumed(&i3c->base.i2c);
+ /*
+ * I3C devices may have retained their dynamic address anyway. Do not
+ * fail the resume because of DAA error.
+ */
return 0;
-err_tresetn:
- reset_control_assert(i3c->tresetn);
+err_clks_disable:
+ clk_bulk_disable(i3c->num_clks, i3c->clks);
err_presetn:
reset_control_assert(i3c->presetn);
+err_tresetn:
+ reset_control_assert(i3c->tresetn);
return ret;
}
static const struct dev_pm_ops renesas_i3c_pm_ops = {
- NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
- renesas_i3c_resume_noirq)
+ SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend, renesas_i3c_resume)
};
static const struct of_device_id renesas_i3c_of_ids[] = {
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 93805df8a940..71f1fc693323 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -455,14 +455,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/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c
index dbbc0875132a..41c84ffe8c09 100644
--- a/drivers/infiniband/core/ib_core_uverbs.c
+++ b/drivers/infiniband/core/ib_core_uverbs.c
@@ -424,6 +424,9 @@ static uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
lockdep_assert_held(&bundle->ufile->device->disassociate_srcu);
+ if (!bundle->method_elm)
+ return NULL;
+
return srcu_dereference(bundle->method_elm->handler,
&bundle->ufile->device->disassociate_srcu);
}
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 878561fa1cb5..ac29dfa69bb3 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1404,7 +1404,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,
@@ -1776,6 +1779,13 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file,
goto out;
}
+ rdma_lock_handler(ctx->cm_id);
+ if (!ctx->uid) {
+ kfree(uevent);
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
uevent->ctx = ctx;
uevent->resp.uid = ctx->uid;
uevent->resp.id = ctx->id;
@@ -1789,6 +1799,8 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file,
mutex_unlock(&ctx->file->mut);
wake_up_interruptible(&ctx->file->poll_wait);
+err_unlock:
+ rdma_unlock_handler(ctx->cm_id);
out:
ucma_put_ctx(ctx);
return ret;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 3ccf58e96aed..0d88b2ee68ff 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -556,6 +556,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
bundle.ufile = file;
bundle.context = NULL; /* only valid if bundle has uobject */
bundle.uobject = NULL;
+ bundle.method_elm = NULL;
if (!method_elm->is_ex) {
size_t in_len = hdr.in_words * 4 - sizeof(hdr);
size_t out_len = hdr.out_words * 4;
diff --git a/drivers/infiniband/core/uverbs_std_types_mr.c b/drivers/infiniband/core/uverbs_std_types_mr.c
index 570b9656801d..0c72f801e0d3 100644
--- a/drivers/infiniband/core/uverbs_std_types_mr.c
+++ b/drivers/infiniband/core/uverbs_std_types_mr.c
@@ -364,7 +364,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_REG_MR)(
dmah, attrs);
else
mr = pd->device->ops.reg_user_mr(pd, addr, length, iova,
- access_flags, dmah, NULL);
+ access_flags, dmah,
+ &attrs->driver_udata);
if (IS_ERR(mr))
return PTR_ERR(mr);
@@ -527,7 +528,8 @@ DECLARE_UVERBS_NAMED_METHOD(
UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_REG_MR_RESP_RKEY,
UVERBS_ATTR_TYPE(u32),
- UA_MANDATORY));
+ UA_MANDATORY),
+ UVERBS_ATTR_UHW());
DECLARE_UVERBS_NAMED_METHOD_DESTROY(
UVERBS_METHOD_MR_DESTROY,
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 102c5646b9ed..c1815972aecf 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -951,6 +951,12 @@ void c4iw_dealloc(struct uld_ctx *ctx)
static void c4iw_remove(struct uld_ctx *ctx)
{
pr_debug("c4iw_dev %p\n", ctx->dev);
+
+ /* c4iw_register_device() may still be using ctx->dev. */
+ cancel_work_sync(&ctx->reg_work);
+ if (!ctx->dev)
+ return;
+
debugfs_remove_recursive(ctx->dev->debugfs_root);
c4iw_unregister_device(ctx->dev);
c4iw_dealloc(ctx);
diff --git a/drivers/infiniband/hw/ionic/ionic_hw_stats.c b/drivers/infiniband/hw/ionic/ionic_hw_stats.c
index f72c9837e135..4f0a2dedbdfe 100644
--- a/drivers/infiniband/hw/ionic/ionic_hw_stats.c
+++ b/drivers/infiniband/hw/ionic/ionic_hw_stats.c
@@ -235,35 +235,34 @@ err_dma:
static struct rdma_hw_stats *
ionic_counter_alloc_stats(struct rdma_counter *counter)
{
+ struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter);
struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
- struct ionic_counter *cntr;
- int err;
+ struct rdma_hw_stats *stats;
+ int id;
- cntr = kzalloc_obj(*cntr);
- if (!cntr)
- return NULL;
-
- /* buffer for current values from the device */
cntr->vals = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!cntr->vals)
- goto err_vals;
+ return NULL;
- err = xa_alloc(&dev->counter_stats->xa_counters, &counter->id,
- cntr,
- XA_LIMIT(0, IONIC_MAX_QPID),
- GFP_KERNEL);
- if (err)
- goto err_xa;
+ id = ida_alloc_max(&dev->counter_stats->counter_ida,
+ IONIC_MAX_QPID, GFP_KERNEL);
+ if (id < 0)
+ goto err_ida;
- INIT_LIST_HEAD(&cntr->qp_list);
+ counter->id = id;
+
+ stats = rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs,
+ dev->counter_stats->queue_stats_count,
+ RDMA_HW_STATS_DEFAULT_LIFESPAN);
+ if (!stats)
+ goto err_hw_stats;
+
+ return stats;
- return rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs,
- dev->counter_stats->queue_stats_count,
- RDMA_HW_STATS_DEFAULT_LIFESPAN);
-err_xa:
+err_hw_stats:
+ ida_free(&dev->counter_stats->counter_ida, id);
+err_ida:
kfree(cntr->vals);
-err_vals:
- kfree(cntr);
return NULL;
}
@@ -271,14 +270,10 @@ err_vals:
static int ionic_counter_dealloc(struct rdma_counter *counter)
{
struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
- struct ionic_counter *cntr;
-
- cntr = xa_erase(&dev->counter_stats->xa_counters, counter->id);
- if (!cntr)
- return -EINVAL;
+ struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter);
+ ida_free(&dev->counter_stats->counter_ida, counter->id);
kfree(cntr->vals);
- kfree(cntr);
return 0;
}
@@ -287,13 +282,8 @@ static int ionic_counter_bind_qp(struct rdma_counter *counter,
struct ib_qp *ibqp,
u32 port)
{
- struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
+ struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter);
struct ionic_qp *qp = to_ionic_qp(ibqp);
- struct ionic_counter *cntr;
-
- cntr = xa_load(&dev->counter_stats->xa_counters, counter->id);
- if (!cntr)
- return -EINVAL;
list_add_tail(&qp->qp_list_counter, &cntr->qp_list);
ibqp->counter = counter;
@@ -313,29 +303,23 @@ static int ionic_counter_unbind_qp(struct ib_qp *ibqp, u32 port)
return 0;
}
-static int ionic_get_qp_stats(struct ib_device *ibdev,
- struct rdma_hw_stats *hw_stats,
- u32 counter_id)
+static int ionic_counter_update_stats(struct rdma_counter *counter)
{
- struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
- struct ionic_counter_stats *cs;
- struct ionic_counter *cntr;
+ struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter);
+ struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
+ struct ionic_counter_stats *cs = dev->counter_stats;
dma_addr_t hw_stats_dma;
struct ionic_qp *qp;
int rc, stat_i = 0;
- cs = dev->counter_stats;
- cntr = xa_load(&cs->xa_counters, counter_id);
- if (!cntr)
- return -EINVAL;
-
hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, cntr->vals,
PAGE_SIZE, DMA_FROM_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma);
if (rc)
return rc;
- memset(hw_stats->value, 0, sizeof(u64) * hw_stats->num_counters);
+ memset(counter->stats->value, 0,
+ sizeof(u64) * counter->stats->num_counters);
list_for_each_entry(qp, &cntr->qp_list, qp_list_counter) {
rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE,
@@ -345,7 +329,7 @@ static int ionic_get_qp_stats(struct ib_device *ibdev,
goto err_cmd;
for (stat_i = 0; stat_i < cs->queue_stats_count; ++stat_i)
- hw_stats->value[stat_i] +=
+ counter->stats->value[stat_i] +=
ionic_v1_stat_val(&cs->hdr[stat_i],
cntr->vals,
PAGE_SIZE);
@@ -360,11 +344,6 @@ err_cmd:
return rc;
}
-static int ionic_counter_update_stats(struct rdma_counter *counter)
-{
- return ionic_get_qp_stats(counter->device, counter->stats, counter->id);
-}
-
static int ionic_alloc_counters(struct ionic_ibdev *dev)
{
struct ionic_counter_stats *cs = dev->counter_stats;
@@ -424,12 +403,22 @@ static const struct ib_device_ops ionic_hw_stats_ops = {
.get_hw_stats = ionic_get_hw_stats,
};
+static void ionic_counter_init(struct rdma_counter *counter)
+{
+ struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter);
+
+ INIT_LIST_HEAD(&cntr->qp_list);
+}
+
static const struct ib_device_ops ionic_counter_stats_ops = {
.counter_alloc_stats = ionic_counter_alloc_stats,
.counter_dealloc = ionic_counter_dealloc,
.counter_bind_qp = ionic_counter_bind_qp,
.counter_unbind_qp = ionic_counter_unbind_qp,
.counter_update_stats = ionic_counter_update_stats,
+ .counter_init = ionic_counter_init,
+
+ INIT_RDMA_OBJ_SIZE(rdma_counter, ionic_rdma_counter, rdma_counter),
};
void ionic_stats_init(struct ionic_ibdev *dev)
@@ -458,7 +447,7 @@ void ionic_stats_init(struct ionic_ibdev *dev)
return;
}
- xa_init_flags(&dev->counter_stats->xa_counters, XA_FLAGS_ALLOC);
+ ida_init(&dev->counter_stats->counter_ida);
ib_set_device_ops(&dev->ibdev, &ionic_counter_stats_ops);
}
@@ -467,7 +456,7 @@ void ionic_stats_init(struct ionic_ibdev *dev)
void ionic_stats_cleanup(struct ionic_ibdev *dev)
{
if (dev->counter_stats) {
- xa_destroy(&dev->counter_stats->xa_counters);
+ ida_destroy(&dev->counter_stats->counter_ida);
kfree(dev->counter_stats->hdr);
kfree(dev->counter_stats->stats_hdrs);
kfree(dev->counter_stats);
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h
index 63828240d659..1c43344ec93c 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.h
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h
@@ -331,14 +331,21 @@ struct ionic_counter_stats {
int queue_stats_count;
struct ionic_v1_stat *hdr;
struct rdma_stat_desc *stats_hdrs;
- struct xarray xa_counters;
+ struct ida counter_ida;
};
-struct ionic_counter {
+struct ionic_rdma_counter {
+ struct rdma_counter rdma_counter;
void *vals;
struct list_head qp_list;
};
+static inline struct ionic_rdma_counter *
+to_ionic_rdma_counter(struct rdma_counter *counter)
+{
+ return container_of(counter, struct ionic_rdma_counter, rdma_counter);
+}
+
static inline struct ionic_ibdev *to_ionic_ibdev(struct ib_device *ibdev)
{
return container_of(ibdev, struct ionic_ibdev, ibdev);
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
index f3cd281c3a2f..45846dc2e89b 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
@@ -70,7 +70,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
* eq_count is tunable; see ionic_eq_count
*/
cfg->aq_count = le32_to_cpu(ident->rdma.aq_qtype.qid_count);
- cfg->eq_count = le32_to_cpu(ident->rdma.eq_qtype.qid_count);
+ cfg->eq_count = lif->ionic->neqs_per_lif;
cfg->cq_count = le32_to_cpu(ident->rdma.cq_qtype.qid_count);
cfg->qp_count = le32_to_cpu(ident->rdma.sq_qtype.qid_count);
cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif);
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 73a9db2c7537..4aa991a54101 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -548,7 +548,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);
}
@@ -646,8 +646,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/ppr.c b/drivers/iommu/amd/ppr.c
index 1f8d2823bea4..80369ca1e316 100644
--- a/drivers/iommu/amd/ppr.c
+++ b/drivers/iommu/amd/ppr.c
@@ -151,7 +151,7 @@ static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw)
/* Submit event */
iommu_report_device_fault(&pdev->dev, &event);
-
+ pci_dev_put(pdev);
return;
out:
@@ -159,6 +159,7 @@ out:
amd_iommu_complete_ppr(&pdev->dev, PPR_PASID(raw[0]),
IOMMU_PAGE_RESP_FAILURE,
PPR_TAG(raw[0]) & 0x1FF);
+ pci_dev_put(pdev);
}
void amd_iommu_poll_ppr_log(struct amd_iommu *iommu)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
index 1ed8a6f29dc4..4cd60d3c450a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
@@ -92,16 +92,6 @@ void arm_smmu_make_sva_cd(struct arm_smmu_cd *target,
target->data[1] = cpu_to_le64(virt_to_phys(mm->pgd) &
CTXDESC_CD_1_TTB0_MASK);
-
- /*
- * Enable Hardware Access and Dirty updates (DBM) if supported.
- * This is safe to enable by default, as PTE_WRITE and PTE_DBM
- * share the same bit.
- */
- if (master->smmu->features & ARM_SMMU_FEAT_HA)
- target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HA);
- if (master->smmu->features & ARM_SMMU_FEAT_HD)
- target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HD);
} else {
target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_EPD0);
@@ -114,6 +104,17 @@ void arm_smmu_make_sva_cd(struct arm_smmu_cd *target,
target->data[0] &=
cpu_to_le64(~(CTXDESC_CD_0_S | CTXDESC_CD_0_R));
}
+ /*
+ * Enable Hardware Access and Dirty updates (DBM) if supported. This is
+ * safe to enable by default, as PTE_WRITE and PTE_DBM share the same bit,
+ * while the EPD0 config can't get as far as fetching any PTEs anyway.
+ */
+ if (master->smmu->features & ARM_SMMU_FEAT_HA)
+ target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HA);
+ if (master->smmu->features & ARM_SMMU_FEAT_HD)
+ target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HD);
+ if (master->smmu->features & ARM_SMMU_FEAT_HAFT && system_supports_haft())
+ target->data[1] |= cpu_to_le64(CTXDESC_CD_1_HAFT);
/*
* MAIR value is pretty much constant and global, so we can just get it
@@ -211,6 +212,9 @@ bool arm_smmu_sva_supported(struct arm_smmu_device *smmu)
if (system_supports_bbml2_noabort())
feat_mask |= ARM_SMMU_FEAT_BBML2;
+ if (system_supports_haft())
+ feat_mask |= ARM_SMMU_FEAT_HAFT;
+
if ((smmu->features & feat_mask) != feat_mask)
return false;
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 10f105a0b29f..5f933d806a14 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4398,6 +4398,20 @@ int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
return 0;
}
+static void arm_smmu_free_iopf_action(void *data)
+{
+ struct iopf_queue *queue = data;
+
+ iopf_queue_free(queue);
+}
+
+static void arm_smmu_destroy_vmid_map(void *data)
+{
+ struct ida *ida = data;
+
+ ida_destroy(ida);
+}
+
static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
{
int ret;
@@ -4425,6 +4439,11 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev));
if (!smmu->evtq.iopf)
return -ENOMEM;
+ ret = devm_add_action_or_reset(smmu->dev,
+ arm_smmu_free_iopf_action,
+ smmu->evtq.iopf);
+ if (ret)
+ return ret;
}
/* priq */
@@ -4503,7 +4522,8 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
ida_init(&smmu->vmid_map);
- return 0;
+ return devm_add_action_or_reset(smmu->dev, arm_smmu_destroy_vmid_map,
+ &smmu->vmid_map);
}
static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
@@ -4716,6 +4736,13 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
return ret;
}
+static void arm_smmu_disable_action(void *data)
+{
+ struct arm_smmu_device *smmu = data;
+
+ arm_smmu_device_disable(smmu);
+}
+
static void arm_smmu_write_strtab(struct arm_smmu_device *smmu)
{
struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
@@ -4921,10 +4948,14 @@ static void arm_smmu_device_iidr_probe(struct arm_smmu_device *smmu)
static void arm_smmu_get_httu(struct arm_smmu_device *smmu, u32 reg)
{
- u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA | ARM_SMMU_FEAT_HD);
+ u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA | ARM_SMMU_FEAT_HD |
+ ARM_SMMU_FEAT_HAFT);
u32 hw_features = 0;
switch (FIELD_GET(IDR0_HTTU, reg)) {
+ case IDR0_HTTU_ACCESS_DIRTY_HAFT:
+ hw_features |= ARM_SMMU_FEAT_HAFT;
+ fallthrough;
case IDR0_HTTU_ACCESS_DIRTY:
hw_features |= ARM_SMMU_FEAT_HD;
fallthrough;
@@ -5256,6 +5287,9 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
smmu->features |= ARM_SMMU_FEAT_COHERENCY;
switch (FIELD_GET(ACPI_IORT_SMMU_V3_HTTU_OVERRIDE, iort_smmu->flags)) {
+ case IDR0_HTTU_ACCESS_DIRTY_HAFT:
+ smmu->features |= ARM_SMMU_FEAT_HAFT;
+ fallthrough;
case IDR0_HTTU_ACCESS_DIRTY:
smmu->features |= ARM_SMMU_FEAT_HD;
fallthrough;
@@ -5472,7 +5506,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
/* Initialise in-memory data structures */
ret = arm_smmu_init_structures(smmu);
if (ret)
- goto err_free_iopf;
+ return ret;
/* Record our private device structure */
platform_set_drvdata(pdev, smmu);
@@ -5482,30 +5516,30 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
/* Reset the device */
ret = arm_smmu_device_reset(smmu);
+ if (ret) {
+ arm_smmu_device_disable(smmu);
+ return ret;
+ }
+
+ /* Register last so it unwinds first, while the CMDQ is still up. */
+ ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu);
if (ret)
- goto err_disable;
+ return ret;
/* And we're up. Go go go! */
ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
"smmu3.%pa", &ioaddr);
if (ret)
- goto err_disable;
+ return ret;
ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev);
if (ret) {
dev_err(dev, "Failed to register iommu\n");
- goto err_free_sysfs;
+ iommu_device_sysfs_remove(&smmu->iommu);
+ return ret;
}
return 0;
-
-err_free_sysfs:
- iommu_device_sysfs_remove(&smmu->iommu);
-err_disable:
- arm_smmu_device_disable(smmu);
-err_free_iopf:
- iopf_queue_free(smmu->evtq.iopf);
- return ret;
}
static void arm_smmu_device_remove(struct platform_device *pdev)
@@ -5514,9 +5548,6 @@ static void arm_smmu_device_remove(struct platform_device *pdev)
iommu_device_unregister(&smmu->iommu);
iommu_device_sysfs_remove(&smmu->iommu);
- arm_smmu_device_disable(smmu);
- iopf_queue_free(smmu->evtq.iopf);
- ida_destroy(&smmu->vmid_map);
}
static void arm_smmu_device_shutdown(struct platform_device *pdev)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 1c4877ada1ee..6859570248e9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -40,6 +40,7 @@ struct arm_vsmmu;
#define IDR0_HTTU GENMASK(7, 6)
#define IDR0_HTTU_ACCESS 1
#define IDR0_HTTU_ACCESS_DIRTY 2
+#define IDR0_HTTU_ACCESS_DIRTY_HAFT 3
#define IDR0_COHACC (1 << 4)
#define IDR0_TTF GENMASK(3, 2)
#define IDR0_TTF_AARCH64 2
@@ -369,6 +370,7 @@ static inline unsigned int arm_smmu_cdtab_l2_idx(unsigned int ssid)
#define CTXDESC_CD_0_ASET (1UL << 47)
#define CTXDESC_CD_0_ASID GENMASK_ULL(63, 48)
+#define CTXDESC_CD_1_HAFT (1UL << 3)
#define CTXDESC_CD_1_TTB0_MASK GENMASK_ULL(51, 4)
/*
@@ -922,6 +924,7 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_HD (1 << 22)
#define ARM_SMMU_FEAT_S2FWB (1 << 23)
#define ARM_SMMU_FEAT_BBML2 (1 << 24)
+#define ARM_SMMU_FEAT_HAFT (1 << 25)
u32 features;
#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index aaf9ce38bd93..e5f52c0f6e9a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -56,6 +56,8 @@
#define VINTF_ENABLED BIT(0)
#define TEGRA241_VINTF_SID_MATCH(s) (0x0040 + 0x4*(s))
+#define VINTF_SID_MATCH_VIRT_SID GENMASK(20, 1)
+#define VINTF_SID_MATCH_ENABLE BIT(0)
#define TEGRA241_VINTF_SID_REPLACE(s) (0x0080 + 0x4*(s))
#define TEGRA241_VINTF_LVCMDQ_ERR_MAP_64(m) \
@@ -1189,7 +1191,7 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev)
u64 virt_sid = vdev->virt_id;
int sidx;
- if (virt_sid > UINT_MAX)
+ if (virt_sid > FIELD_MAX(VINTF_SID_MATCH_VIRT_SID))
return -EINVAL;
WARN_ON_ONCE(master->num_streams != 1);
@@ -1201,7 +1203,9 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev)
return sidx;
writel(stream->id, REG_VINTF(vintf, SID_REPLACE(sidx)));
- writel(virt_sid << 1 | 0x1, REG_VINTF(vintf, SID_MATCH(sidx)));
+ writel(FIELD_PREP(VINTF_SID_MATCH_VIRT_SID, virt_sid) |
+ VINTF_SID_MATCH_ENABLE,
+ REG_VINTF(vintf, SID_MATCH(sidx)));
dev_dbg(vintf->cmdqv->dev,
"VINTF%u: allocated SID_REPLACE%d for pSID=%x, vSID=%x\n",
vintf->idx, sidx, stream->id, (u32)virt_sid);
diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h
index c2752151c80a..07ec2b3ab986 100644
--- a/drivers/iommu/generic_pt/iommu_pt.h
+++ b/drivers/iommu/generic_pt/iommu_pt.h
@@ -224,7 +224,7 @@ phys_addr_t DOMAIN_NS(iova_to_phys)(struct iommu_domain *domain,
ret = make_range(common_from_iommu(iommu_table), &range, iova, 1);
if (ret)
- return ret;
+ return 0;
ret = pt_walk_range(&range, __iova_to_phys, &res);
/* PHYS_ADDR_MAX would be a better error code */
diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 767ec092accd..e32685402f74 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -915,6 +915,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;
@@ -928,8 +940,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 849d06dfe1ae..4e7ba60f3a0a 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -57,7 +57,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))
@@ -2482,10 +2482,11 @@ static 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");
/*
@@ -2496,7 +2497,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 775f1c4ae346..2cee36138d6e 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -1354,6 +1354,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)
{
@@ -1366,6 +1367,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/iommu-sva.c b/drivers/iommu/iommu-sva.c
index bc7c7232a43e..9742cb593577 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -145,8 +145,8 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
list_add(&domain->next, &iommu_mm->sva_domains);
out:
refcount_set(&handle->users, 1);
- mutex_unlock(&iommu_sva_lock);
handle->dev = dev;
+ mutex_unlock(&iommu_sva_lock);
return handle;
out_free_domain:
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e8f13dcebbde..23a531595835 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -390,7 +390,7 @@ int iommu_mock_device_add(struct device *dev, struct iommu_device *iommu)
rc = device_add(dev);
if (rc)
- iommu_fwspec_free(dev);
+ dev_iommu_free(dev);
return rc;
}
EXPORT_SYMBOL_GPL(iommu_mock_device_add);
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 2895e5370910..d488c23fd353 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -1307,8 +1307,8 @@ void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova,
xa_lock(&ioas->iopt.access_list);
xa_for_each(&ioas->iopt.access_list, index, access) {
- if (!iommufd_lock_obj(&access->obj) ||
- iommufd_access_is_internal(access))
+ if (iommufd_access_is_internal(access) ||
+ !iommufd_lock_obj(&access->obj))
continue;
xa_unlock(&ioas->iopt.access_list);
diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
index 71bffece84b5..3459776871e6 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -427,6 +427,8 @@ static int iommufd_take_all_iova_rwsem(struct iommufd_ctx *ictx,
rc = xa_err(xa_store(ioas_list, index, ioas, GFP_KERNEL));
if (rc) {
+ up_write(&ioas->iopt.iova_rwsem);
+ refcount_dec(&ioas->obj.users);
iommufd_release_all_iova_rwsem(ictx, ioas_list);
return rc;
}
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index af07c642a526..727b59799d5f 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -177,6 +177,7 @@ struct mock_dev {
struct device dev;
struct mock_viommu *viommu;
struct rw_semaphore viommu_rwsem;
+ struct rw_semaphore iopf_rwsem;
unsigned long flags;
unsigned long vdev_id;
int id;
@@ -997,6 +998,7 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
return ERR_PTR(-ENOMEM);
init_rwsem(&mdev->viommu_rwsem);
+ init_rwsem(&mdev->iopf_rwsem);
device_initialize(&mdev->dev);
mdev->flags = dev_flags;
mdev->dev.release = mock_dev_release;
@@ -1022,7 +1024,9 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
goto err_put;
}
+ down_write(&mdev->iopf_rwsem);
rc = iommu_mock_device_add(&mdev->dev, &mock_iommu.iommu_dev);
+ up_write(&mdev->iopf_rwsem);
if (rc)
goto err_put;
return mdev;
@@ -1077,7 +1081,9 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
}
sobj->idev.idev = idev;
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
rc = iommufd_device_attach(idev, IOMMU_NO_PASID, &pt_id);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
if (rc)
goto out_unbind;
@@ -1092,7 +1098,9 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
return 0;
out_detach:
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_device_detach(idev, IOMMU_NO_PASID);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
out_unbind:
iommufd_device_unbind(idev);
out_mdev:
@@ -1136,7 +1144,9 @@ static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
if (IS_ERR(sobj))
return PTR_ERR(sobj);
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
rc = iommufd_device_replace(sobj->idev.idev, IOMMU_NO_PASID, &pt_id);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
if (rc)
goto out_sobj;
@@ -1739,10 +1749,16 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,
{
struct iopf_fault event = {};
struct iommufd_device *idev;
+ struct mock_dev *mdev;
idev = iommufd_get_device(ucmd, cmd->trigger_iopf.dev_id);
if (IS_ERR(idev))
return PTR_ERR(idev);
+ if (!iommufd_selftest_is_mock_dev(idev->dev)) {
+ iommufd_put_object(ucmd->ictx, &idev->obj);
+ return -EINVAL;
+ }
+ mdev = to_mock_dev(idev->dev);
event.fault.prm.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
if (cmd->trigger_iopf.pasid != IOMMU_NO_PASID)
@@ -1753,7 +1769,9 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,
event.fault.prm.grpid = cmd->trigger_iopf.grpid;
event.fault.prm.perm = cmd->trigger_iopf.perm;
+ down_read(&mdev->iopf_rwsem);
iommu_report_device_fault(idev->dev, &event);
+ up_read(&mdev->iopf_rwsem);
iommufd_put_object(ucmd->ictx, &idev->obj);
return 0;
@@ -1861,14 +1879,19 @@ static int iommufd_test_pasid_attach(struct iommufd_ucmd *ucmd,
if (IS_ERR(sobj))
return PTR_ERR(sobj);
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
rc = iommufd_device_attach(sobj->idev.idev, cmd->pasid_attach.pasid,
&cmd->pasid_attach.pt_id);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
if (rc)
goto out_sobj;
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
- if (rc)
+ if (rc) {
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_device_detach(sobj->idev.idev, cmd->pasid_attach.pasid);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
+ }
out_sobj:
iommufd_put_object(ucmd->ictx, &sobj->obj);
@@ -1885,8 +1908,10 @@ static int iommufd_test_pasid_replace(struct iommufd_ucmd *ucmd,
if (IS_ERR(sobj))
return PTR_ERR(sobj);
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
rc = iommufd_device_replace(sobj->idev.idev, cmd->pasid_attach.pasid,
&cmd->pasid_attach.pt_id);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
if (rc)
goto out_sobj;
@@ -1906,7 +1931,9 @@ static int iommufd_test_pasid_detach(struct iommufd_ucmd *ucmd,
if (IS_ERR(sobj))
return PTR_ERR(sobj);
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_device_detach(sobj->idev.idev, cmd->pasid_detach.pasid);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_put_object(ucmd->ictx, &sobj->obj);
return 0;
}
@@ -1917,7 +1944,9 @@ void iommufd_selftest_destroy(struct iommufd_object *obj)
switch (sobj->type) {
case TYPE_IDEV:
+ down_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_device_detach(sobj->idev.idev, IOMMU_NO_PASID);
+ up_write(&sobj->idev.mock_dev->iopf_rwsem);
iommufd_device_unbind(sobj->idev.idev);
mock_dev_destroy(sobj->idev.mock_dev);
break;
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 0ad5ff431d5b..d0d926be7495 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -784,19 +784,25 @@ static int msm_iommu_probe(struct platform_device *pdev)
"msm-smmu.%pa", &ioaddr);
if (ret) {
pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr);
- return ret;
+ goto err_remove_list;
}
ret = iommu_device_register(&iommu->iommu, &msm_iommu_ops, &pdev->dev);
if (ret) {
pr_err("Could not register msm-smmu at %pa\n", &ioaddr);
- return ret;
+ goto err_remove_sysfs;
}
pr_info("device mapped at %p, irq %d with %d ctx banks\n",
iommu->base, iommu->irq, iommu->ncb);
return ret;
+
+err_remove_sysfs:
+ iommu_device_sysfs_remove(&iommu->iommu);
+err_remove_list:
+ list_del(&iommu->dev_node);
+ return ret;
}
static const struct of_device_id msm_iommu_dt_match[] = {
diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
index d957d989c0ce..dead907aa7a2 100644
--- a/drivers/mailbox/qcom-ipcc.c
+++ b/drivers/mailbox/qcom-ipcc.c
@@ -167,7 +167,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;
@@ -180,16 +180,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/md/dm-bufio.c b/drivers/md/dm-bufio.c
index a458b9fd2fcd..d58eed96a8ff 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1287,11 +1287,11 @@ static void free_buffer(struct dm_buffer *b)
* dm-io completion routine. It just calls b->bio.bi_end_io, pretending
* that the request was handled directly with bio interface.
*/
-static void dmio_complete(unsigned long error, void *context)
+static void dmio_complete(unsigned long error, unsigned long unsup, void *context)
{
struct dm_buffer *b = context;
- b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : 0);
+ b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : unlikely(unsup != 0) ? BLK_STS_NOTSUPP : 0);
}
static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector,
@@ -1319,7 +1319,7 @@ static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector,
io_req.mem.ptr.vma = (char *)b->data + offset;
}
- r = dm_io(&io_req, 1, &region, NULL, ioprio);
+ r = dm_io(&io_req, 1, &region, NULL, NULL, ioprio);
if (unlikely(r))
b->end_io(b, errno_to_blk_status(r));
}
@@ -2220,7 +2220,7 @@ int dm_bufio_issue_flush(struct dm_bufio_client *c)
if (WARN_ON_ONCE(dm_bufio_in_request()))
return -EINVAL;
- return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT);
+ return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT);
}
EXPORT_SYMBOL_GPL(dm_bufio_issue_flush);
@@ -2246,7 +2246,7 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c
if (WARN_ON_ONCE(dm_bufio_in_request()))
return -EINVAL; /* discards are optional */
- return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT);
+ return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT);
}
EXPORT_SYMBOL_GPL(dm_bufio_issue_discard);
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 7fe4d19ade4f..ea499adca4ce 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-integrity.c b/drivers/md/dm-integrity.c
index 1f2593f113f6..81d3f42c4f48 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -562,7 +562,7 @@ static int sync_rw_sb(struct dm_integrity_c *ic, blk_opf_t opf)
}
}
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r))
return r;
@@ -1035,12 +1035,14 @@ static void encrypt_journal(struct dm_integrity_c *ic, bool encrypt, unsigned in
return crypt_journal(ic, encrypt, section, n_sections, comp);
}
-static void complete_journal_io(unsigned long error, void *context)
+static void complete_journal_io(unsigned long error, unsigned long unsup, void *context)
{
struct journal_completion *comp = context;
if (unlikely(error != 0))
dm_integrity_io_error(comp->ic, "writing journal", -EIO);
+ else if (unlikely(unsup != 0))
+ dm_integrity_io_error(comp->ic, "writing journal", -EOPNOTSUPP);
complete_journal_op(comp);
}
@@ -1055,7 +1057,7 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf,
if (unlikely(dm_integrity_failed(ic))) {
if (comp)
- complete_journal_io(-1UL, comp);
+ complete_journal_io(-1UL, -1UL, comp);
return;
}
@@ -1080,13 +1082,13 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf,
io_loc.sector = ic->start + SB_SECTORS + sector;
io_loc.count = n_sectors;
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r)) {
dm_integrity_io_error(ic, (opf & REQ_OP_MASK) == REQ_OP_READ ?
"reading journal" : "writing journal", r);
if (comp) {
WARN_ONCE(1, "asynchronous dm_io failed: %d", r);
- complete_journal_io(-1UL, comp);
+ complete_journal_io(-1UL, -1UL, comp);
}
}
}
@@ -1177,7 +1179,7 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u
BUG_ON((target | n_sectors | offset) & (unsigned int)(ic->sectors_per_block - 1));
if (unlikely(dm_integrity_failed(ic))) {
- fn(-1UL, data);
+ fn(-1UL, -1UL, data);
return;
}
@@ -1197,10 +1199,10 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u
io_loc.sector = target;
io_loc.count = n_sectors;
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r)) {
WARN_ONCE(1, "asynchronous dm_io failed: %d", r);
- fn(-1UL, data);
+ fn(-1UL, -1UL, data);
}
}
@@ -1493,12 +1495,14 @@ struct flush_request {
struct completion comp;
};
-static void flush_notify(unsigned long error, void *fr_)
+static void flush_notify(unsigned long error, unsigned long unsup, void *fr_)
{
struct flush_request *fr = fr_;
if (unlikely(error != 0))
dm_integrity_io_error(fr->ic, "flushing disk cache", -EIO);
+ else if (unlikely(unsup != 0))
+ dm_integrity_io_error(fr->ic, "flushing disk cache", -EOPNOTSUPP);
complete(&fr->comp);
}
@@ -1521,7 +1525,7 @@ static void dm_integrity_flush_buffers(struct dm_integrity_c *ic, bool flush_dat
fr.io_reg.count = 0;
fr.ic = ic;
init_completion(&fr.comp);
- r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, NULL, IOPRIO_DEFAULT);
BUG_ON(r);
}
@@ -1837,7 +1841,7 @@ static noinline void integrity_recheck(struct dm_integrity_io *dio, char *checks
buffer_offset = (sector - io_loc.sector) << SECTOR_SHIFT;
io_loc.count = round_up(io_loc.count, alignment);
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r)) {
dio->bi_status = errno_to_blk_status(r);
goto free_ret;
@@ -2890,7 +2894,7 @@ release_flush_bios:
}
}
-static void complete_copy_from_journal(unsigned long error, void *context)
+static void complete_copy_from_journal(unsigned long error, unsigned long unsup, void *context)
{
struct journal_io *io = context;
struct journal_completion *comp = io->comp;
@@ -2900,6 +2904,8 @@ static void complete_copy_from_journal(unsigned long error, void *context)
mempool_free(io, &ic->journal_io_mempool);
if (unlikely(error != 0))
dm_integrity_io_error(ic, "copying from journal", -EIO);
+ else if (unlikely(unsup != 0))
+ dm_integrity_io_error(ic, "copying from journal", -EOPNOTSUPP);
complete_journal_op(comp);
}
@@ -3215,7 +3221,7 @@ next_chunk:
io_loc.sector = get_data_sector(ic, area, offset);
io_loc.count = n_sectors;
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r)) {
dm_integrity_io_error(ic, "reading data", r);
goto err;
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 1db565b37620..777b917be400 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -33,6 +33,7 @@ struct dm_io_client {
*/
struct io {
unsigned long error_bits;
+ unsigned long unsup_bits;
atomic_t count;
struct dm_io_client *client;
io_notify_fn callback;
@@ -119,6 +120,7 @@ static void retrieve_io_and_region_from_bio(struct bio *bio, struct io **io,
static void complete_io(struct io *io)
{
unsigned long error_bits = io->error_bits;
+ unsigned long unsup_bits = io->unsup_bits;
io_notify_fn fn = io->callback;
void *context = io->context;
@@ -127,13 +129,17 @@ static void complete_io(struct io *io)
io->vma_invalidate_size);
mempool_free(io, &io->client->pool);
- fn(error_bits, context);
+ fn(error_bits, unsup_bits, context);
}
static void dec_count(struct io *io, unsigned int region, blk_status_t error)
{
- if (error)
- set_bit(region, &io->error_bits);
+ if (unlikely(error)) {
+ if (error == BLK_STS_NOTSUPP || error == BLK_STS_INVAL)
+ set_bit(region, &io->unsup_bits);
+ else
+ set_bit(region, &io->error_bits);
+ }
if (atomic_dec_and_test(&io->count))
complete_io(io);
@@ -170,12 +176,11 @@ struct dpages {
struct page **p, unsigned long *len, unsigned int *offset);
void (*next_page)(struct dpages *dp);
- union {
- unsigned int context_u;
- struct bvec_iter context_bi;
- };
+ unsigned int context_u;
void *context_ptr;
+ struct bio *orig_bio;
+
void *vma_invalidate_address;
unsigned long vma_invalidate_size;
};
@@ -211,44 +216,6 @@ static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned int o
}
/*
- * Functions for getting the pages from a bvec.
- */
-static void bio_get_page(struct dpages *dp, struct page **p,
- unsigned long *len, unsigned int *offset)
-{
- struct bio_vec bvec = bvec_iter_bvec((struct bio_vec *)dp->context_ptr,
- dp->context_bi);
-
- *p = bvec.bv_page;
- *len = bvec.bv_len;
- *offset = bvec.bv_offset;
-
- /* avoid figuring it out again in bio_next_page() */
- dp->context_bi.bi_sector = (sector_t)bvec.bv_len;
-}
-
-static void bio_next_page(struct dpages *dp)
-{
- unsigned int len = (unsigned int)dp->context_bi.bi_sector;
-
- bvec_iter_advance((struct bio_vec *)dp->context_ptr,
- &dp->context_bi, len);
-}
-
-static void bio_dp_init(struct dpages *dp, struct bio *bio)
-{
- dp->get_page = bio_get_page;
- dp->next_page = bio_next_page;
-
- /*
- * We just use bvec iterator to retrieve pages, so it is ok to
- * access the bvec table directly here
- */
- dp->context_ptr = bio->bi_io_vec;
- dp->context_bi = bio->bi_iter;
-}
-
-/*
* Functions for getting the pages from a VMA.
*/
static void vm_get_page(struct dpages *dp,
@@ -332,6 +299,21 @@ static void do_region(const blk_opf_t opf, unsigned int region,
return;
}
+ if (dp->orig_bio) {
+ bio = bio_alloc_clone(where->bdev, dp->orig_bio, GFP_NOIO,
+ &io->client->bios);
+ bio->bi_iter.bi_sector = where->sector;
+ bio->bi_iter.bi_size = where->count << SECTOR_SHIFT;
+ bio->bi_opf = opf;
+ bio->bi_end_io = endio;
+ bio->bi_ioprio = ioprio;
+ store_io_and_region_in_bio(bio, io, region);
+
+ atomic_inc(&io->count);
+ submit_bio(bio);
+ return;
+ }
+
/*
* where->count may be zero if op holds a flush and we need to
* send a zero-sized flush.
@@ -418,6 +400,7 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions,
io = mempool_alloc(&client->pool, GFP_NOIO);
io->error_bits = 0;
+ io->unsup_bits = 0;
atomic_set(&io->count, 1); /* see dispatch_io() */
io->client = client;
io->callback = fn;
@@ -431,20 +414,23 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions,
struct sync_io {
unsigned long error_bits;
+ unsigned long unsup_bits;
struct completion wait;
};
-static void sync_io_complete(unsigned long error, void *context)
+static void sync_io_complete(unsigned long error, unsigned long unsup, void *context)
{
struct sync_io *sio = context;
sio->error_bits = error;
+ sio->unsup_bits = unsup;
complete(&sio->wait);
}
static int sync_io(struct dm_io_client *client, unsigned int num_regions,
struct dm_io_region *where, blk_opf_t opf, struct dpages *dp,
- unsigned long *error_bits, unsigned short ioprio)
+ unsigned long *error_bits, unsigned long *unsup_bits,
+ unsigned short ioprio)
{
struct sync_io sio;
@@ -457,8 +443,10 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
if (error_bits)
*error_bits = sio.error_bits;
+ if (unsup_bits)
+ *unsup_bits = sio.unsup_bits;
- return sio.error_bits ? -EIO : 0;
+ return sio.error_bits ? -EIO : sio.unsup_bits ? -EOPNOTSUPP : 0;
}
static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
@@ -468,6 +456,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
dp->vma_invalidate_address = NULL;
dp->vma_invalidate_size = 0;
+ dp->orig_bio = NULL;
switch (io_req->mem.type) {
case DM_IO_PAGE_LIST:
@@ -475,7 +464,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
break;
case DM_IO_BIO:
- bio_dp_init(dp, io_req->mem.ptr.bio);
+ /*
+ * The destination bios clone this bio's biovec directly, so
+ * there are no per-page accessors to set up here.
+ */
+ dp->orig_bio = io_req->mem.ptr.bio;
break;
case DM_IO_VMA:
@@ -500,7 +493,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
int dm_io(struct dm_io_request *io_req, unsigned int num_regions,
struct dm_io_region *where, unsigned long *sync_error_bits,
- unsigned short ioprio)
+ unsigned long *sync_unsup_bits, unsigned short ioprio)
{
int r;
struct dpages dp;
@@ -516,7 +509,8 @@ int dm_io(struct dm_io_request *io_req, unsigned int num_regions,
if (!io_req->notify.fn)
return sync_io(io_req->client, num_regions, where,
- io_req->bi_opf, &dp, sync_error_bits, ioprio);
+ io_req->bi_opf, &dp, sync_error_bits,
+ sync_unsup_bits, ioprio);
async_io(io_req->client, num_regions, where, io_req->bi_opf, &dp,
io_req->notify.fn, io_req->notify.context, ioprio);
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 96c8b8ff61c2..5c9c24a3dcd9 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -517,16 +517,16 @@ static int run_complete_job(struct kcopyd_job *job)
return 0;
}
-static void complete_io(unsigned long error, void *context)
+static void complete_io(unsigned long error, unsigned long unsup, void *context)
{
struct kcopyd_job *job = context;
struct dm_kcopyd_client *kc = job->kc;
io_job_finish(kc->throttle);
- if (error) {
+ if (unlikely((error | unsup) != 0)) {
if (op_is_write(job->op))
- job->write_err |= error;
+ job->write_err |= error | unsup;
else
job->read_err = 1;
@@ -578,9 +578,9 @@ static int run_io_job(struct kcopyd_job *job)
io_job_start(job->kc->throttle);
if (job->op == REQ_OP_READ)
- r = dm_io(&io_req, 1, &job->source, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &job->source, NULL, NULL, IOPRIO_DEFAULT);
else
- r = dm_io(&io_req, job->num_dests, job->dests, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, job->num_dests, job->dests, NULL, NULL, IOPRIO_DEFAULT);
return r;
}
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index 9d5918eb0a30..3ca35aa4e057 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-log.c b/drivers/md/dm-log.c
index 2ddeb4250c59..4c114fc5ef83 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -300,7 +300,7 @@ static int rw_header(struct log_c *lc, enum req_op op)
{
lc->io_req.bi_opf = op;
- return dm_io(&lc->io_req, 1, &lc->header_location, NULL, IOPRIO_DEFAULT);
+ return dm_io(&lc->io_req, 1, &lc->header_location, NULL, NULL, IOPRIO_DEFAULT);
}
static int flush_header(struct log_c *lc)
@@ -313,7 +313,7 @@ static int flush_header(struct log_c *lc)
lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
- return dm_io(&lc->io_req, 1, &null_location, NULL, IOPRIO_DEFAULT);
+ return dm_io(&lc->io_req, 1, &null_location, NULL, NULL, IOPRIO_DEFAULT);
}
static int read_header(struct log_c *log)
diff --git a/drivers/md/dm-pcache/cache.c b/drivers/md/dm-pcache/cache.c
index bb1ada31e483..e68dacb69459 100644
--- a/drivers/md/dm-pcache/cache.c
+++ b/drivers/md/dm-pcache/cache.c
@@ -119,6 +119,10 @@ int cache_pos_decode(struct pcache_cache *cache,
return -EIO;
pos->cache_seg = &cache->segments[latest.cache_seg_id];
+
+ if (latest.seg_off >= pos->cache_seg->segment.data_size)
+ return -EIO;
+
pos->seg_off = latest.seg_off;
*seq = latest.header.seq;
*index = (latest_addr - pos_onmedia);
@@ -247,6 +251,13 @@ static int get_seg_id(struct pcache_cache *cache,
} else {
*seg_id = cache->cache_info.seg_id;
}
+
+ if (*seg_id >= cache_dev->seg_num) {
+ pcache_dev_err(pcache, "invalid segment id %u from cache device (seg_num %u)\n",
+ *seg_id, cache_dev->seg_num);
+ ret = -EIO;
+ goto err;
+ }
}
return 0;
err:
@@ -262,6 +273,13 @@ static int cache_segs_init(struct pcache_cache *cache)
int ret;
u32 i;
+ if (cache_info->n_segs > cache->cache_dev->seg_num) {
+ pcache_dev_err(CACHE_TO_PCACHE(cache),
+ "cache_info n_segs %u exceeds cache device segments %u\n",
+ cache_info->n_segs, cache->cache_dev->seg_num);
+ return -EIO;
+ }
+
for (i = 0; i < cache_info->n_segs; i++) {
ret = get_seg_id(cache, prev_cache_seg, new_cache, &seg_id);
if (ret)
diff --git a/drivers/md/dm-pcache/cache.h b/drivers/md/dm-pcache/cache.h
index 27613b56be54..afc112b79496 100644
--- a/drivers/md/dm-pcache/cache.h
+++ b/drivers/md/dm-pcache/cache.h
@@ -491,6 +491,27 @@ static inline u32 cache_key_data_crc(struct pcache_cache_key *key)
return crc32c(PCACHE_CRC_SEED, data, key->len);
}
+/**
+ * kset_onmedia_valid - Validate a kset header read from the cache device.
+ * @kset_onmedia: Pointer to the kset copied from on-media metadata.
+ *
+ * The magic and CRC are attacker-computable (fixed public seed). A non-last
+ * kset stores key_num keys inline, and cache_kset_crc() and the replay loop
+ * read struct_size(.., data, key_num) bytes from a buffer sized for
+ * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use.
+ */
+static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia *kset_onmedia)
+{
+ if (kset_onmedia->magic != PCACHE_KSET_MAGIC)
+ return false;
+
+ if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) &&
+ kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX)
+ return false;
+
+ return true;
+}
+
static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia *kset_onmedia)
{
u32 crc_size;
diff --git a/drivers/md/dm-pcache/cache_dev.c b/drivers/md/dm-pcache/cache_dev.c
index ece689e6ce59..f0259353ee39 100644
--- a/drivers/md/dm-pcache/cache_dev.c
+++ b/drivers/md/dm-pcache/cache_dev.c
@@ -242,6 +242,8 @@ int cache_dev_start(struct dm_pcache *pcache)
struct pcache_cache_dev *cache_dev = &pcache->cache_dev;
struct pcache_sb sb;
bool format = false;
+ u32 seg_num;
+ u64 max_segs;
int ret;
mutex_init(&cache_dev->seg_lock);
@@ -269,7 +271,25 @@ int cache_dev_start(struct dm_pcache *pcache)
goto dax_release;
cache_dev->sb_flags = le32_to_cpu(sb.flags);
- ret = cache_dev_init(cache_dev, le32_to_cpu(sb.seg_num));
+
+ /*
+ * seg_num is read from the crc32c-only superblock, so whoever supplies
+ * the cache device controls it. It is the ceiling every later on-media
+ * segment id is validated against, so bound it against what the device
+ * physically holds before it is trusted, or a forged seg_num lets a
+ * segment id address past the DAX mapping.
+ */
+ seg_num = le32_to_cpu(sb.seg_num);
+ max_segs = (bdev_nr_bytes(cache_dev->dm_dev->bdev) - PCACHE_SEGMENTS_OFF) /
+ PCACHE_SEG_SIZE;
+ if (seg_num == 0 || seg_num > max_segs || seg_num > PCACHE_CACHE_SEGS_MAX) {
+ pcache_dev_err(pcache, "invalid seg_num %u from cache device (device holds %llu, max %u)\n",
+ seg_num, max_segs, (u32)PCACHE_CACHE_SEGS_MAX);
+ ret = -EIO;
+ goto dax_release;
+ }
+
+ ret = cache_dev_init(cache_dev, seg_num);
if (ret)
goto dax_release;
diff --git a/drivers/md/dm-pcache/cache_gc.c b/drivers/md/dm-pcache/cache_gc.c
index 94f8b276a021..3088cfef951b 100644
--- a/drivers/md/dm-pcache/cache_gc.c
+++ b/drivers/md/dm-pcache/cache_gc.c
@@ -37,18 +37,18 @@ static bool need_gc(struct pcache_cache *cache, struct pcache_cache_pos *dirty_t
kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->gc_kset_onmedia_buf;
- to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - key_tail->seg_off);
+ to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(key_tail));
ret = copy_mc_to_kernel(kset_onmedia, key_addr, to_copy);
if (ret) {
pcache_dev_err(pcache, "error to read kset: %d", ret);
return false;
}
- /* Check if kset_onmedia is corrupted */
- if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
- pcache_dev_debug(pcache, "gc error: magic is not as expected. key_tail: %u:%u magic: %llx, expected: %llx\n",
+ /* Reject a corrupted or out-of-bounds kset before reading its keys */
+ if (!kset_onmedia_valid(kset_onmedia)) {
+ pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: %u:%u magic: %llx, key_num: %u\n",
key_tail->cache_seg->cache_seg_id, key_tail->seg_off,
- kset_onmedia->magic, PCACHE_KSET_MAGIC);
+ kset_onmedia->magic, kset_onmedia->key_num);
return false;
}
diff --git a/drivers/md/dm-pcache/cache_key.c b/drivers/md/dm-pcache/cache_key.c
index e068e878231b..51dd1ccf4e2f 100644
--- a/drivers/md/dm-pcache/cache_key.c
+++ b/drivers/md/dm-pcache/cache_key.c
@@ -97,6 +97,14 @@ int cache_key_decode(struct pcache_cache *cache,
key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id];
key->cache_pos.seg_off = key_onmedia->cache_seg_off;
+ if ((u64)key->cache_pos.seg_off + key->len >
+ key->cache_pos.cache_seg->segment.data_size) {
+ pcache_dev_err(pcache, "key seg_off %u + len %u exceeds segment data size %u\n",
+ key->cache_pos.seg_off, key->len,
+ key->cache_pos.cache_seg->segment.data_size);
+ return -EIO;
+ }
+
key->seg_gen = key_onmedia->seg_gen;
key->flags = key_onmedia->flags;
@@ -728,18 +736,17 @@ static int kset_replay(struct pcache_cache *cache, struct pcache_cache_kset_onme
goto err;
}
- __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
-
/* Check if the segment generation is valid for insertion. */
if (key->seg_gen < key->cache_pos.cache_seg->gen) {
cache_key_put(key);
- } else {
- cache_subtree = get_subtree(&cache->req_key_tree, key->off);
- spin_lock(&cache_subtree->tree_lock);
- cache_key_insert(&cache->req_key_tree, key, true);
- spin_unlock(&cache_subtree->tree_lock);
+ continue;
}
+ __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
+ cache_subtree = get_subtree(&cache->req_key_tree, key->off);
+ spin_lock(&cache_subtree->tree_lock);
+ cache_key_insert(&cache->req_key_tree, key, true);
+ spin_unlock(&cache_subtree->tree_lock);
cache_seg_get(key->cache_pos.cache_seg);
}
@@ -754,7 +761,7 @@ int cache_replay(struct pcache_cache *cache)
struct pcache_cache_pos pos_tail;
struct pcache_cache_pos *pos;
struct pcache_cache_kset_onmedia *kset_onmedia;
- u32 to_copy, count = 0;
+ u32 to_copy, count = 0, last_hops = 0;
int ret = 0;
kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL);
@@ -771,14 +778,14 @@ int cache_replay(struct pcache_cache *cache)
__set_bit(pos->cache_seg->cache_seg_id, cache->seg_map);
while (true) {
- to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off);
+ to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(pos));
ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy);
if (ret) {
ret = -EIO;
goto out;
}
- if (kset_onmedia->magic != PCACHE_KSET_MAGIC ||
+ if (!kset_onmedia_valid(kset_onmedia) ||
kset_onmedia->crc != cache_kset_crc(kset_onmedia)) {
break;
}
@@ -789,6 +796,11 @@ int cache_replay(struct pcache_cache *cache)
pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id);
+ if (++last_hops > cache->n_segs) {
+ ret = -EIO;
+ goto out;
+ }
+
next_seg = &cache->segments[kset_onmedia->next_cache_seg_id];
pos->cache_seg = next_seg;
diff --git a/drivers/md/dm-pcache/cache_segment.c b/drivers/md/dm-pcache/cache_segment.c
index 9d92e2b067ed..c698ebbc626d 100644
--- a/drivers/md/dm-pcache/cache_segment.c
+++ b/drivers/md/dm-pcache/cache_segment.c
@@ -243,8 +243,16 @@ struct pcache_cache_segment *get_cache_segment(struct pcache_cache *cache)
spin_lock(&cache->seg_map_lock);
again:
- seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg);
- if (seg_id == cache->n_segs) {
+ /*
+ * Only allocate initialized segments. cache_segs_init() initializes
+ * cache_info.n_segs of the cache->n_segs device segments; a forged
+ * smaller cache_info.n_segs leaves the rest as zeroed structs whose data
+ * pointer is NULL. Bounding the search to cache_info.n_segs keeps such a
+ * segment from reaching cache_kset_close(), which writes through it.
+ */
+ seg_id = find_next_zero_bit(cache->seg_map, cache->cache_info.n_segs,
+ cache->last_cache_seg);
+ if (seg_id == cache->cache_info.n_segs) {
/* reset the hint of ->last_cache_seg and retry */
if (cache->last_cache_seg) {
cache->last_cache_seg = 0;
diff --git a/drivers/md/dm-pcache/cache_writeback.c b/drivers/md/dm-pcache/cache_writeback.c
index 87a82b3fe836..3df8687e2382 100644
--- a/drivers/md/dm-pcache/cache_writeback.c
+++ b/drivers/md/dm-pcache/cache_writeback.c
@@ -48,18 +48,18 @@ static inline bool is_cache_clean(struct pcache_cache *cache, struct pcache_cach
addr = cache_pos_addr(dirty_tail);
kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf;
- to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - dirty_tail->seg_off);
+ to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(dirty_tail));
ret = copy_mc_to_kernel(kset_onmedia, addr, to_copy);
if (ret) {
pcache_dev_err(pcache, "error to read kset: %d", ret);
return true;
}
- /* Check if the magic number matches the expected value */
- if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
- pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not expected: %llx\n",
+ /* Reject a corrupted or out-of-bounds kset before reading its keys */
+ if (!kset_onmedia_valid(kset_onmedia)) {
+ pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset magic: %llx, key_num: %u\n",
dirty_tail->cache_seg->cache_seg_id, dirty_tail->seg_off,
- kset_onmedia->magic, PCACHE_KSET_MAGIC);
+ kset_onmedia->magic, kset_onmedia->key_num);
return true;
}
diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c
index d5cfd162c063..645fc27d82ba 100644
--- a/drivers/md/dm-pcache/dm_pcache.c
+++ b/drivers/md/dm-pcache/dm_pcache.c
@@ -439,13 +439,13 @@ static int dm_pcache_message(struct dm_target *ti, unsigned int argc,
char **argv, char *result, unsigned int maxlen)
{
struct dm_pcache *pcache = ti->private;
- unsigned long val;
+ u8 val;
if (argc != 2)
goto err;
if (!strcasecmp(argv[0], "gc_percent")) {
- if (kstrtoul(argv[1], 10, &val))
+ if (kstrtou8(argv[1], 10, &val))
goto err;
return pcache_cache_set_gc_percent(&pcache->cache, val);
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index de5c00704e69..da2a5e2002ec 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -258,7 +258,7 @@ out:
static int mirror_flush(struct dm_target *ti)
{
struct mirror_set *ms = ti->private;
- unsigned long error_bits;
+ unsigned long error_bits, unsup_bits;
unsigned int i;
struct dm_io_region io[MAX_NR_MIRRORS];
@@ -277,8 +277,8 @@ static int mirror_flush(struct dm_target *ti)
}
error_bits = -1;
- dm_io(&io_req, ms->nr_mirrors, io, &error_bits, IOPRIO_DEFAULT);
- if (unlikely(error_bits != 0)) {
+ dm_io(&io_req, ms->nr_mirrors, io, &error_bits, &unsup_bits, IOPRIO_DEFAULT);
+ if (unlikely((error_bits | unsup_bits) != 0)) {
for (i = 0; i < ms->nr_mirrors; i++)
if (test_bit(i, &error_bits))
fail_mirror(ms->mirror + i,
@@ -511,7 +511,7 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio)
* Reads
*---------------------------------------------------------------
*/
-static void read_callback(unsigned long error, void *context)
+static void read_callback(unsigned long error, unsigned long unsup, void *context)
{
struct bio *bio = context;
struct mirror *m;
@@ -520,6 +520,8 @@ static void read_callback(unsigned long error, void *context)
bio_set_m(bio, NULL);
if (likely(!error)) {
+ if (unlikely(unsup != 0))
+ bio->bi_status = BLK_STS_INVAL;
bio_endio(bio);
return;
}
@@ -553,7 +555,7 @@ static void read_async_bio(struct mirror *m, struct bio *bio)
map_region(&io, m, bio);
bio_set_m(bio, m);
- BUG_ON(dm_io(&io_req, 1, &io, NULL, IOPRIO_DEFAULT));
+ BUG_ON(dm_io(&io_req, 1, &io, NULL, NULL, IOPRIO_DEFAULT));
}
static inline int region_in_sync(struct mirror_set *ms, region_t region,
@@ -600,7 +602,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads)
* NOSYNC: increment pending, just write to the default mirror
*---------------------------------------------------------------------
*/
-static void write_callback(unsigned long error, void *context)
+static void write_callback(unsigned long error, unsigned long unsup, void *context)
{
unsigned int i;
struct bio *bio = context;
@@ -617,7 +619,7 @@ static void write_callback(unsigned long error, void *context)
* This way we handle both writes to SYNC and NOSYNC
* regions with the same code.
*/
- if (likely(!error)) {
+ if (likely(!(error | unsup))) {
bio_endio(bio);
return;
}
@@ -632,6 +634,12 @@ static void write_callback(unsigned long error, void *context)
return;
}
+ if (!error && unsup) {
+ bio->bi_status = BLK_STS_INVAL;
+ bio_endio(bio);
+ return;
+ }
+
for (i = 0; i < ms->nr_mirrors; i++)
if (test_bit(i, &error))
fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR);
@@ -680,7 +688,7 @@ static void do_write(struct mirror_set *ms, struct bio *bio)
*/
bio_set_m(bio, get_default_mirror(ms));
- BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, IOPRIO_DEFAULT));
+ BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, NULL, IOPRIO_DEFAULT));
}
static void do_writes(struct mirror_set *ms, struct bio_list *writes)
@@ -1262,7 +1270,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio,
return DM_ENDIO_DONE;
}
- if (*error == BLK_STS_NOTSUPP)
+ if (*error == BLK_STS_NOTSUPP || *error == BLK_STS_INVAL)
goto out;
if (bio->bi_opf & REQ_RAHEAD)
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index aa239ccda270..e86d929b0e95 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -223,7 +223,7 @@ static void do_metadata(struct work_struct *work)
{
struct mdata_req *req = container_of(work, struct mdata_req, work);
- req->result = dm_io(req->io_req, 1, req->where, NULL, IOPRIO_DEFAULT);
+ req->result = dm_io(req->io_req, 1, req->where, NULL, NULL, IOPRIO_DEFAULT);
}
/*
@@ -247,7 +247,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, blk_opf_t opf,
struct mdata_req req;
if (!metadata)
- return dm_io(&io_req, 1, &where, NULL, IOPRIO_DEFAULT);
+ return dm_io(&io_req, 1, &where, NULL, NULL, IOPRIO_DEFAULT);
req.where = &where;
req.io_req = &io_req;
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index 5df710061a11..beabbe3b39f3 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -178,8 +178,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 5952f02de1e6..e5b507b4fa7b 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-verity-target.c b/drivers/md/dm-verity-target.c
index 1b0763091254..c67dea40c9cd 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -395,7 +395,7 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
io_loc.bdev = v->data_dev->bdev;
io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT);
io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT);
- r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r))
goto free_ret;
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index 493f5202ad04..f02305de1c88 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -474,12 +474,14 @@ struct io_notify {
atomic_t count;
};
-static void writecache_notify_io(unsigned long error, void *context)
+static void writecache_notify_io(unsigned long error, unsigned long unsup, void *context)
{
struct io_notify *endio = context;
if (unlikely(error != 0))
writecache_error(endio->wc, -EIO, "error writing metadata");
+ else if (unlikely(unsup != 0))
+ writecache_error(endio->wc, -EOPNOTSUPP, "error writing metadata");
BUG_ON(atomic_read(&endio->count) <= 0);
if (atomic_dec_and_test(&endio->count))
complete(&endio->c);
@@ -530,11 +532,11 @@ static void ssd_commit_flushed(struct dm_writecache *wc, bool wait_for_ios)
req.notify.context = &endio;
/* writing via async dm-io (implied by notify.fn above) won't return an error */
- (void) dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+ (void) dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
i = j;
}
- writecache_notify_io(0, &endio);
+ writecache_notify_io(0, 0, &endio);
wait_for_completion_io(&endio.c);
if (wait_for_ios)
@@ -567,7 +569,7 @@ static void ssd_commit_superblock(struct dm_writecache *wc)
req.notify.fn = NULL;
req.notify.context = NULL;
- r = dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r))
writecache_error(wc, r, "error writing superblock");
}
@@ -595,7 +597,7 @@ static void writecache_disk_flush(struct dm_writecache *wc, struct dm_dev *dev)
req.client = wc->dm_io;
req.notify.fn = NULL;
- r = dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+ r = dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
if (unlikely(r))
writecache_error(wc, r, "error flushing metadata: %d", r);
}
@@ -989,7 +991,7 @@ static int writecache_read_metadata(struct dm_writecache *wc, sector_t n_sectors
req.client = wc->dm_io;
req.notify.fn = NULL;
- return dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+ return dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
}
static void writecache_resume(struct dm_target *ti)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d1465bcd86c8..4dd133445539 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1920,6 +1920,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);
diff --git a/drivers/md/persistent-data/dm-array.c b/drivers/md/persistent-data/dm-array.c
index 8f8792e55806..961fa3c1439a 100644
--- a/drivers/md/persistent-data/dm-array.c
+++ b/drivers/md/persistent-data/dm-array.c
@@ -38,6 +38,14 @@ struct array_block {
*/
#define CSUM_XOR 595846735
+/*
+ * Each array block can hold this many values.
+ */
+static uint32_t calc_max_entries(size_t value_size, size_t size_of_block)
+{
+ return (size_of_block - sizeof(struct array_block)) / value_size;
+}
+
static void array_block_prepare_for_write(const struct dm_block_validator *v,
struct dm_block *b,
size_t size_of_block)
@@ -55,6 +63,7 @@ static int array_block_check(const struct dm_block_validator *v,
size_t size_of_block)
{
struct array_block *bh_le = dm_block_data(b);
+ uint32_t nr_entries, max_entries, value_size;
__le32 csum_disk;
if (dm_block_location(b) != le64_to_cpu(bh_le->blocknr)) {
@@ -74,6 +83,26 @@ static int array_block_check(const struct dm_block_validator *v,
return -EILSEQ;
}
+ nr_entries = le32_to_cpu(bh_le->nr_entries);
+ max_entries = le32_to_cpu(bh_le->max_entries);
+ value_size = le32_to_cpu(bh_le->value_size);
+
+ if (!value_size) {
+ DMERR_LIMIT("%s failed: value_size is zero", __func__);
+ return -EILSEQ;
+ }
+
+ if (max_entries != calc_max_entries(value_size, size_of_block)) {
+ DMERR_LIMIT("%s failed: max_entries %u invalid for value_size %u",
+ __func__, max_entries, value_size);
+ return -EILSEQ;
+ }
+
+ if (nr_entries > max_entries) {
+ DMERR_LIMIT("%s failed: too many entries", __func__);
+ return -EILSEQ;
+ }
+
return 0;
}
@@ -139,14 +168,6 @@ static void dec_ablock_entries(struct dm_array_info *info, struct array_block *a
}
/*
- * Each array block can hold this many values.
- */
-static uint32_t calc_max_entries(size_t value_size, size_t size_of_block)
-{
- return (size_of_block - sizeof(struct array_block)) / value_size;
-}
-
-/*
* Allocate a new array block. The caller will need to unlock block.
*/
static int alloc_ablock(struct dm_array_info *info, size_t size_of_block,
@@ -225,6 +246,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 +316,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/raid10.c b/drivers/md/raid10.c
index 0a3cfdd3f5df..54cddb3a98cd 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3365,7 +3365,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
struct md_rdev *rdev = conf->mirrors[j].rdev;
if (rdev == NULL || test_bit(Faulty, &rdev->flags)) {
- still_degraded = false;
+ still_degraded = true;
break;
}
}
diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c
index 1ec0cece0a5b..8c2fc232202d 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/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index 318e8330f16a..ff9d50fb05fd 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -63,11 +63,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/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 5516b2bbb08f..d750bf10febe 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -1071,25 +1071,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/mfd/cgbc-core.c b/drivers/mfd/cgbc-core.c
index 10bb4b414c34..2becaf797646 100644
--- a/drivers/mfd/cgbc-core.c
+++ b/drivers/mfd/cgbc-core.c
@@ -364,9 +364,9 @@ static void cgbc_remove(struct platform_device *pdev)
{
struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
- cgbc_session_release(cgbc);
-
mfd_remove_devices(&pdev->dev);
+
+ cgbc_session_release(cgbc);
}
static struct platform_driver cgbc_driver = {
diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c
index 8de974ddac3e..93a3dd93404d 100644
--- a/drivers/mfd/qnap-mcu.c
+++ b/drivers/mfd/qnap-mcu.c
@@ -56,6 +56,7 @@ struct qnap_mcu_reply {
* @reply: Reply data structure
* @variant: Device variant specific information
* @version: MCU firmware version
+ * @rx: Receive buffer the reply is assembled in
*/
struct qnap_mcu {
struct serdev_device *serdev;
@@ -63,6 +64,7 @@ struct qnap_mcu {
struct qnap_mcu_reply reply;
const struct qnap_mcu_variant *variant;
u8 version[QNAP_MCU_VERSION_LEN];
+ u8 rx[QNAP_MCU_RX_BUFFER_SIZE];
};
/*
@@ -214,19 +216,18 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
const u8 *cmd_data, size_t cmd_data_size,
u8 *reply_data, size_t reply_data_size)
{
- unsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];
size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE;
struct qnap_mcu_reply *reply = &mcu->reply;
int ret = 0;
- if (length > sizeof(rx)) {
+ if (length > sizeof(mcu->rx)) {
dev_err(&mcu->serdev->dev, "expected data too big for receive buffer");
return -EINVAL;
}
guard(mutex)(&mcu->bus_lock);
- reply->data = rx;
+ reply->data = mcu->rx;
reply->length = length;
reply->received = 0;
reinit_completion(&reply->done);
@@ -242,15 +243,15 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
return -ETIMEDOUT;
}
- if (!qnap_mcu_verify_checksum(rx, reply->received)) {
+ if (!qnap_mcu_verify_checksum(mcu->rx, reply->received)) {
dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n");
return -EPROTO;
}
- if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received))
+ if (qnap_mcu_reply_is_any_error(mcu, mcu->rx, reply->received))
return -EPROTO;
- memcpy(reply_data, rx, reply_data_size);
+ memcpy(reply_data, mcu->rx, reply_data_size);
return 0;
}
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index b5bda477ebfc..77888965d91e 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -1627,6 +1627,7 @@ static void sm501_pci_remove(struct pci_dev *dev)
release_mem_region(sm->io_res->start, 0x100);
pci_disable_device(dev);
+ kfree(sm);
}
static void sm501_plat_remove(struct platform_device *dev)
@@ -1637,6 +1638,7 @@ static void sm501_plat_remove(struct platform_device *dev)
iounmap(sm->regs);
release_mem_region(sm->io_res->start, 0x100);
+ kfree(sm);
}
static const struct pci_device_id sm501_pci_tbl[] = {
diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c
index 3960506eb7ab..c3b474450d15 100644
--- a/drivers/misc/nsm.c
+++ b/drivers/misc/nsm.c
@@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm)
goto cleanup;
}
- msg->resp.len = len;
+ msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data));
rc = 0;
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 8c049f8355cd..4910d0f444ba 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);
release:
@@ -1199,6 +1205,10 @@ static void via_sd_remove(struct pci_dev *pcidev)
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);
+
timer_delete_sync(&sdhost->timer);
cancel_work_sync(&sdhost->finish_bh_work);
diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c
index 9dd50b20c000..d2e92bab55d2 100644
--- a/drivers/net/dsa/realtek/rtl83xx.c
+++ b/drivers/net/dsa/realtek/rtl83xx.c
@@ -321,7 +321,7 @@ void rtl83xx_reset_assert(struct realtek_priv *priv)
"Failed to assert the switch reset control: %pe\n",
ERR_PTR(ret));
- gpiod_set_value(priv->reset, true);
+ gpiod_set_value_cansleep(priv->reset, true);
}
void rtl83xx_reset_deassert(struct realtek_priv *priv)
@@ -334,7 +334,7 @@ void rtl83xx_reset_deassert(struct realtek_priv *priv)
"Failed to deassert the switch reset control: %pe\n",
ERR_PTR(ret));
- gpiod_set_value(priv->reset, false);
+ gpiod_set_value_cansleep(priv->reset, false);
}
/**
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 208a894d6190..39eb6ab5f805 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -13473,10 +13473,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/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index bc7b37cb74a7..54bae193d1f6 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -485,6 +485,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct bnxt_sw_tx_bd *tx_buf;
__le32 lflags = 0;
skb_frag_t *frag;
+ netdev_tx_t ret;
i = skb_get_queue_mapping(skb);
if (unlikely(i >= bp->tx_nr_rings)) {
@@ -501,17 +502,19 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb_shinfo(skb)->nr_frags > TX_MAX_FRAGS) {
netdev_warn_once(dev, "SKB has too many (%d) fragments, max supported is %d. SKB will be linearized.\n",
skb_shinfo(skb)->nr_frags, TX_MAX_FRAGS);
- if (skb_linearize(skb)) {
- dev_kfree_skb_any(skb);
- dev_core_stats_tx_dropped_inc(dev);
- return NETDEV_TX_OK;
- }
+ if (skb_linearize(skb))
+ goto tx_free;
}
#endif
if (skb_is_gso(skb) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) &&
- !(bp->flags & BNXT_FLAG_UDP_GSO_CAP))
- return bnxt_sw_udp_gso_xmit(bp, txr, txq, skb);
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) {
+ ret = bnxt_sw_udp_gso_xmit(bp, txr, txq, skb);
+ if (txr->kick_pending)
+ bnxt_txr_db_kick(bp, txr, txr->tx_prod);
+
+ return ret;
+ }
free_size = bnxt_tx_avail(bp, txr);
if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
index f317f60414e8..f7e18bea0fb8 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
@@ -223,9 +223,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
netdev_tx_sent_queue(txq, skb->len);
WRITE_ONCE(txr->tx_prod, prod);
- /* Sync BDs before doorbell */
- wmb();
- bnxt_db_write(bp, &txr->tx_db, prod);
+ txr->kick_pending = 1;
if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh))
netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 74526002d52b..42027665f2a9 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -35,6 +35,7 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac)
{
unsigned long flags;
+ netdev_lock(mal->napi.dev);
spin_lock_irqsave(&mal->lock, flags);
MAL_DBG(mal, "reg(%08x, %08x)" NL,
@@ -44,18 +45,20 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac)
if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
(mal->rx_chan_mask & commac->rx_chan_mask)) {
spin_unlock_irqrestore(&mal->lock, flags);
+ netdev_unlock(mal->napi.dev);
printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
mal->index);
return -EBUSY;
}
if (list_empty(&mal->list))
- napi_enable(&mal->napi);
+ napi_enable_locked(&mal->napi);
mal->tx_chan_mask |= commac->tx_chan_mask;
mal->rx_chan_mask |= commac->rx_chan_mask;
list_add(&commac->list, &mal->list);
spin_unlock_irqrestore(&mal->lock, flags);
+ netdev_unlock(mal->napi.dev);
return 0;
}
@@ -64,7 +67,9 @@ void mal_unregister_commac(struct mal_instance *mal,
struct mal_commac *commac)
{
unsigned long flags;
+ bool disable_napi;
+ netdev_lock(mal->napi.dev);
spin_lock_irqsave(&mal->lock, flags);
MAL_DBG(mal, "unreg(%08x, %08x)" NL,
@@ -73,10 +78,12 @@ void mal_unregister_commac(struct mal_instance *mal,
mal->tx_chan_mask &= ~commac->tx_chan_mask;
mal->rx_chan_mask &= ~commac->rx_chan_mask;
list_del_init(&commac->list);
- if (list_empty(&mal->list))
- napi_disable(&mal->napi);
+ disable_napi = list_empty(&mal->list);
spin_unlock_irqrestore(&mal->lock, flags);
+ if (disable_napi)
+ napi_disable_locked(&mal->napi);
+ netdev_unlock(mal->napi.dev);
}
int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 6fbc0441c4b8..6fc6605d2054 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -2263,6 +2263,11 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq
data_offset = wqe_offset & (page_size - 1);
page_idx = wqe_offset >> rq->mpwqe.page_shift;
+ if (unlikely(cqe_bcnt <= ETH_ZLEN + 2 * VLAN_HLEN)) {
+ match = false;
+ flush = true;
+ }
+
if (*skb &&
!(match && mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt,
page_size))) {
diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 58d3e55def48..dc804e111564 100644
--- a/drivers/net/ethernet/nvidia/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -6221,7 +6221,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;
@@ -6236,7 +6236,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 c11e0d8f98aa..a02624f6d6d5 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/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 5e56ec9b1013..3ee4c6108189 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -1028,6 +1028,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];
@@ -1123,6 +1124,8 @@ struct ravb_private {
int msg_enable;
int speed;
int emac_irq;
+ int err_irq;
+ int mgmt_irq;
unsigned no_avb_link:1;
unsigned avb_link_active_low:1;
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 5f88733094d0..ea1c7e536791 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1779,7 +1779,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;
@@ -2885,11 +2885,13 @@ static int ravb_setup_irqs(struct ravb_private *priv)
return error;
if (info->err_mgmt_irqs) {
- error = ravb_setup_irq(priv, "err_a", "err_a", NULL, ravb_multi_interrupt);
+ error = ravb_setup_irq(priv, "err_a", "err_a", &priv->err_irq,
+ ravb_multi_interrupt);
if (error)
return error;
- error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", NULL, ravb_multi_interrupt);
+ error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", &priv->mgmt_irq,
+ ravb_multi_interrupt);
if (error)
return error;
}
@@ -2953,6 +2955,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 226c6c0ab945..43218bc15b15 100644
--- a/drivers/net/ethernet/renesas/ravb_ptp.c
+++ b/drivers/net/ethernet/renesas/ravb_ptp.c
@@ -289,16 +289,17 @@ static const struct ptp_clock_info ravb_ptp_info = {
void ravb_ptp_interrupt(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
+ struct ptp_clock *clock = READ_ONCE(priv->ptp.clock);
u32 gis = ravb_read(ndev, GIS);
gis &= ravb_read(ndev, GIC);
- if (gis & GIS_PTCF) {
+ if ((gis & GIS_PTCF) && clock) {
struct ptp_clock_event event;
event.type = PTP_CLOCK_EXTTS;
event.index = 0;
event.timestamp = ravb_read(ndev, GCPT);
- ptp_clock_event(priv->ptp.clock, &event);
+ ptp_clock_event(clock, &event);
}
if (gis & GIS_PTMF) {
struct ravb_ptp_perout *perout = priv->ptp.perout;
@@ -315,6 +316,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;
@@ -327,15 +329,45 @@ 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;
+ }
+
+ WRITE_ONCE(priv->ptp.clock, clock);
+ if (clock)
+ WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock));
+}
+
+static void ravb_ptp_disable(struct net_device *ndev)
+{
+ ravb_write(ndev, 0, GIC);
+ ravb_write(ndev, 0, GIS);
+}
+
+static void ravb_ptp_sync_irqs(struct net_device *ndev)
+{
+ struct ravb_private *priv = netdev_priv(ndev);
+
+ synchronize_irq(ndev->irq);
+ if (priv->info->err_mgmt_irqs) {
+ synchronize_irq(priv->err_irq);
+ synchronize_irq(priv->mgmt_irq);
+ }
}
void ravb_ptp_stop(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
+ struct ptp_clock *clock;
- ravb_write(ndev, 0, GIC);
- ravb_write(ndev, 0, GIS);
+ WRITE_ONCE(priv->ptp.phc_index, -1);
+ clock = xchg(&priv->ptp.clock, NULL);
+
+ ravb_ptp_disable(ndev);
+ ravb_ptp_sync_irqs(ndev);
- ptp_clock_unregister(priv->ptp.clock);
+ if (clock)
+ ptp_clock_unregister(clock);
}
diff --git a/drivers/net/ipa/ipa_modem.c b/drivers/net/ipa/ipa_modem.c
index 9b136f6b8b4a..d84c1dbd3b1a 100644
--- a/drivers/net/ipa/ipa_modem.c
+++ b/drivers/net/ipa/ipa_modem.c
@@ -266,13 +266,29 @@ void ipa_modem_suspend(struct net_device *netdev)
* the modem. We can't enable the queue directly in ipa_modem_resume()
* because transmits restart the instant the queue is awakened; but the
* device power state won't be ACTIVE until *after* ipa_modem_resume()
- * returns.
+ * returns. A transmit restarted before that would stop the queue
+ * again and get -EINPROGRESS from pm_runtime_get(), and with this
+ * work having already run, nothing would ever wake the queue again.
+ * So wait for the resume to complete before waking the queue.
*/
static void ipa_modem_wake_queue_work(struct work_struct *work)
{
struct ipa_priv *priv = container_of(work, struct ipa_priv, work);
+ struct device *dev = priv->ipa->dev;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ /* Wake the queue even if the device could not be resumed, so
+ * that pending packets are dropped by the transmit path rather
+ * than stranded behind a stopped queue.
+ */
netif_wake_queue(priv->tx->netdev);
+
+ if (ret < 0)
+ pm_runtime_put_noidle(dev);
+ else
+ (void)pm_runtime_put_autosuspend(dev);
}
/** ipa_modem_resume() - resume callback for runtime_pm
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index 029a4a532a10..9c171697e762 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -144,6 +144,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++;
@@ -155,13 +158,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
skb->ip_summed = CHECKSUM_NONE;
skb_record_rx_queue(skb, q->qid);
- 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;
@@ -199,8 +196,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_netdev_queue *q, int size)
{
- if (__netif_subqueue_stopped(ndev, q->qid) ||
- (ntb_transport_tx_free_entry(q->qp) >= size))
+ if (__netif_subqueue_stopped(ndev, q->qid))
+ return -EBUSY;
+
+ if (ntb_transport_tx_free_entry(q->qp) >= size)
return 0;
return __ntb_netdev_maybe_stop_tx(ndev, q, size);
@@ -256,21 +255,30 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
q = &dev->queues[qid];
- ntb_netdev_maybe_stop_tx(ndev, q, tx_stop);
+ if (unlikely(ntb_netdev_maybe_stop_tx(ndev, q, tx_stop)))
+ return NETDEV_TX_BUSY;
rc = ntb_transport_tx_enqueue(q->qp, skb, skb->data, skb->len);
- if (rc)
- goto err;
+ if (rc) {
+ if (rc == -EAGAIN || rc == -EBUSY) {
+ netif_stop_subqueue(ndev, q->qid);
+ mod_timer(&q->tx_timer,
+ jiffies + usecs_to_jiffies(tx_time));
+ return NETDEV_TX_BUSY;
+ }
+
+ goto drop;
+ }
/* check for next submit */
ntb_netdev_maybe_stop_tx(ndev, q, 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/phy/phylink.c b/drivers/net/phy/phylink.c
index 18d2ead97aa5..7397169236fd 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -965,7 +965,7 @@ static unsigned int phylink_inband_caps(struct phylink *pl,
return 0;
pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
- if (!pcs)
+ if (IS_ERR_OR_NULL(pcs))
return 0;
return phylink_pcs_inband_caps(pcs, interface);
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index faae711cf793..85b2438e8923 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;
@@ -908,7 +913,7 @@ static void slip_close(struct tty_struct *tty)
#endif
/* Flush network side */
unregister_netdev(sl->dev);
- /* This will complete via sl_free_netdev */
+ /* sl_uninit() has dropped the slip_devs[] entry by now */
}
static void slip_hangup(struct tty_struct *tty)
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index 98893732bc6e..2a1728621887 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -626,6 +626,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);
@@ -647,6 +655,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;
}
@@ -691,6 +702,7 @@ err_stop_rings:
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)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index fed9dfdfcc3b..5bbe3123979e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1107,11 +1107,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
@@ -1822,7 +1827,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:
@@ -1842,7 +1853,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 94cdb61dca83..8178a8758cd3 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1360,6 +1360,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/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index cc0f2c45fc3a..62f663c0daa2 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -754,6 +754,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/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 9f7ed1d293a0..381801af3ac9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -1827,17 +1827,18 @@ gotpkt:
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/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index ca5a8140908a..6bd5b6d84b2a 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -1511,10 +1511,10 @@ out_destroy_workqueue:
priv->workqueue = NULL;
out_uninit_drv:
iwl_uninit_drv(priv);
-out_free_eeprom_blob:
- kfree(priv->eeprom_blob);
out_free_eeprom:
kfree(priv->nvm_data);
+out_free_eeprom_blob:
+ kfree(priv->eeprom_blob);
out_leave_trans:
iwl_trans_op_mode_leave(priv->trans);
out_free_hw:
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index 9460d5352b23..19196848778c 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/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index fc619acbb40d..67f56e428d9a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -1239,11 +1239,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/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index e8fe86f93309..bbb2fedacb25 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -2917,8 +2917,15 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf)
return ret;
res = (struct mt7915_mcu_eeprom_info *)skb->data;
- if (!buf)
- buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
+ if (!buf) {
+ u32 addr = le32_to_cpu(res->addr);
+
+ if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ buf = dev->mt76.eeprom.data + addr;
+ }
memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE);
dev_kfree_skb(skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index c7a07c727444..9b58ffec3649 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1323,6 +1323,7 @@ void mt7925_mac_reset_work(struct work_struct *work)
cancel_delayed_work_sync(&dev->mphy.mac_work);
cancel_delayed_work_sync(&pm->ps_work);
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
cancel_work_sync(&pm->wake_work);
for (i = 0; i < 10; i++) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 2b6cc8e253c0..3beb2c1fbec9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1640,6 +1640,7 @@ static int mt7925_suspend(struct ieee80211_hw *hw,
cancel_delayed_work_sync(&phy->mt76->mac_work);
cancel_delayed_work_sync(&dev->pm.ps_work);
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
mt792x_mutex_acquire(dev);
@@ -2484,10 +2485,19 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw,
}
}
+static void mt7925_stop(struct ieee80211_hw *hw, bool suspend)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
+
+ mt792x_stop(hw, suspend);
+}
+
const struct ieee80211_ops mt7925_ops = {
.tx = mt792x_tx,
.start = mt7925_start,
- .stop = mt792x_stop,
+ .stop = mt7925_stop,
.add_interface = mt7925_add_interface,
.remove_interface = mt792x_remove_interface,
.config = mt7925_config,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index ea64303283ed..084baadfa183 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -48,6 +48,7 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
mt76_for_each_q_rx(&dev->mt76, i)
napi_disable(&dev->mt76.napi[i]);
cancel_delayed_work_sync(&pm->ps_work);
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
cancel_work_sync(&pm->wake_work);
cancel_work_sync(&dev->reset_work);
@@ -517,6 +518,7 @@ static int mt7925_pci_suspend(struct device *device)
dev->hif_resumed = false;
flush_work(&dev->reset_work);
cancel_delayed_work_sync(&pm->ps_work);
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
cancel_work_sync(&pm->wake_work);
mt7925_roc_abort_sync(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
index e9f58492bf7d..a798164b9a05 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -269,6 +269,7 @@ static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state)
pm->suspended = true;
dev->hif_resumed = false;
flush_work(&dev->reset_work);
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, false);
ret = wait_event_timeout(dev->wait,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index ac05f7d75d63..ec33521db564 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -150,6 +150,12 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default)
goto out;
}
+ if (fw->size < MT7996_EEPROM_SIZE) {
+ dev_err(dev->mt76.dev, "Invalid default bin size\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
if (!use_default && mt7996_eeprom_variant_valid(dev, fw->data))
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 0eebc8182ca9..730fd8e2fa05 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -1240,6 +1240,30 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta,
}
static void
+mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t)
+{
+ u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t);
+ __le32 *txwi = (__le32 *)txwi_ptr;
+ __le32 *txp;
+ dma_addr_t addr;
+ u32 val;
+
+ if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) {
+ mt76_connac_txp_skb_unmap(mdev, t);
+ return;
+ }
+
+ txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE);
+ val = le32_to_cpu(txp[3]);
+ addr = le32_to_cpu(txp[2]);
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32;
+#endif
+ dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val),
+ DMA_TO_DEVICE);
+}
+
+static void
mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
struct ieee80211_link_sta *link_sta,
struct mt76_wcid *wcid, struct list_head *free_list)
@@ -1248,7 +1272,7 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
__le32 *txwi;
u16 wcid_idx;
- mt76_connac_txp_skb_unmap(mdev, t);
+ mt7996_txp_skb_unmap(mdev, t);
if (!t->skb)
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 2e83f4b79c87..a1bae5db8500 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -4352,11 +4352,18 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
event = (struct mt7996_mcu_eeprom_access_event *)skb->data;
if (event->valid) {
u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
+ u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE :
+ MT7996_EEPROM_BLOCK_SIZE;
addr = le32_to_cpu(event->addr);
- if (!buf)
+ if (!buf) {
+ if (addr > dev->mt76.eeprom.size - block) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
buf = (u8 *)dev->mt76.eeprom.data + addr;
+ }
switch (mode) {
case EEPROM_MODE_EFUSE:
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
index 070c0431c482..4a5989172a5e 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
@@ -1652,7 +1652,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 1d21c468a236..a766712187f7 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -1445,7 +1445,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/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 646fe76b086e..4f4bda3e0ce2 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -5838,14 +5838,19 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv,
{
struct sk_buff *skb;
unsigned long flags;
- int pending = 0;
spin_lock_irqsave(&priv->rx_urb_lock, flags);
if (!priv->shutdown) {
list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list);
priv->rx_urb_pending_count++;
- pending = priv->rx_urb_pending_count;
+ /*
+ * Arm the worker under rx_urb_lock so this is atomic with the
+ * shutdown check: moving it out of the lock would let a
+ * completion arm the work after rtl8xxxu_stop() canceled it.
+ */
+ if (priv->rx_urb_pending_count > RTL8XXXU_RX_URB_PENDING_WATER)
+ schedule_work(&priv->rx_urb_wq);
} else {
skb = (struct sk_buff *)rx_urb->urb.context;
dev_kfree_skb_irq(skb);
@@ -5853,9 +5858,6 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv,
}
spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
-
- if (pending > RTL8XXXU_RX_URB_PENDING_WATER)
- schedule_work(&priv->rx_urb_wq);
}
static void rtl8xxxu_rx_urb_work(struct work_struct *work)
@@ -7507,6 +7509,13 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw, bool suspend)
priv->shutdown = true;
spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
+ /*
+ * Cancel before killing rx_anchor: the worker re-anchors every URB
+ * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running
+ * after the kill could submit a URB that escapes it.
+ */
+ cancel_work_sync(&priv->rx_urb_wq);
+
usb_kill_anchored_urbs(&priv->rx_anchor);
usb_kill_anchored_urbs(&priv->tx_anchor);
if (priv->usb_interrupts)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c
index 7bafb051d5ec..88e8cc590c54 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c
@@ -145,8 +145,10 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw)
/* for firmware buf */
rtlpriv->rtlhal.pfirmware = kmalloc(0x8000, GFP_KERNEL);
- if (!rtlpriv->rtlhal.pfirmware)
- return -ENOMEM;
+ if (!rtlpriv->rtlhal.pfirmware) {
+ err = -ENOMEM;
+ goto error;
+ }
rtlpriv->max_fw_size = 0x8000;
pr_info("Driver for Realtek RTL8192DU WLAN interface\n");
@@ -160,10 +162,14 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw)
pr_err("Failed to request firmware!\n");
kfree(rtlpriv->rtlhal.pfirmware);
rtlpriv->rtlhal.pfirmware = NULL;
- return err;
+ goto error;
}
return 0;
+
+error:
+ rtl92du_deinit_shared_data(hw);
+ return err;
}
static void rtl92du_deinit_sw_vars(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c
index 743ce0cfffe6..c608c51f1b78 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c
@@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee80211_hw *hw,
if (sta) {
sta_entry = (struct rtl_sta_info *)sta->drv_priv;
tid = ieee80211_get_tid(hdr);
- agg_state = sta_entry->tids[tid].agg.agg_state;
+ if (tid < MAX_TID_COUNT)
+ agg_state = sta_entry->tids[tid].agg.agg_state;
ampdu_density = sta->deflink.ht_cap.ampdu_density;
}
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index a30467228912..69f2840fed09 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1834,7 +1834,7 @@ int rtw_pci_probe(struct pci_dev *pdev,
ret = rtw_pci_napi_init(rtwdev);
if (ret) {
rtw_err(rtwdev, "failed to setup NAPI\n");
- goto err_pci_declaim;
+ goto err_destroy_rsrc;
}
ret = rtw_chip_info_setup(rtwdev);
@@ -1866,6 +1866,8 @@ int rtw_pci_probe(struct pci_dev *pdev,
err_destroy_pci:
rtw_pci_napi_deinit(rtwdev);
+
+err_destroy_rsrc:
rtw_pci_destroy(rtwdev, pdev);
err_pci_declaim:
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
index 9d747a060b98..8786bbb421c2 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.c
+++ b/drivers/net/wireless/realtek/rtw88/tx.c
@@ -619,6 +619,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/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 5547888d7e67..a85b9c39c257 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5373,6 +5373,7 @@ enum rtw89_flags {
RTW89_FLAG_CHANGING_INTERFACE,
RTW89_FLAG_HW_RFKILL_STATE,
RTW89_FLAG_UNPLUGGED,
+ RTW89_FLAG_SHUTDOWN,
NUM_OF_RTW89_FLAGS,
};
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index aade5c5b79e8..9baedfde7085 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -1979,7 +1979,8 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw)
lockdep_assert_wiphy(hw->wiphy);
/* wl_disable GPIO get floating when entering LPS */
- if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+ if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags) ||
+ test_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags))
return;
rtw89_core_rfkill_poll(rtwdev, false);
diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
index 102bae488180..61c54b52be1f 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.c
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -4874,6 +4874,19 @@ void rtw89_pci_remove(struct pci_dev *pdev)
}
EXPORT_SYMBOL(rtw89_pci_remove);
+void rtw89_pci_shutdown(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct rtw89_dev *rtwdev;
+
+ if (!hw)
+ return;
+
+ rtwdev = hw->priv;
+ set_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags);
+}
+EXPORT_SYMBOL(rtw89_pci_shutdown);
+
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek PCI 802.11ax wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h
index c3f2d0df5846..0728120d721f 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.h
+++ b/drivers/net/wireless/realtek/rtw89/pci.h
@@ -1751,6 +1751,7 @@ struct pci_device_id;
int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);
void rtw89_pci_remove(struct pci_dev *pdev);
+void rtw89_pci_shutdown(struct pci_dev *pdev);
void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume);
void rtw89_pci_ops_reset(struct rtw89_dev *rtwdev);
int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c
index 640672eb0d26..61e6c997b206 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c
@@ -93,6 +93,7 @@ static struct pci_driver rtw89_8851be_driver = {
.id_table = rtw89_8851be_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
index 64306cdc1ee4..e6fbcf75f1ad 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
@@ -95,6 +95,7 @@ static struct pci_driver rtw89_8852ae_driver = {
.id_table = rtw89_8852ae_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c
index 5bc0a6a99d1d..25463342ac89 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c
@@ -97,6 +97,7 @@ static struct pci_driver rtw89_8852be_driver = {
.id_table = rtw89_8852be_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
index 49a72ca835ac..bf22be739f39 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
@@ -99,6 +99,7 @@ static struct pci_driver rtw89_8852bte_driver = {
.id_table = rtw89_8852bte_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
index 3c64c0539205..b8d4d9f02686 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
@@ -122,6 +122,7 @@ static struct pci_driver rtw89_8852ce_driver = {
.id_table = rtw89_8852ce_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
index 5527a8db393b..54bcd3f3d008 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
@@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922ae_driver = {
.id_table = rtw89_8922ae_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops_be,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c
index a1a81c338be3..65476a35a323 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c
@@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922de_driver = {
.id_table = rtw89_8922de_id_table,
.probe = rtw89_pci_probe,
.remove = rtw89_pci_remove,
+ .shutdown = rtw89_pci_shutdown,
.driver.pm = &rtw89_pm_ops_be,
.err_handler = &rtw89_pci_err_handler,
};
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index f59f926d4bfa..f9caa1a653c5 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1719,9 +1719,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) {
@@ -1768,15 +1775,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_on_stack(struct ntb_queue_entry *entry, void __iomem *offset)
@@ -1950,15 +1955,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_pkts++;
@@ -2348,9 +2344,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/label.c b/drivers/nvdimm/label.c
index 4218e3ac4a2a..ec12ce72cfe2 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -202,7 +202,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/core.c b/drivers/nvme/host/core.c
index 453c1f0b2dd0..ee993dfa1e41 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -5195,7 +5195,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 04363b9c4489..cd76a1be2038 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2318,7 +2318,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/pci.c b/drivers/nvme/host/pci.c
index 69932d640b53..d094717c17a0 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2400,6 +2400,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 dd40798bc248..06d46f72df5c 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -80,6 +80,7 @@ struct nvme_tcp_request {
struct bio *curr_bio;
struct iov_iter iter;
+ u32 data_recvd;
/* send state */
size_t offset;
@@ -615,6 +616,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)
{
@@ -634,6 +658,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++;
@@ -644,6 +671,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);
@@ -654,7 +682,8 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
return -ENOENT;
}
- if (!blk_rq_payload_bytes(rq)) {
+ 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);
@@ -748,6 +777,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",
@@ -956,6 +992,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) {
@@ -964,6 +1001,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++;
@@ -1012,6 +1051,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++;
}
@@ -2744,6 +2786,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/of/base.c b/drivers/of/base.c
index 6e7a42dedad3..477017ed6f49 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1967,7 +1967,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/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 7a4da9fae1ea..71b0ebc0e472 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -401,7 +401,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 cfc8fa403dad..89816a2bd7cd 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -2119,6 +2119,7 @@ static bool hv_pcie_init_dev_msi_info(struct device *dev, struct irq_domain *dom
info->ops->msi_prepare = hv_msi_prepare;
chip->irq_set_affinity = irq_chip_set_affinity_parent;
+ chip->irq_retrigger = irq_chip_retrigger_hierarchy;
if (IS_ENABLED(CONFIG_X86))
chip->flags |= IRQCHIP_MOVE_DEFERRED;
diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c
index f9a34f323ad8..fd1a11b4c8eb 100644
--- a/drivers/pci/controller/plda/pcie-plda-host.c
+++ b/drivers/pci/controller/plda/pcie-plda-host.c
@@ -419,6 +419,8 @@ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
return plda_allocate_msi_domains(port);
}
+static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie);
+
int plda_init_interrupts(struct platform_device *pdev,
struct plda_pcie_rp *port,
const struct plda_event *event)
@@ -440,14 +442,17 @@ int plda_init_interrupts(struct platform_device *pdev,
}
port->irq = platform_get_irq(pdev, 0);
- if (port->irq < 0)
- return -ENODEV;
+ if (port->irq < 0) {
+ ret = -ENODEV;
+ goto err_irq_domain_deinit;
+ }
for_each_set_bit(i, &port->events_bitmap, port->num_events) {
event_irq = irq_create_mapping(port->event_domain, i);
if (!event_irq) {
dev_err(dev, "failed to map hwirq %d\n", i);
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
}
if (event->request_event_irq)
@@ -459,7 +464,7 @@ int plda_init_interrupts(struct platform_device *pdev,
if (ret) {
dev_err(dev, "failed to request IRQ %d\n", event_irq);
- return ret;
+ goto err_irq_domain_deinit;
}
}
@@ -467,7 +472,8 @@ int plda_init_interrupts(struct platform_device *pdev,
event->intx_event);
if (!port->intx_irq) {
dev_err(dev, "failed to map INTx interrupt\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
}
/* Plug the INTx chained handler */
@@ -475,8 +481,11 @@ int plda_init_interrupts(struct platform_device *pdev,
port->msi_irq = irq_create_mapping(port->event_domain,
event->msi_event);
- if (!port->msi_irq)
- return -ENXIO;
+ if (!port->msi_irq) {
+ dev_err(dev, "failed to map MSI interrupt\n");
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
+ }
/* Plug the MSI chained handler */
irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port);
@@ -485,6 +494,11 @@ int plda_init_interrupts(struct platform_device *pdev,
irq_set_chained_handler_and_data(port->irq, plda_handle_event, port);
return 0;
+
+err_irq_domain_deinit:
+ plda_pcie_irq_domain_deinit(port);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(plda_init_interrupts);
@@ -559,9 +573,27 @@ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie)
{
- irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
- irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
- irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
+ u32 i, event_irq;
+
+ if (pcie->irq > 0)
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+ if (pcie->msi_irq > 0)
+ irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
+ if (pcie->intx_irq > 0)
+ irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
+
+ for_each_set_bit(i, &pcie->events_bitmap, pcie->num_events) {
+ event_irq = irq_find_mapping(pcie->event_domain, i);
+ if (event_irq) {
+ devm_free_irq(pcie->dev, event_irq, pcie);
+ irq_dispose_mapping(event_irq);
+ }
+ }
+
+ if (pcie->intx_irq)
+ irq_dispose_mapping(pcie->intx_irq);
+ if (pcie->msi_irq)
+ irq_dispose_mapping(pcie->msi_irq);
irq_domain_remove(pcie->msi.dev_domain);
diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c
index 298036c3e7f9..628f8c8d6747 100644
--- a/drivers/pci/controller/plda/pcie-starfive.c
+++ b/drivers/pci/controller/plda/pcie-starfive.c
@@ -304,12 +304,14 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
ret = starfive_pcie_clk_rst_init(pcie);
if (ret)
- return ret;
+ goto err_disable_phy;
if (pcie->vpcie3v3) {
ret = regulator_enable(pcie->vpcie3v3);
- if (ret)
+ if (ret) {
dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n");
+ goto err_clk_rst;
+ }
}
if (pcie->reset_gpio)
@@ -379,6 +381,13 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
dev_info(dev, "port link down\n");
return 0;
+
+err_clk_rst:
+ starfive_pcie_clk_rst_deinit(pcie);
+err_disable_phy:
+ starfive_pcie_disable_phy(pcie);
+
+ return ret;
}
static const struct plda_pcie_host_ops sf_host_ops = {
diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c
index 209373c92e9e..80a9db417dc8 100644
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -870,6 +870,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
{
struct msi_desc *entry;
bool write_msg;
+ u16 cmd;
if (!dev->msix_enabled)
return;
@@ -879,6 +880,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);
scoped_guard (msi_descs_lock, &dev->dev) {
@@ -889,6 +898,7 @@ void __pci_restore_msix_state(struct pci_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 5ec0b245a69b..1be627913a9b 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -718,7 +718,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;
@@ -799,7 +799,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;
@@ -888,12 +888,30 @@ static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ u32 val = 0;
+ int ret;
/* Only support 1, 2 or 4 byte accesses */
if (count != 1 && count != 2 && count != 4)
return -EINVAL;
- return pci_legacy_read(bus, off, (u32 *)buf, count);
+ ret = pci_legacy_read(bus, off, &val, count);
+ if (ret < 0)
+ return ret;
+
+ switch (count) {
+ case 1:
+ buf[0] = *(u8 *)&val;
+ break;
+ case 2:
+ put_unaligned_le16(*(u16 *)&val, buf);
+ break;
+ case 4:
+ put_unaligned_le32(val, buf);
+ break;
+ }
+
+ return ret;
}
/**
@@ -913,12 +931,24 @@ static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj,
char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ u32 val;
- /* Only support 1, 2 or 4 byte accesses */
- if (count != 1 && count != 2 && count != 4)
+ /* Only support 1, 2 or 4 byte accesses. */
+ switch (count) {
+ case 1:
+ val = *(u8 *)buf;
+ break;
+ case 2:
+ val = get_unaligned_le16(buf);
+ break;
+ case 4:
+ val = get_unaligned_le32(buf);
+ break;
+ default:
return -EINVAL;
+ }
- return pci_legacy_write(bus, off, *(u32 *)buf, count);
+ return pci_legacy_write(bus, off, val, count);
}
/**
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index c4fd9c0b2a54..fbb764ffb916 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -428,23 +428,32 @@ void pci_aer_exit(struct pci_dev *dev)
#define AER_AGENT_REQUESTER 1
#define AER_AGENT_COMPLETER 2
#define AER_AGENT_TRANSMITTER 3
+#define AER_AGENT_COMPONENT 4
#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \
- 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP))
+ 0 : PCI_ERR_UNC_COMP_TIME)
#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \
0 : PCI_ERR_UNC_COMP_ABORT)
#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \
- (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0)
+ (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : \
+ (PCI_ERR_UNC_POISON_BLK|PCI_ERR_UNC_ATOMEG| \
+ PCI_ERR_UNC_DMWR_BLK|PCI_ERR_UNC_XLAT_BLK| \
+ PCI_ERR_UNC_TLPPRE))
+#define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \
+ (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN))
#define AER_GET_AGENT(t, e) \
((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \
(e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \
(e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \
+ (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \
AER_AGENT_RECEIVER)
#define AER_PHYSICAL_LAYER_ERROR 0
#define AER_DATA_LINK_LAYER_ERROR 1
#define AER_TRANSACTION_LAYER_ERROR 2
+#define AER_GENERAL_ERROR 3
#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
PCI_ERR_COR_RCVR : 0)
@@ -452,11 +461,14 @@ void pci_aer_exit(struct pci_dev *dev)
(PCI_ERR_COR_BAD_TLP| \
PCI_ERR_COR_BAD_DLLP| \
PCI_ERR_COR_REP_ROLL| \
- PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP)
+ PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN))
+#define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN)
#define AER_GET_LAYER_ERROR(t, e) \
((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \
(e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \
+ (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \
AER_TRANSACTION_LAYER_ERROR)
/*
@@ -471,7 +483,8 @@ static const char * const aer_error_severity_string[] = {
static const char *aer_error_layer[] = {
"Physical Layer",
"Data Link Layer",
- "Transaction Layer"
+ "Transaction Layer",
+ "General",
};
static const char *aer_correctable_error_string[] = {
@@ -548,7 +561,8 @@ static const char *aer_agent_string[] = {
"Receiver ID",
"Requester ID",
"Completer ID",
- "Transmitter ID"
+ "Transmitter ID",
+ "Component ID",
};
#define aer_stats_dev_attr(name, stats_array, strings_array, \
@@ -942,7 +956,8 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
status = aer->uncor_status;
mask = aer->uncor_mask;
info.level = KERN_ERR;
- tlp_header_valid = tlp_header_logged(status, aer->cap_control);
+ tlp_header_valid = tlp_header_logged(status & ~mask,
+ aer->cap_control);
}
info.status = status;
@@ -1336,7 +1351,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i)
pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc);
info->first_error = PCI_ERR_CAP_FEP(aercc);
- if (tlp_header_logged(info->status, aercc)) {
+ if (tlp_header_logged(info->status & ~info->mask, aercc)) {
info->tlp_header_valid = 1;
pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
aer + PCI_ERR_PREFIX_LOG,
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 71ad289fcb8e..f5fd860a0540 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"
@@ -39,13 +41,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)
@@ -122,6 +127,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 b09f27f7846f..0500d5fd4f11 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2507,6 +2507,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s);
+/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s);
+
static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev)
{
pcie_aspm_remove_cap(dev,
@@ -5361,7 +5364,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);
}
}
@@ -6263,6 +6266,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/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index b05d80e849a1..9a33c06d6fc3 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -173,9 +173,9 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
return sw;
}
-static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
+static void tca_blk_put_typec_switch(void *data)
{
- typec_switch_unregister(sw);
+ typec_switch_unregister(data);
}
static void tca_blk_orientation_set(struct tca_blk *tca,
@@ -248,6 +248,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct resource *res;
struct tca_blk *tca;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
@@ -266,17 +267,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
tca->orientation = TYPEC_ORIENTATION_NORMAL;
tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
- return tca;
-}
-
-static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
-{
- struct tca_blk *tca = imx_phy->tca;
-
- if (!tca)
- return;
+ ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
+ if (ret)
+ return ERR_PTR(ret);
- tca_blk_put_typec_switch(tca->sw);
+ return tca;
}
static u32 phy_tx_vref_tune_from_property(u32 percent)
@@ -741,9 +736,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
static void imx8mq_usb_phy_remove(struct platform_device *pdev)
{
- struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
- imx95_usb_phy_put_tca(imx_phy);
}
static struct platform_driver imx8mq_usb_phy_driver = {
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index cbd780556da8..18f5f582d1d7 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -1526,7 +1526,7 @@ static const struct regmap_config samsung_mipi_dcphy_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .max_register = 0x10000,
+ .max_register = 0xfffc,
};
static struct phy *samsung_mipi_dcphy_xlate(struct device *dev,
diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index a10579144c34..e613dce24430 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -475,6 +475,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) {
@@ -502,10 +517,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,
diff --git a/drivers/platform/x86/amd/pmc/mp1_stb.c b/drivers/platform/x86/amd/pmc/mp1_stb.c
index 753d630f3283..1ec0e599df7f 100644
--- a/drivers/platform/x86/amd/pmc/mp1_stb.c
+++ b/drivers/platform/x86/amd/pmc/mp1_stb.c
@@ -157,7 +157,7 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
struct amd_pmc_dev *dev = filp->f_inode->i_private;
u32 fsize, num_samples, val, stb_rdptr_offset = 0;
struct amd_stb_v2_data *stb_data_arr;
- int ret;
+ int ret = 0;
/* Write dummy postcode while reading the STB buffer */
ret = amd_stb_write(dev, AMD_PMC_STB_DUMMY_PC);
@@ -176,22 +176,24 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
* the enhanced dram size. Note that we land here only for the
* platforms that support enhanced dram size reporting.
*/
- if (dump_custom_stb)
- return amd_stb_handle_efr(filp);
+ if (dump_custom_stb) {
+ ret = amd_stb_handle_efr(filp);
+ goto out;
+ }
/* Get the num_samples to calculate the last push location */
ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->stb_arg.s2d_msg_id, true);
- /* Clear msg_port for other SMU operation */
- dev->msg_port = MSG_PORT_PMC;
if (ret) {
dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
- return ret;
+ goto out;
}
fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX);
stb_data_arr = kmalloc_flex(*stb_data_arr, data, fsize);
- if (!stb_data_arr)
- return -ENOMEM;
+ if (!stb_data_arr) {
+ ret = -ENOMEM;
+ goto out;
+ }
stb_data_arr->size = fsize;
@@ -214,7 +216,10 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
filp->private_data = stb_data_arr;
- return 0;
+out:
+ /* Restore the default message port for subsequent SMU operations */
+ dev->msg_port = MSG_PORT_PMC;
+ return ret;
}
static ssize_t amd_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,
@@ -289,7 +294,7 @@ int amd_stb_s2d_init(struct amd_pmc_dev *dev)
u32 phys_addr_low, phys_addr_hi;
u64 stb_phys_addr;
u32 size = 0;
- int ret;
+ int ret = 0;
if (!enable_stb)
return 0;
@@ -306,27 +311,42 @@ int amd_stb_s2d_init(struct amd_pmc_dev *dev)
/* Spill to DRAM feature uses separate SMU message port */
dev->msg_port = MSG_PORT_S2D;
- amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
- if (size != S2D_TELEMETRY_BYTES_MAX)
- return -EIO;
+ ret = amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
+ if (ret)
+ goto out;
+ if (size != S2D_TELEMETRY_BYTES_MAX) {
+ ret = -EIO;
+ goto out;
+ }
- /* Get DRAM size */
- ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true);
- if (ret || !dev->dram_size)
+ /* Get DRAM size; fall back to the default if the query fails */
+ if (amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true) ||
+ !dev->dram_size)
dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
/* Get STB DRAM address */
- amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true);
- amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true);
+ ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low,
+ dev->stb_arg.s2d_msg_id, true);
+ if (ret)
+ goto out;
+ ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi,
+ dev->stb_arg.s2d_msg_id, true);
+ if (ret)
+ goto out;
stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
-
- /* Clear msg_port for other SMU operation */
- dev->msg_port = MSG_PORT_PMC;
+ if (!stb_phys_addr) {
+ dev_err(dev->dev, "S2D phys addr query returned invalid address\n");
+ ret = -ENXIO;
+ goto out;
+ }
dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
if (!dev->stb_virt_addr)
- return -ENOMEM;
+ ret = -ENOMEM;
- return 0;
+out:
+ /* Restore the default message port for subsequent SMU operations */
+ dev->msg_port = MSG_PORT_PMC;
+ return ret;
}
diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c
index a37083cdb908..542b5d04515a 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -941,13 +941,17 @@ static int amd_pmc_probe(struct platform_device *pdev)
amd_pmc_dbgfs_register(dev);
err = amd_stb_s2d_init(dev);
if (err)
- goto err_pci_dev_put;
+ goto err_dbgfs_unregister;
if (IS_ENABLED(CONFIG_AMD_MP2_STB))
amd_mp2_stb_init(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0;
+err_dbgfs_unregister:
+ amd_pmc_dbgfs_unregister(dev);
+ if (IS_ENABLED(CONFIG_SUSPEND))
+ acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
err_pci_dev_put:
pci_dev_put(rdev);
return err;
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 db278ff4cc4d..154e115af4b4 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/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 27fd6cd21529..309634c1cc20 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;
/*
@@ -661,12 +661,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 */
@@ -731,26 +736,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 af4d1920d488..446dd18d2cee 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c
@@ -163,10 +163,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 */
@@ -227,6 +228,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:
@@ -280,6 +283,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);
@@ -300,10 +305,12 @@ exit_enumeration_package:
* 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)
{
@@ -312,7 +319,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 d96e160953e3..a27907066448 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
@@ -243,6 +243,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:
@@ -275,10 +277,12 @@ exit_integer_package:
* 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)
{
@@ -286,7 +290,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 f09489a085c8..5bf8d40bdf81 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
@@ -145,7 +145,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:
@@ -232,6 +232,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:
@@ -261,7 +263,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;
@@ -298,10 +302,12 @@ exit_list:
* 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];
@@ -309,7 +315,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 4d79eb8056a5..9821403e42b6 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
@@ -66,7 +66,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)
@@ -123,7 +123,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);
@@ -321,6 +321,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;
@@ -362,6 +364,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;
@@ -385,10 +389,12 @@ exit_package:
* 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];
@@ -396,7 +402,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 2b00a14792e9..4d94e48c1a4c 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 fe5a9a3a4ef1..f3dfba270f7e 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
@@ -233,6 +233,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:
@@ -263,10 +265,12 @@ exit_string_package:
* 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)
{
@@ -275,7 +279,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/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 8ba286ed8721..94147102cca4 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -266,6 +266,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
.driver_data = (void *)&omen_v1_no_ec_thermal_params,
},
{
+ .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") },
+ .driver_data = (void *)&omen_v1_no_ec_thermal_params,
+ },
+ {
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") },
.driver_data = (void *)&omen_v1_legacy_thermal_params,
},
diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c
index 849f7b415c1e..7263114f0b3d 100644
--- a/drivers/platform/x86/intel/int1092/intel_sar.c
+++ b/drivers/platform/x86/intel/int1092/intel_sar.c
@@ -91,8 +91,10 @@ static acpi_status parse_package(struct wwan_sar_context *context, union acpi_ob
item->package.count <= data->total_dev_mode)
return AE_ERROR;
- data->device_mode_info = kmalloc_objs(struct wwan_device_mode_info,
- data->total_dev_mode);
+ data->device_mode_info = devm_kmalloc_array(&context->sar_device->dev,
+ data->total_dev_mode,
+ sizeof(*data->device_mode_info),
+ GFP_KERNEL);
if (!data->device_mode_info)
return AE_ERROR;
@@ -253,7 +255,7 @@ static int sar_probe(struct platform_device *device)
if (!handle)
return -ENODEV;
- context = kzalloc_obj(*context);
+ context = devm_kzalloc(&device->dev, sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
@@ -264,7 +266,7 @@ static int sar_probe(struct platform_device *device)
result = guid_parse(SAR_DSM_UUID, &context->guid);
if (result) {
dev_err(&device->dev, "SAR UUID parse error: %d\n", result);
- goto r_free;
+ return result;
}
for (reg = 0; reg < MAX_REGULATORY; reg++)
@@ -272,43 +274,29 @@ static int sar_probe(struct platform_device *device)
if (sar_get_device_mode(device) != AE_OK) {
dev_err(&device->dev, "Failed to get device mode\n");
- result = -EIO;
- goto r_free;
+ return -EIO;
}
result = sysfs_create_group(&device->dev.kobj, &intcsar_group);
if (result) {
dev_err(&device->dev, "sysfs creation failed\n");
- goto r_free;
+ return result;
}
if (acpi_install_notify_handler(ACPI_HANDLE(&device->dev), ACPI_DEVICE_NOTIFY,
sar_notify, (void *)device) != AE_OK) {
dev_err(&device->dev, "Failed acpi_install_notify_handler\n");
- result = -EIO;
- goto r_sys;
+ sysfs_remove_group(&device->dev.kobj, &intcsar_group);
+ return -EIO;
}
return 0;
-
-r_sys:
- sysfs_remove_group(&device->dev.kobj, &intcsar_group);
-r_free:
- kfree(context);
- return result;
}
static void sar_remove(struct platform_device *device)
{
- struct wwan_sar_context *context = dev_get_drvdata(&device->dev);
- int reg;
-
acpi_remove_notify_handler(ACPI_HANDLE(&device->dev),
ACPI_DEVICE_NOTIFY, sar_notify);
sysfs_remove_group(&device->dev.kobj, &intcsar_group);
- for (reg = 0; reg < MAX_REGULATORY; reg++)
- kfree(context->config_data[reg].device_mode_info);
-
- kfree(context);
}
static struct platform_driver sar_driver = {
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 24334ae70d82..66dd974720be 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
@@ -336,8 +336,11 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
int i;
pd_info->perf_levels = devm_kcalloc(dev, levels, sizeof(struct perf_level), GFP_KERNEL);
- if (!pd_info->perf_levels)
- return 0;
+ if (!pd_info->perf_levels) {
+ pd_info->pp_header.allowed_level_mask = 0;
+ pd_info->pp_header.level_en_mask = 0;
+ return -ENOMEM;
+ }
pd_info->ratio_unit = pd_info->pp_header.ratio_unit;
pd_info->avx_levels = SST_MAX_AVX_LEVELS;
@@ -367,7 +370,7 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info)
{
struct device *dev = &auxdev->dev;
- int i, mask, levels;
+ int i, ret, mask, levels;
*((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base);
pd_info->sst_header.cp_offset *= 8;
@@ -399,8 +402,12 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai
levels = i;
mask <<= 1;
}
+
+ ret = sst_add_perf_profiles(auxdev, pd_info, levels + 1);
+ if (ret)
+ return ret;
+
pd_info->max_level = levels;
- sst_add_perf_profiles(auxdev, pd_info, levels + 1);
return 0;
}
@@ -599,6 +606,9 @@ static bool disable_dynamic_sst_features(void)
#define SST_CP_PRIORITY_TYPE_START 1
#define SST_CP_PRIORITY_TYPE_WIDTH 1
+#define SST_CP_MAX_ENABLE 1
+#define SST_CP_MAX_PRIORITY_TYPE 1
+
static long isst_if_core_power_state(void __user *argp)
{
struct tpmi_per_power_domain_info *power_domain_info;
@@ -618,6 +628,10 @@ static long isst_if_core_power_state(void __user *argp)
if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
return -EPERM;
+ if (core_power.enable > SST_CP_MAX_ENABLE ||
+ core_power.priority_type > SST_CP_MAX_PRIORITY_TYPE)
+ return -EINVAL;
+
_write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET,
SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)
_write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,
@@ -649,6 +663,11 @@ 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
+
+#define SST_MAX_FREQ 0xff
+#define SST_CLOS_MAX_PRIORITY 0x0f
+
static long isst_if_clos_param(void __user *argp)
{
struct tpmi_per_power_domain_info *power_domain_info;
@@ -657,6 +676,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;
@@ -665,6 +687,15 @@ static long isst_if_clos_param(void __user *argp)
if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
return -EPERM;
+ if (!in_range(clos_param.min_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1))
+ return -EINVAL;
+
+ if (!in_range(clos_param.max_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1))
+ return -EINVAL;
+
+ if (!in_range(clos_param.prop_prio, 0, SST_CLOS_MAX_PRIORITY + 1))
+ return -EINVAL;
+
_write_cp_info("clos.min_freq", clos_param.min_freq_mhz,
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH,
@@ -703,6 +734,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;
@@ -729,7 +762,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;
@@ -747,6 +786,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;
punit_id = map_partition_power_domain_id(sst_inst, punit_id, &part);
if (punit_id < 0)
@@ -845,6 +886,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
@@ -877,7 +919,7 @@ static int isst_if_get_perf_level(void __user *argp)
SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE)
perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1));
- level_mask = perf_level.level_mask;
+ level_mask = perf_level.level_mask & power_domain_info->pp_header.level_en_mask;
perf_level.sst_bf_support = 0;
for_each_set_bit(level, &level_mask, BITS_PER_BYTE) {
/*
@@ -932,6 +974,9 @@ static int isst_if_set_perf_level(void __user *argp)
if (!power_domain_info)
return -EINVAL;
+ if (perf_level.level > power_domain_info->max_level)
+ return -EINVAL;
+
if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -995,6 +1040,9 @@ static int isst_if_set_perf_feature(void __user *argp)
if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
return -EPERM;
+ 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)
@@ -1261,6 +1309,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)
@@ -1306,6 +1360,9 @@ static int isst_if_get_base_freq_info(void __user *argp)
if (base_freq.level > power_domain_info->max_level)
return -EINVAL;
+ if (!(power_domain_info->pp_header.level_en_mask & BIT(base_freq.level)))
+ return -EINVAL;
+
_read_bf_level_info("p1_high", base_freq.high_base_freq_mhz, base_freq.level,
SST_BF_INFO_0_OFFSET, SST_BF_P1_HIGH_START, SST_BF_P1_HIGH_WIDTH,
SST_MUL_FACTOR_FREQ)
@@ -1342,6 +1399,12 @@ 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;
+
+ if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.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)
@@ -1370,6 +1433,8 @@ static int isst_if_get_tpmi_instance_count(void __user *argp)
return -EINVAL;
sst_inst = isst_common.sst_inst[tpmi_inst.socket_id];
+ if (!sst_inst)
+ return -EINVAL;
tpmi_inst.count = isst_instance_count(sst_inst);
@@ -1437,6 +1502,9 @@ static int isst_if_get_turbo_freq_info(void __user *argp)
if (turbo_freq.level > power_domain_info->max_level)
return -EINVAL;
+ if (!(power_domain_info->pp_header.level_en_mask & BIT(turbo_freq.level)))
+ return -EINVAL;
+
turbo_freq.max_buckets = TRL_MAX_BUCKETS;
turbo_freq.max_trl_levels = TRL_MAX_LEVELS;
turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS;
diff --git a/drivers/platform/x86/lenovo/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c
index e215e86e3db7..a0e3fa766e37 100644
--- a/drivers/platform/x86/lenovo/think-lmi.c
+++ b/drivers/platform/x86/lenovo/think-lmi.c
@@ -438,14 +438,13 @@ static ssize_t current_password_store(struct kobject *kobj,
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
size_t pwdlen;
- pwdlen = strlen(buf);
+ /* Strip newline; setting password won't work if one is present. */
+ pwdlen = strchrnul(buf, '\n') - buf;
/* pwdlen == 0 is allowed to clear the password */
if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen)))
return -EINVAL;
- strscpy(setting->password, buf, setting->maxlen);
- /* Strip out CR if one is present, setting password won't work if it is present */
- strreplace(setting->password, '\n', '\0');
+ strscpy(setting->password, buf, pwdlen + 1);
return count;
}
@@ -745,6 +744,8 @@ static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_att
return -EOPNOTSUPP;
for (i = 0; i < ARRAY_SIZE(thumbtypes); i++) {
+ ssize_t ret;
+
if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
/* Format: 'SVC | SMC, Thumbtype' */
wmistr = kasprintf(GFP_KERNEL, "%s,%s",
@@ -756,8 +757,12 @@ static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_att
}
if (!wmistr)
return -ENOMEM;
- count += cert_thumbprint(buf, wmistr, count);
+
+ ret = cert_thumbprint(buf, wmistr, count);
kfree(wmistr);
+ if (ret < 0)
+ return ret;
+ count = ret;
}
return count;
@@ -1456,6 +1461,10 @@ static void tlmi_release_attr(void)
/* Free up any saved signatures */
kfree(tlmi_priv.pwd_admin->signature);
kfree(tlmi_priv.pwd_admin->save_signature);
+ if (tlmi_priv.pwd_system) {
+ kfree(tlmi_priv.pwd_system->signature);
+ kfree(tlmi_priv.pwd_system->save_signature);
+ }
/* Authentication structures */
list_for_each_entry_safe(pos, n, &tlmi_priv.authentication_kset->list, entry)
diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c
index 1b73a55f1b89..015e046b0fce 100644
--- a/drivers/platform/x86/lenovo/ymc.c
+++ b/drivers/platform/x86/lenovo/ymc.c
@@ -8,6 +8,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
@@ -20,6 +22,8 @@
#define LENOVO_YMC_QUERY_INSTANCE 0
#define LENOVO_YMC_QUERY_METHOD 0x01
+#define LENOVO_YMC_STATE_MASK GENMASK(7, 0)
+
static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
@@ -85,7 +89,9 @@ static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
"WMI event data is not an integer\n");
goto free_obj;
}
- code = obj->integer.value;
+
+ /* strip upper bits (e.g. 0x50000) on newer devices */
+ code = FIELD_GET(LENOVO_YMC_STATE_MASK, obj->integer.value);
if (!sparse_keymap_report_event(priv->input_dev, code, 1, true))
dev_warn(&wdev->dev, "Unknown key %d pressed\n", code);
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 719add753cb3..49098d82d6e0 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -360,7 +360,16 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
} else
pr_err("Invalid HKEY.SINF data\n");
}
- pcc->sinf[hkey->package.count] = -1;
+ /*
+ * pcc->sinf[] has pcc->num_sifr elements (valid indices
+ * 0..num_sifr-1). On DSDTs where SINF's package count equals
+ * num_sifr exactly -- the off-by-one case probe()'s num_sifr++
+ * already allocates a spare element for -- there is no room left
+ * for this trailing sentinel; nothing reads it back, so just skip
+ * the write rather than running one element past the flex array.
+ */
+ if (hkey->package.count < pcc->num_sifr)
+ pcc->sinf[hkey->package.count] = -1;
end:
kfree(buffer.pointer);
diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c
index 72f1bfea8d54..54dd9d4ec39c 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>
@@ -1003,10 +1004,6 @@ static int bq24257_probe(struct i2c_client *client)
if (bq->info->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
@@ -1047,6 +1044,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 |
@@ -1064,9 +1069,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 5a6634fde837..43e1da849df4 100644
--- a/drivers/power/supply/bq256xx_charger.c
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -896,6 +896,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,
@@ -1721,24 +1723,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) {
@@ -1746,6 +1736,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 180bc137a863..741a45cc5f35 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/charger-manager.c b/drivers/power/supply/charger-manager.c
index 71b6e49a835f..fd4cd9cd2ce2 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -1014,6 +1014,29 @@ static int charger_extcon_init(struct charger_manager *cm,
return 0;
}
+static int charger_manager_get_regulators(struct charger_manager *cm)
+{
+ struct charger_desc *desc = cm->desc;
+ struct charger_regulator *charger;
+ int i, ret;
+
+ for (i = 0; i < desc->num_charger_regulators; i++) {
+ charger = &desc->charger_regulators[i];
+ charger->consumer = regulator_get(cm->dev,
+ charger->regulator_name);
+ if (IS_ERR(charger->consumer)) {
+ dev_err(cm->dev, "Cannot find charger(%s)\n",
+ charger->regulator_name);
+ ret = PTR_ERR(charger->consumer);
+ while (i-- > 0)
+ regulator_put(desc->charger_regulators[i].consumer);
+ return ret;
+ }
+ charger->cm = cm;
+ }
+ return 0;
+}
+
/**
* charger_manager_register_extcon - Register extcon device to receive state
* of charger cable.
@@ -1036,15 +1059,6 @@ static int charger_manager_register_extcon(struct charger_manager *cm)
for (i = 0; i < desc->num_charger_regulators; i++) {
charger = &desc->charger_regulators[i];
- charger->consumer = regulator_get(cm->dev,
- charger->regulator_name);
- if (IS_ERR(charger->consumer)) {
- dev_err(cm->dev, "Cannot find charger(%s)\n",
- charger->regulator_name);
- return PTR_ERR(charger->consumer);
- }
- charger->cm = cm;
-
for (j = 0; j < charger->num_cables; j++) {
struct charger_cable *cable = &charger->cables[j];
@@ -1580,13 +1594,23 @@ static int charger_manager_probe(struct platform_device *pdev)
}
psy_cfg.attr_grp = desc->sysfs_groups;
+ /*
+ * Acquire charger regulators before exposing the sysfs entries, so
+ * userspace cannot reach externally_control before the regulators
+ * (and charger->cm) are available. Mirrors the order in remove().
+ */
+ ret = charger_manager_get_regulators(cm);
+ if (ret < 0)
+ return ret;
+
cm->charger_psy = power_supply_register(&pdev->dev,
&cm->charger_psy_desc,
&psy_cfg);
if (IS_ERR(cm->charger_psy)) {
dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
cm->charger_psy_desc.name);
- return PTR_ERR(cm->charger_psy);
+ ret = PTR_ERR(cm->charger_psy);
+ goto err_regulator;
}
/* Register extcon device for charger cable */
@@ -1620,11 +1644,11 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0;
err_reg_extcon:
+ power_supply_unregister(cm->charger_psy);
+err_regulator:
for (i = 0; i < desc->num_charger_regulators; i++)
regulator_put(desc->charger_regulators[i].consumer);
- power_supply_unregister(cm->charger_psy);
-
return ret;
}
@@ -1642,12 +1666,12 @@ static void charger_manager_remove(struct platform_device *pdev)
cancel_work_sync(&setup_polling);
cancel_delayed_work_sync(&cm_monitor_work);
- for (i = 0 ; i < desc->num_charger_regulators ; i++)
- regulator_put(desc->charger_regulators[i].consumer);
+ try_charger_enable(cm, false);
power_supply_unregister(cm->charger_psy);
- try_charger_enable(cm, false);
+ for (i = 0 ; i < desc->num_charger_regulators ; i++)
+ regulator_put(desc->charger_regulators[i].consumer);
}
static const struct platform_device_id charger_manager_id[] = {
diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c
index c1cbe6e5476e..69d227ceb5c9 100644
--- a/drivers/power/supply/cros_usbpd-charger.c
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -124,6 +124,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;
}
@@ -137,6 +142,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;
}
@@ -588,10 +598,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/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c
index 641e0445a410..4537b7b413dd 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/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c
index f0a680c155c4..1bc2c150fb91 100644
--- a/drivers/power/supply/lp8788-charger.c
+++ b/drivers/power/supply/lp8788-charger.c
@@ -710,8 +710,8 @@ static void lp8788_charger_remove(struct platform_device *pdev)
{
struct lp8788_charger *pchg = platform_get_drvdata(pdev);
- flush_work(&pchg->charger_work);
lp8788_irq_unregister(pdev, pchg);
+ cancel_work_sync(&pchg->charger_work);
}
static struct platform_driver lp8788_charger_driver = {
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
index 03ac569e185c..41744191a363 100644
--- a/drivers/power/supply/max17040_battery.c
+++ b/drivers/power/supply/max17040_battery.c
@@ -192,8 +192,11 @@ static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)
static int max17040_get_vcell(struct max17040_chip *chip)
{
u32 vcell;
+ int ret;
- regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
+ ret = regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
+ if (ret)
+ return ret;
return max17040_raw_vcell_to_uvolts(chip, vcell);
}
@@ -201,8 +204,11 @@ static int max17040_get_vcell(struct max17040_chip *chip)
static int max17040_get_soc(struct max17040_chip *chip)
{
u32 soc;
+ int ret;
- regmap_read(chip->regmap, MAX17040_SOC, &soc);
+ ret = regmap_read(chip->regmap, MAX17040_SOC, &soc);
+ if (ret)
+ return ret;
return soc >> (chip->quirk_double_soc ? 9 : 8);
}
@@ -261,7 +267,11 @@ static int max17040_get_of_data(struct max17040_chip *chip)
static void max17040_check_changes(struct max17040_chip *chip)
{
- chip->soc = max17040_get_soc(chip);
+ int soc;
+
+ soc = max17040_get_soc(chip);
+ if (soc >= 0)
+ chip->soc = soc;
}
static void max17040_queue_work(struct max17040_chip *chip)
@@ -396,10 +406,16 @@ static int max17040_get_property(struct power_supply *psy,
val->intval = max17040_get_online(chip);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = max17040_get_vcell(chip);
+ ret = max17040_get_vcell(chip);
+ if (ret < 0)
+ return ret;
+ val->intval = ret;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = max17040_get_soc(chip);
+ ret = max17040_get_soc(chip);
+ if (ret < 0)
+ return ret;
+ val->intval = ret;
break;
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
val->intval = chip->low_soc_alert;
@@ -458,16 +474,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;
@@ -568,7 +580,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/pf1550-charger.c b/drivers/power/supply/pf1550-charger.c
index 41036f4cb64a..38cac156d2e6 100644
--- a/drivers/power/supply/pf1550-charger.c
+++ b/drivers/power/supply/pf1550-charger.c
@@ -514,7 +514,7 @@ static int pf1550_reg_init(struct pf1550_charger *chg)
* a battery. The other supported mode is mode 2, the charger is turned
* on to charge a battery when present.
*/
- if (power_supply_get_battery_info(chg->charger, &info)) {
+ if (!power_supply_get_battery_info(chg->charger, &info)) {
ret = regmap_write(chg->pf1550->regmap,
PF1550_CHARG_REG_CHG_OPER,
PF1550_CHG_BAT_ON);
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index 490137a23d00..91cf39b0083a 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -5,6 +5,7 @@
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/auxiliary_bus.h>
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
@@ -1231,7 +1232,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);
}
}
@@ -1648,7 +1649,6 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
psy_cfg_supply.supplied_to = qcom_battmgr_battery;
psy_cfg_supply.num_supplicants = 1;
- INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker);
mutex_init(&battmgr->lock);
init_completion(&battmgr->ack);
@@ -1711,6 +1711,11 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
"failed to register wireless charing power supply\n");
}
+ ret = devm_work_autocancel(dev, &battmgr->enable_work,
+ qcom_battmgr_enable_worker);
+ if (ret)
+ return ret;
+
battmgr->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_BATTMGR,
qcom_battmgr_callback,
qcom_battmgr_pdr_notify,
diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c
index 7045d2908148..515bb3b2d67a 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_MAPLE,
};
+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/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index 04216b2bfb6c..e5b4d72720de 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>
@@ -1002,8 +1003,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 3f44cc902f84..ca58c214e93e 100644
--- a/drivers/power/supply/ucs1002_power.c
+++ b/drivers/power/supply/ucs1002_power.c
@@ -11,6 +11,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>
@@ -640,7 +641,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/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c
index bb0e14bac9f2..8ba14bbec15e 100644
--- a/drivers/ptp/ptp_vmclock.c
+++ b/drivers/ptp/ptp_vmclock.c
@@ -378,6 +378,12 @@ static int vmclock_miscdev_mmap(struct file *fp, struct vm_area_struct *vma)
*/
vm_flags_clear(vma, VM_MAYWRITE);
+ /*
+ * Restrict the read-only mapping so it cannot be upgraded to
+ * writable later with mprotect().
+ */
+ vm_flags_clear(vma, VM_MAYWRITE);
+
if (vma->vm_end - vma->vm_start != PAGE_SIZE || vma->vm_pgoff)
return -EINVAL;
diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c
index 009b3b595bbf..ad82c2108a56 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -564,9 +564,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/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/max8998.c b/drivers/regulator/max8998.c
index cc85fbe8b77c..90651f4d1828 100644
--- a/drivers/regulator/max8998.c
+++ b/drivers/regulator/max8998.c
@@ -582,7 +582,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);
pdata->buck_voltage_lock = of_property_read_bool(pmic_np, "max8998,pmic-buck-voltage-lock");
diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c
index 299ac3c8c3bc..6a3795469927 100644
--- a/drivers/regulator/qcom-refgen-regulator.c
+++ b/drivers/regulator/qcom-refgen-regulator.c
@@ -66,7 +66,7 @@ static const 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 const 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/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index 85a74c9ec521..436656bdfa8b 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/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 62adc4db2317..35bb03e67ae8 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/s390/block/dasd.c b/drivers/s390/block/dasd.c
index d8d912a3b3fe..56ef38243f82 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1698,8 +1698,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 d356a9f8f016..b64ca714b53e 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1492,6 +1492,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);
@@ -1647,6 +1649,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;
}
@@ -1654,6 +1659,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;
}
@@ -1667,6 +1675,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;
@@ -1681,6 +1692,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;
@@ -1690,6 +1704,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;
}
@@ -1832,7 +1849,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;
@@ -1848,6 +1869,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;
}
@@ -1855,6 +1879,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;
}
@@ -1865,6 +1892,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;
}
@@ -5938,8 +5968,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 */
@@ -6804,6 +6837,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 89d7516b9ec8..468f0b2cc342 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -123,6 +123,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);
@@ -130,6 +131,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) {
@@ -147,6 +149,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/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c
index 132f00512ee1..28c9861b6501 100644
--- a/drivers/scsi/fnic/fip.c
+++ b/drivers/scsi/fnic/fip.c
@@ -139,7 +139,7 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"process_vlan_resp: FIP VLAN %d\n", vid);
- vlan = kzalloc_obj(*vlan);
+ vlan = kzalloc_obj(*vlan, GFP_ATOMIC);
if (!vlan) {
/* retry from timer */
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index daeb3693fe55..480a0bf2aff3 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1187,8 +1187,10 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd)
if (blk_rq_bytes(rq) & rq->q->limits.dma_pad_mask) {
unsigned int pad_len =
(rq->q->limits.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/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index f1f9f6fbc00e..1d7186eecd23 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/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c
index cb2f49f3e0fa..bf262c01a2b8 100644
--- a/drivers/staging/media/ipu7/ipu7-isys.c
+++ b/drivers/staging/media/ipu7/ipu7-isys.c
@@ -773,6 +773,7 @@ static int isys_probe(struct auxiliary_device *auxdev,
return 0;
out_cleanup:
+ isys_notifier_cleanup(isys);
isys_unregister_devices(isys);
out_cleanup_fw:
ipu7_fw_isys_release(isys);
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 456134a9e8cf..f461e117305e 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1257,6 +1257,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");
@@ -1267,8 +1268,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) {
@@ -1289,10 +1290,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 9ff1f1750a15..5fba11e31e1d 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/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 251a50c8aa38..b0f34a6e7d4f 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -22,6 +22,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ktime.h>
+#include <linux/mutex.h>
#include <linux/pinctrl/consumer.h>
#include <linux/rational.h>
#include <linux/slab.h>
@@ -2080,6 +2081,9 @@ static const struct uart_ops imx_uart_pops = {
static struct imx_port *imx_uart_ports[UART_NR];
+/* Held across uart_add/remove_one_port(); console callbacks must not take it. */
+static DEFINE_MUTEX(imx_uart_ports_lock);
+
#if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch)
{
@@ -2632,11 +2636,19 @@ static int imx_uart_probe(struct platform_device *pdev)
}
}
- imx_uart_ports[sport->port.line] = sport;
-
platform_set_drvdata(pdev, sport);
- ret = uart_add_one_port(&imx_uart_uart_driver, &sport->port);
+ scoped_guard(mutex, &imx_uart_ports_lock) {
+ if (imx_uart_ports[sport->port.line]) {
+ ret = -EBUSY;
+ } else {
+ imx_uart_ports[sport->port.line] = sport;
+ ret = uart_add_one_port(&imx_uart_uart_driver,
+ &sport->port);
+ if (ret)
+ imx_uart_ports[sport->port.line] = NULL;
+ }
+ }
err_clk:
clk_disable_unprepare(sport->clk_ipg);
@@ -2648,7 +2660,9 @@ static void imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
+ guard(mutex)(&imx_uart_ports_lock);
uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
+ imx_uart_ports[sport->port.line] = NULL;
}
static void imx_uart_restore_context(struct imx_port *sport)
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index c8b02c27d27d..102169b40419 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -4680,6 +4680,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);
@@ -4691,6 +4692,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);
@@ -4704,9 +4712,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/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1082e9c9afaa..1afe533b3ebd 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -3497,6 +3497,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
}
dwc3_debugfs_remove_endpoint_dir(dep);
+ cancel_delayed_work_sync(&dep->nostream_work);
kfree(dep);
}
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 44218be1e676..826bf41260c2 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -548,6 +548,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)
@@ -582,10 +583,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_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024343..a4b72a6fad8a 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,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);
@@ -2639,6 +2640,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 98414e7611c0..9e6d4f39900a 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1675,19 +1675,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_obj(struct usbg_tpg);
@@ -1714,7 +1720,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;
@@ -1727,8 +1732,6 @@ unref_dep:
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);
@@ -2666,9 +2669,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_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 73dc7e42875f..d1bf3ea75197 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -889,9 +889,12 @@ error_unlock:
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;
@@ -1075,7 +1078,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 e53f2927b539..391419c208d4 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)
{
@@ -1256,6 +1270,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
@@ -1424,6 +1440,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);
@@ -1449,12 +1467,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/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 2f9700b3f1b6..9ba09118bb74 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -821,7 +821,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
/* Allocate a kthread for asynchronous hw submit handler. */
video->kworker = kthread_run_worker(0, "UVCG");
if (IS_ERR(video->kworker)) {
- uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n");
+ uvcg_err(&uvc->func, "failed to create UVCG kworker\n");
return PTR_ERR(video->kworker);
}
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index 5aa360ba4f03..8e5d7fb71549 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -1794,6 +1794,19 @@ static void at91udc_of_init(struct at91_udc *udc, struct device_node *np)
udc->caps = match->data;
}
+/*
+ * The work handler re-arms this timer, so shut the timer down before
+ * draining the work; otherwise it restarts the polling cycle.
+ */
+static void at91_udc_shutdown_vbus_timer(struct at91_udc *udc)
+{
+ if (!(udc->board.vbus_pin && udc->board.vbus_polled))
+ return;
+
+ timer_shutdown_sync(&udc->vbus_timer);
+ cancel_work_sync(&udc->vbus_timer_work);
+}
+
static int at91udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1907,7 +1920,7 @@ static int at91udc_probe(struct platform_device *pdev)
}
retval = usb_add_gadget_udc(dev, &udc->gadget);
if (retval)
- goto err_unprepare_iclk;
+ goto err_shutdown_vbus;
dev_set_drvdata(dev, udc);
device_init_wakeup(dev, 1);
create_debug_file(udc);
@@ -1915,6 +1928,8 @@ static int at91udc_probe(struct platform_device *pdev)
INFO("%s version %s\n", driver_name, DRIVER_VERSION);
return 0;
+err_shutdown_vbus:
+ at91_udc_shutdown_vbus_timer(udc);
err_unprepare_iclk:
clk_unprepare(udc->iclk);
err_unprepare_fclk:
@@ -1933,6 +1948,9 @@ static void at91udc_remove(struct platform_device *pdev)
DBG("remove\n");
usb_del_gadget_udc(&udc->gadget);
+
+ at91_udc_shutdown_vbus_timer(udc);
+
if (udc->driver) {
dev_err(&pdev->dev,
"Driver still in use but removing anyhow\n");
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
index db842a6de643..0ee3ed185d9c 100644
--- a/drivers/usb/gadget/udc/snps_udc_plat.c
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -159,10 +159,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/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 35d79f11b03d..1986f7e9d9be 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -46,7 +46,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,
};
@@ -57,11 +57,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;
@@ -102,7 +102,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;
@@ -115,7 +115,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;
@@ -133,7 +133,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;
@@ -151,7 +151,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;
@@ -169,7 +169,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;
@@ -188,7 +188,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;
@@ -203,7 +203,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;
@@ -219,7 +219,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
@@ -230,7 +230,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);
@@ -238,7 +238,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);
@@ -251,7 +251,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;
@@ -266,7 +266,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)
@@ -276,13 +276,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,
@@ -339,7 +339,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);
@@ -391,7 +391,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;
@@ -421,7 +421,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;
@@ -443,7 +443,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;
@@ -455,7 +455,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;
@@ -522,7 +522,7 @@ end:
* 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;
@@ -704,7 +704,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;
@@ -830,7 +830,7 @@ err:
}
/* 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);
@@ -1002,7 +1002,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 = fsl_otg_remove,
.driver = {
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index 95bfe7f1b83a..b754077875c3 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/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 32250b94262a..601d39ee17c4 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -303,6 +303,8 @@ static void tbt_altmode_remove(struct typec_altmode *alt)
{
struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ disable_work_sync(&tbt->work);
+
for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
if (tbt->plug[i])
typec_altmode_put_plug(tbt->plug[i]);
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 3e39b800e6b5..d0de5a2488f9 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -218,7 +218,7 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
if (ret)
dev_err(hd3ss3220->dev,
- "vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret);
+ "vbus regulator %s failed: %d\n", on ? "enable" : "disable", ret);
}
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 7ac7000b2d13..6717ac914c6a 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -38,6 +38,7 @@ struct tcpci {
struct regmap *regmap;
unsigned int alert_mask;
+ unsigned int rx_type_mask;
bool controls_vbus;
@@ -488,6 +489,8 @@ static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable)
if (tcpci->data->cable_comm_capable)
reg |= TCPC_RX_DETECT_SOP1;
}
+
+ tcpci->rx_type_mask = reg;
ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg);
if (ret < 0)
return ret;
@@ -749,6 +752,7 @@ process_status:
if (status & TCPC_ALERT_RX_STATUS) {
struct pd_message msg;
unsigned int cnt, payload_cnt;
+ enum tcpm_transmit_type rx_type;
u16 header;
regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
@@ -773,10 +777,16 @@ process_status:
regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
&msg.payload, payload_cnt);
+ ret = regmap_read(tcpci->regmap, TCPC_RX_BUF_FRAME_TYPE, &rx_type);
+ if (ret)
+ return ret;
+
/* Read complete, clear RX status alert bit */
tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
- tcpm_pd_receive(tcpci->port, &msg, TCPC_TX_SOP);
+ rx_type &= TCPC_RX_BUF_FRAME_TYPE_MASK;
+ if (tcpci->rx_type_mask & BIT(rx_type))
+ tcpm_pd_receive(tcpci->port, &msg, rx_type);
}
if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) {
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 49f1c53721bb..81d74242c816 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -91,7 +91,8 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
if (ret)
goto out_clear_bit;
- if (!wait_for_completion_timeout(&ucsi->complete, 5 * HZ))
+ if (!wait_for_completion_timeout(&ucsi->complete,
+ msecs_to_jiffies(UCSI_TIMEOUT_MS)))
ret = -ETIMEDOUT;
out_clear_bit:
diff --git a/drivers/video/backlight/aw99706.c b/drivers/video/backlight/aw99706.c
index 18299faf06ad..9ac8ba50a9ea 100644
--- a/drivers/video/backlight/aw99706.c
+++ b/drivers/video/backlight/aw99706.c
@@ -130,23 +130,23 @@ static const struct aw99706_dt_prop aw99706_dt_props[] = {
AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
},
{
- "awinic,sw-freq", aw99706_dt_property_lookup,
+ "awinic,sw-freq-hz", aw99706_dt_property_lookup,
aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
},
{
- "awinic,sw-ilmt", aw99706_dt_property_lookup,
+ "awinic,sw-ilmt-microamp", aw99706_dt_property_lookup,
aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
},
{
- "awinic,iled-max", aw99706_dt_property_iled_max_convert,
+ "awinic,iled-max-microamp", aw99706_dt_property_iled_max_convert,
NULL, 0,
AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
},
{
- "awinic,uvlo-thres", aw99706_dt_property_lookup,
+ "awinic,uvlo-thres-microvolt", aw99706_dt_property_lookup,
aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
},
@@ -335,7 +335,7 @@ static int aw99706_bl_update_status(struct backlight_device *bl)
{
struct aw99706_device *aw = bl_get_data(bl);
- return aw99706_update_brightness(aw, bl->props.brightness);
+ return aw99706_update_brightness(aw, backlight_get_brightness(bl));
}
static const struct backlight_ops aw99706_bl_ops = {
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 5e7963b4aa93..8d1dcd437113 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -1150,14 +1150,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 9428716e2dc4..a6e7abca7a06 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;
@@ -1077,7 +1077,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 644b8d97b381..c4fdecafd856 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)
@@ -601,6 +662,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/uvesafb.c b/drivers/video/fbdev/uvesafb.c
index 9d82326c744f..ccc9dbc25813 100644
--- a/drivers/video/fbdev/uvesafb.c
+++ b/drivers/video/fbdev/uvesafb.c
@@ -1907,6 +1907,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 0d37e4b10447..1d4477f20450 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)
@@ -1148,6 +1148,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/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c
index e53bc41bde3c..b638963d4b59 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/xen/balloon.c b/drivers/xen/balloon.c
index e7f1d4ca6d75..e7f74ea7cd5e 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -679,7 +679,7 @@ void xen_free_ballooned_pages(unsigned int nr_pages, struct page **pages)
}
EXPORT_SYMBOL(xen_free_ballooned_pages);
-static int __init balloon_add_regions(void)
+static int __init balloon_add_regions(bool append)
{
unsigned long start_pfn, pages;
unsigned long pfn, extra_pfn_end;
@@ -703,19 +703,26 @@ static int __init balloon_add_regions(void)
balloon_append(pfn_to_page(pfn));
/*
- * Extra regions are accounted for in the physmap, but need
- * decreasing from current_pages and target_pages to balloon
- * down the initial allocation, because they are already
- * accounted for in total_pages.
+ * There are two different use-cases depending on how the
+ * initial memory target is fetched. For PVH dom0 and PV the
+ * target is usually set to reflect the domain assigned memory,
+ * and hence extra regions need adding.
+ *
+ * OTOH for HVM and PVH domU the target is set to the amount of
+ * RAM reported in the memory map, and hence extra regions need
+ * subtracting to reflect the real memory usage.
*/
pages = extra_pfn_end - start_pfn;
- if (pages >= balloon_stats.current_pages ||
- pages >= balloon_stats.target_pages) {
+ if (append) {
+ balloon_stats.total_pages += pages;
+ } else if (pages >= balloon_stats.current_pages ||
+ pages >= balloon_stats.target_pages) {
WARN(1, "Extra pages underflow current target");
return -ERANGE;
+ } else {
+ balloon_stats.current_pages -= pages;
+ balloon_stats.target_pages -= pages;
}
- balloon_stats.current_pages -= pages;
- balloon_stats.target_pages -= pages;
}
return 0;
@@ -726,6 +733,7 @@ static int __init balloon_init(void)
struct task_struct *task;
long current_pages = 0;
domid_t domid = DOMID_SELF;
+ bool append = true;
int rc;
if (!xen_domain())
@@ -745,6 +753,7 @@ static int __init balloon_init(void)
} else {
if (xen_unpopulated_pages >= get_num_physpages())
goto underflow;
+ append = false;
current_pages = get_num_physpages() -
xen_unpopulated_pages;
}
@@ -767,7 +776,7 @@ static int __init balloon_init(void)
register_sysctl_init("xen/balloon", balloon_table);
#endif
- rc = balloon_add_regions();
+ rc = balloon_add_regions(append);
if (rc)
return rc;
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 080c99696cd0..cc101143f921 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -35,7 +35,7 @@ struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred)
{
- const struct path *user_path = &user_file->f_path;
+ const struct path *user_path = file_user_path(user_file);
struct file *f;
int error;
diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c
index 460326d34143..19a1259b3b2f 100644
--- a/fs/btrfs/direct-io.c
+++ b/fs/btrfs/direct-io.c
@@ -281,17 +281,24 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
em2 = btrfs_create_dio_extent(BTRFS_I(inode), dio_data, start,
&file_extent, type);
btrfs_dec_nocow_writers(bg);
- if (type == BTRFS_ORDERED_PREALLOC) {
- btrfs_free_extent_map(em);
- *map = em2;
- em = em2;
- }
-
if (IS_ERR(em2)) {
ret = PTR_ERR(em2);
+ btrfs_free_extent_map(em);
+ *map = NULL;
goto out;
}
+ /*
+ * True NOCOW writes don't need to create a new extent map,
+ * while PREALLOC writes must replace the existing one.
+ */
+ if (em2) {
+ ASSERT(type == BTRFS_ORDERED_PREALLOC);
+ btrfs_free_extent_map(em);
+ *map = em2;
+ em = em2;
+ }
+
dio_data->nocow_done = true;
} else {
/* Our caller expects us to free the input extent map. */
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index f032f0858f40..45c4d4a3b29b 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -6,6 +6,7 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/page-flags.h>
+#include <linux/rmap.h>
#include <linux/sched/mm.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
@@ -299,6 +300,25 @@ static noinline void unlock_delalloc_folio(const struct inode *inode,
PAGE_UNLOCK);
}
+#ifdef CONFIG_BTRFS_DEBUG
+/*
+ * Writeback must write-protect a folio when locking it for IO, before
+ * anything consumes its data (zeroing, inline copy, compression,
+ * checksumming). If this fails, then an mmap writer would be able to
+ * modify the data concurrently while we need it to be stable.
+ */
+void btrfs_check_folio_write_protected(struct folio *folio)
+{
+ if (folio_mkclean(folio)) {
+ const struct btrfs_inode *inode = BTRFS_I(folio->mapping->host);
+
+ DEBUG_WARN("writable mmap PTEs, root %llu ino %llu pos %llu order %u",
+ btrfs_root_id(inode->root), btrfs_ino(inode), folio_pos(folio),
+ folio_order(folio));
+ }
+}
+#endif
+
static noinline int lock_delalloc_folios(struct inode *inode,
struct folio *locked_folio,
u64 start, u64 end)
@@ -332,6 +352,8 @@ static noinline int lock_delalloc_folios(struct inode *inode,
folio_unlock(folio);
goto out;
}
+ /* Locked for writeback; revoke writable mmap PTEs before using the data. */
+ folio_mkclean(folio);
range_start = max_t(u64, folio_pos(folio), start);
range_len = min_t(u64, folio_next_pos(folio), end + 1) - range_start;
btrfs_folio_set_lock(fs_info, folio, range_start, range_len);
@@ -1893,6 +1915,13 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
ASSERT(end <= folio_end, "start=%llu len=%u folio_start=%llu folio_size=%zu",
start, len, folio_start, folio_size(folio));
+ /*
+ * We are about to checksum and write out the data, so it must not be
+ * mmap writeable, or we could corrupt the data and end up with invalid
+ * checksums.
+ */
+ btrfs_check_folio_write_protected(folio);
+
/* Truncate the submit bitmap to the current range. */
if (start > folio_start)
bitmap_clear(bio_ctrl->submit_bitmap, 0,
@@ -2703,6 +2732,8 @@ retry:
continue;
}
+ /* Locked for writeback; revoke writable mmap PTEs before using the data. */
+ folio_mkclean(folio);
ret = extent_writepage(folio, bio_ctrl);
if (ret < 0) {
done = true;
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 9896e15ddc40..869925337699 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -255,6 +255,11 @@ bool try_release_extent_mapping(struct folio *folio, gfp_t mask);
int try_release_extent_buffer(struct folio *folio);
int btrfs_read_folio(struct file *file, struct folio *folio);
+#ifdef CONFIG_BTRFS_DEBUG
+void btrfs_check_folio_write_protected(struct folio *folio);
+#else
+static inline void btrfs_check_folio_write_protected(struct folio *folio) { }
+#endif
void extent_write_locked_range(struct inode *inode, const struct folio *locked_folio,
u64 start, u64 end, struct writeback_control *wbc,
bool pages_dirty);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 2534cd9284d5..39a01f9cf6b5 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -775,19 +775,28 @@ static inline void inode_should_defrag(struct btrfs_inode *inode,
static int extent_range_clear_dirty_for_io(struct btrfs_inode *inode, u64 start, u64 end)
{
+ pgoff_t index = start >> PAGE_SHIFT;
const pgoff_t end_index = end >> PAGE_SHIFT;
struct folio *folio;
int ret = 0;
- for (pgoff_t index = start >> PAGE_SHIFT; index <= end_index; index++) {
+ while (index <= end_index) {
folio = filemap_get_folio(inode->vfs_inode.i_mapping, index);
if (IS_ERR(folio)) {
if (!ret)
ret = PTR_ERR(folio);
+ index++;
continue;
}
+ /*
+ * We are about to compress the folio, so it must not be mmap
+ * writeable or we could corrupt the data as we attempt to
+ * compress it.
+ */
+ btrfs_check_folio_write_protected(folio);
btrfs_folio_clamp_clear_dirty(inode->root->fs_info, folio, start,
end + 1 - start);
+ index = folio_next_index(folio);
folio_put(folio);
}
return ret;
@@ -877,11 +886,6 @@ static void compress_file_range(struct btrfs_work *work)
inode_should_defrag(inode, start, end, end - start + 1, SZ_16K);
- /*
- * We need to call clear_page_dirty_for_io on each page in the range.
- * Otherwise applications with the file mmap'd can wander in and change
- * the page contents while we are compressing them.
- */
ret = extent_range_clear_dirty_for_io(inode, start, end);
/*
@@ -2317,6 +2321,13 @@ static int run_delalloc_inline(struct btrfs_inode *inode, struct folio *locked_f
int ret;
ASSERT(folio_pos(locked_folio) == 0);
+ /*
+ * If an mmap writer could modify the folio while we copy it into an
+ * inline extent we might see only part of their modification then
+ * wrongly mark it clean again after copying, losing that write. So the
+ * folio must be write protected here.
+ */
+ btrfs_check_folio_write_protected(locked_folio);
if (btrfs_inode_can_compress(inode) &&
inode_need_compress(inode, 0, blocksize, true)) {
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index baa645e98812..a537036303a4 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -356,14 +356,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
inode_flags |= BTRFS_INODE_NODATACOW;
}
} else {
- /*
- * Revert back under same assumptions as above
- */
- if (S_ISREG(inode->vfs_inode.i_mode)) {
- if (inode->vfs_inode.i_size == 0)
- inode_flags &= ~(BTRFS_INODE_NODATACOW |
- BTRFS_INODE_NODATASUM);
- } else {
+ /* We can only change NODATACOW for zero-sized regular file. */
+ if (S_ISREG(inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size == 0)) {
+ inode_flags &= ~BTRFS_INODE_NODATACOW;
+ /*
+ * There is currently no way to change NODATASUM flag
+ * through fileattr API. If we unconditionally keep the
+ * current NODATASUM flag, chattr +C then chattr -C will
+ * keep the NODATASUM flag, and no way to remove that
+ * flag.
+ *
+ * So respect the current mount option for NODATASUM flag.
+ */
+ if (!btrfs_test_opt(fs_info, NODATASUM))
+ inode_flags &= ~BTRFS_INODE_NODATASUM;
+ } else if (!S_ISREG(inode->vfs_inode.i_mode)) {
inode_flags &= ~BTRFS_INODE_NODATACOW;
}
}
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index fc5c14b5adad..4eca2b5e4a92 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -5555,6 +5555,24 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
return ret;
}
+static void release_recovered_fs_roots(struct list_head *roots, bool drop_reloc_refs)
+{
+ struct btrfs_root *root;
+ struct btrfs_root *next;
+
+ list_for_each_entry_safe(root, next, roots, reloc_dirty_list) {
+ list_del_init(&root->reloc_dirty_list);
+ if (drop_reloc_refs) {
+ struct btrfs_root *reloc_root = root->reloc_root;
+
+ ASSERT(reloc_root);
+ root->reloc_root = NULL;
+ btrfs_put_root(reloc_root);
+ }
+ btrfs_put_root(root);
+ }
+}
+
/*
* recover relocation interrupted by system crash.
*
@@ -5564,6 +5582,7 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
{
LIST_HEAD(reloc_roots);
+ LIST_HEAD(recovered_roots);
struct btrfs_key key;
struct btrfs_root *fs_root;
struct btrfs_root *reloc_root;
@@ -5680,7 +5699,7 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
ret = PTR_ERR(fs_root);
list_add_tail(&reloc_root->root_list, &reloc_roots);
btrfs_end_transaction(trans);
- goto out_unset;
+ goto out_drop_reloc_refs;
}
ret = __add_reloc_root(reloc_root, rc);
@@ -5689,15 +5708,17 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
list_add_tail(&reloc_root->root_list, &reloc_roots);
btrfs_put_root(fs_root);
btrfs_end_transaction(trans);
- goto out_unset;
+ goto out_drop_reloc_refs;
}
+ ASSERT(list_empty(&fs_root->reloc_dirty_list));
fs_root->reloc_root = btrfs_grab_root(reloc_root);
- btrfs_put_root(fs_root);
+ list_add_tail(&fs_root->reloc_dirty_list, &recovered_roots);
}
ret = btrfs_commit_transaction(trans);
if (ret)
- goto out_unset;
+ goto out_drop_reloc_refs;
+ release_recovered_fs_roots(&recovered_roots, false);
merge_reloc_roots(rc);
@@ -5713,6 +5734,8 @@ out_clean:
ret2 = clean_dirty_subvols(rc);
if (ret2 < 0 && !ret)
ret = ret2;
+out_drop_reloc_refs:
+ release_recovered_fs_roots(&recovered_roots, true);
out_unset:
unset_reloc_control(rc);
reloc_chunk_end(fs_info);
diff --git a/fs/buffer.c b/fs/buffer.c
index 9af5f061a1f8..955ab07b34eb 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2177,6 +2177,7 @@ void block_commit_write(struct folio *folio, size_t from, size_t to)
{
size_t block_start, block_end;
bool partial = false;
+ bool uptodate = folio_test_uptodate(folio);
unsigned blocksize;
struct buffer_head *bh, *head;
@@ -2199,6 +2200,8 @@ void block_commit_write(struct folio *folio, size_t from, size_t to)
clear_buffer_new(bh);
block_start = block_end;
+ if (uptodate && block_start >= to)
+ break;
bh = bh->b_this_page;
} while (bh != head);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index ecf33b66610c..e9f561bedf47 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1426,6 +1426,16 @@ void ceph_shift_unused_folios_left(struct folio_batch *fbatch)
fbatch->nr = n;
}
+static void ceph_undo_wrbuffer_claim(struct inode *inode, struct folio *folio)
+{
+ struct ceph_snap_context *snapc = folio_detach_private(folio);
+
+ if (!snapc)
+ return;
+ ceph_put_wrbuffer_cap_refs(ceph_inode(inode), 1, snapc);
+ ceph_put_snap_context(snapc);
+}
+
static
int ceph_submit_write(struct address_space *mapping,
struct writeback_control *wbc,
@@ -1489,6 +1499,7 @@ new_request:
if (!page)
continue;
+ ceph_undo_wrbuffer_claim(inode, page_folio(page));
redirty_page_for_writepage(wbc, page);
unlock_page(page);
}
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index d7283fb54cec..bb5ef0eacd8f 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -979,6 +979,27 @@ int __ceph_caps_revoking_other(struct ceph_inode_info *ci,
return 0;
}
+/*
+ * Return true if any cap of this inode holds caps which the MDS has
+ * revoked, but which we have not released yet.
+ */
+static bool __ceph_is_any_revoking(const struct ceph_inode_info *ci)
+{
+ const struct rb_node *p;
+
+ lockdep_assert_held(&ci->i_ceph_lock);
+
+ for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) {
+ const struct ceph_cap *cap =
+ rb_entry(p, struct ceph_cap, ci_node);
+
+ if (cap->implemented & ~cap->issued)
+ return true;
+ }
+
+ return false;
+}
+
int __ceph_caps_used(struct ceph_inode_info *ci)
{
int used = 0;
@@ -1421,6 +1442,9 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
cap->implemented &= cap->issued | used;
cap->mds_wanted = want;
+ if ((ci->i_ceph_flags & CEPH_I_FLUSH_FORCE) != 0 && !__ceph_is_any_revoking(ci))
+ clear_bit(CEPH_I_FLUSH_FORCE_BIT, &ci->i_ceph_flags);
+
arg->session = cap->session;
arg->ino = ceph_vino(inode).ino;
arg->cid = cap->cap_id;
@@ -2038,6 +2062,14 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags)
if (ci->i_ceph_flags & CEPH_I_FLUSH)
flags |= CHECK_CAPS_FLUSH;
+ /*
+ * A revoke whose response was deferred (see handle_cap_grant()) must
+ * still be acknowledged. Replay the forced flush here so that even a
+ * check triggered by writeback/invalidation completion sends a cap
+ * message to the MDS.
+ */
+ if (ci->i_ceph_flags & CEPH_I_FLUSH_FORCE)
+ flags |= CHECK_CAPS_FLUSH_FORCE;
retry:
/* Caps wanted by virtue of active open files. */
file_wanted = __ceph_caps_file_wanted(ci);
@@ -2589,9 +2621,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)) {
@@ -2601,6 +2638,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;
@@ -2641,6 +2679,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
}
spin_lock(&ci->i_ceph_lock);
+ cf = next;
}
}
@@ -3757,13 +3796,30 @@ static void handle_cap_grant(struct inode *inode,
BUG_ON(cap->issued & ~cap->implemented);
/* don't let check_caps skip sending a response to MDS for revoke msgs */
- if (!revoke_wait && le32_to_cpu(grant->op) == CEPH_CAP_OP_REVOKE) {
- cap->mds_wanted = 0;
- flags |= CHECK_CAPS_FLUSH_FORCE;
- if (cap == ci->i_auth_cap)
- check_caps = 1; /* check auth cap only */
- else
- check_caps = 2; /* check all caps */
+ if (le32_to_cpu(grant->op) == CEPH_CAP_OP_REVOKE) {
+ if (revoke_wait) {
+ /*
+ * We can't ack the revoke yet: the response is deferred
+ * until the writeback or cache invalidation queued above
+ * completes. Set the CEPH_I_FLUSH_FORCE flag to remember
+ * that a forced cap message is owed so that deferred
+ * completion (ceph_put_wrbuffer_cap_refs() or the
+ * invalidate worker, both of which call ceph_check_caps())
+ * actually sends one, even if by then the revoked caps look
+ * unused, the inode is retaining caps, or the MDS has
+ * re-granted them. Without this, the cap message is never
+ * sent and the MDS hangs ("isn't responding to
+ * mclientcaps(revoke)").
+ */
+ set_bit(CEPH_I_FLUSH_FORCE_BIT, &ci->i_ceph_flags);
+ } else {
+ cap->mds_wanted = 0;
+ flags |= CHECK_CAPS_FLUSH_FORCE;
+ if (cap == ci->i_auth_cap)
+ check_caps = 1; /* check auth cap only */
+ else
+ check_caps = 2; /* check all caps */
+ }
}
if (extra_info->inline_version > 0 &&
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index ef9e92e362d3..40326517481c 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1764,11 +1764,11 @@ static int __dir_lease_check(const struct dentry *dentry,
if (ret > 0) {
if (time_before(jiffies, di->time + lwc->dir_lease_ttl))
return STOP;
+ if (!lwc->expire_dir_lease)
+ return KEEP;
/* Move dentry to tail of dir lease list if we don't want
* to delete it. So dentries in the list are checked in a
* round robin manner */
- if (!lwc->expire_dir_lease)
- return TOUCH;
if (dentry->d_lockref.count > 0 ||
(di->flags & CEPH_DENTRY_REFERENCED))
return TOUCH;
@@ -1795,7 +1795,7 @@ int ceph_trim_dentries(struct ceph_mds_client *mdsc)
lwc.dir_lease = false;
lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE * 2;
freed = __dentry_leases_walk(mdsc, &lwc);
- if (!lwc.nr_to_scan) /* more invalid leases */
+ if (freed > 0 && !lwc.nr_to_scan) /* more invalid leases */
return -EAGAIN;
if (lwc.nr_to_scan < CEPH_CAPS_PER_RELEASE)
@@ -1805,6 +1805,10 @@ int ceph_trim_dentries(struct ceph_mds_client *mdsc)
lwc.expire_dir_lease = freed < count;
lwc.dir_lease_ttl = mdsc->fsc->mount_options->caps_wanted_delay_max * HZ;
freed +=__dentry_leases_walk(mdsc, &lwc);
+ if (freed == 0 && count == 0)
+ /* no progress possible currently, retry futile */
+ return 0;
+
if (!lwc.nr_to_scan) /* more to check */
return -EAGAIN;
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index b2f2af104679..debb9634b9e3 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -442,6 +442,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)
{
@@ -513,9 +523,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;
}
}
@@ -580,8 +589,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,
@@ -595,10 +604,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/mds_client.c b/fs/ceph/mds_client.c
index 3c692ad02c85..ec886ca9b526 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -615,10 +615,36 @@ bad:
#define DELEGATED_INO_AVAILABLE xa_mk_value(1)
+static int ceph_insert_deleg_ino(struct ceph_mds_session *s, u64 ino)
+{
+ struct ceph_client *cl = s->s_mdsc->fsc->client;
+ int err;
+
+ /*
+ * Cap how many delegated inodes a single session may hold. This is
+ * the only place that grows the count, so atomic_add_unless() bounds
+ * it at exactly CEPH_MAX_DELEG_INOS; s_num_deleg_inos can never exceed
+ * that.
+ */
+ if (!atomic_add_unless(&s->s_num_deleg_inos, 1, CEPH_MAX_DELEG_INOS)) {
+ pr_warn_ratelimited_client(cl,
+ "MDS session already holds %d delegated inodes\n",
+ CEPH_MAX_DELEG_INOS);
+ return -EOVERFLOW;
+ }
+
+ err = xa_insert(&s->s_delegated_inos, ino, DELEGATED_INO_AVAILABLE,
+ GFP_KERNEL);
+ if (err)
+ atomic_dec(&s->s_num_deleg_inos);
+ return err;
+}
+
static int ceph_parse_deleg_inos(void **p, void *end,
struct ceph_mds_session *s)
{
struct ceph_client *cl = s->s_mdsc->fsc->client;
+ u64 msg_deleg_inos = 0;
u32 sets;
ceph_decode_32_safe(p, end, sets, bad);
@@ -636,16 +662,34 @@ static int ceph_parse_deleg_inos(void **p, void *end,
start, len);
continue;
}
+
+ /*
+ * Bound the number of inodes one reply may delegate.
+ * ceph_insert_deleg_ino() separately caps the per-session
+ * population, so this only has to stop one reply from spinning
+ * the insert loop under an attacker-controlled len.
+ */
+ if (len > (u64)CEPH_MAX_DELEG_INOS ||
+ msg_deleg_inos > (u64)CEPH_MAX_DELEG_INOS - len) {
+ pr_warn_ratelimited_client(cl,
+ "MDS reply delegates too many inodes (have %llu, +%llu, max %d)\n",
+ msg_deleg_inos, len, CEPH_MAX_DELEG_INOS);
+ return -EIO;
+ }
+ msg_deleg_inos += len;
+
while (len--) {
- int err = xa_insert(&s->s_delegated_inos, start++,
- DELEGATED_INO_AVAILABLE,
- GFP_KERNEL);
+ int err = ceph_insert_deleg_ino(s, start++);
+
if (!err) {
doutc(cl, "added delegated inode 0x%llx\n", start - 1);
} else if (err == -EBUSY) {
pr_warn_client(cl,
"MDS delegated inode 0x%llx more than once.\n",
start - 1);
+ } else if (err == -EOVERFLOW) {
+ /* ceph_insert_deleg_ino() already warned. */
+ return -EIO;
} else {
return err;
}
@@ -663,16 +707,17 @@ u64 ceph_get_deleg_ino(struct ceph_mds_session *s)
xa_for_each(&s->s_delegated_inos, ino, val) {
val = xa_erase(&s->s_delegated_inos, ino);
- if (val == DELEGATED_INO_AVAILABLE)
+ if (val == DELEGATED_INO_AVAILABLE) {
+ atomic_dec(&s->s_num_deleg_inos);
return ino;
+ }
}
return 0;
}
int ceph_restore_deleg_ino(struct ceph_mds_session *s, u64 ino)
{
- return xa_insert(&s->s_delegated_inos, ino, DELEGATED_INO_AVAILABLE,
- GFP_KERNEL);
+ return ceph_insert_deleg_ino(s, ino);
}
#else /* BITS_PER_LONG == 64 */
/*
@@ -1059,6 +1104,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
INIT_LIST_HEAD(&s->s_waiting);
INIT_LIST_HEAD(&s->s_unsafe);
xa_init(&s->s_delegated_inos);
+ atomic_set(&s->s_num_deleg_inos, 0);
INIT_LIST_HEAD(&s->s_cap_releases);
INIT_WORK(&s->s_cap_release_work, ceph_cap_release_work);
@@ -4441,7 +4487,9 @@ static void handle_session(struct ceph_mds_session *session,
pr_err_client(cl, "No memory for path\n");
goto fail;
}
- ceph_decode_copy(&p, cap_auths[i].match.path, _len);
+ ceph_decode_copy_safe(&p, end,
+ cap_auths[i].match.path,
+ _len, bad);
/* Remove the tailing '/' */
while (_len && cap_auths[i].match.path[_len - 1] == '/') {
@@ -4458,7 +4506,9 @@ static void handle_session(struct ceph_mds_session *session,
pr_err_client(cl, "No memory for fs_name\n");
goto fail;
}
- ceph_decode_copy(&p, cap_auths[i].match.fs_name, _len);
+ ceph_decode_copy_safe(&p, end,
+ cap_auths[i].match.fs_name,
+ _len, bad);
}
ceph_decode_8_safe(&p, end, cap_auths[i].match.root_squash, bad);
@@ -5106,6 +5156,7 @@ static int send_mds_reconnect(struct ceph_mds_client *mdsc,
/* Serialized by s_mutex against concurrent ceph_get_deleg_ino(). */
xa_destroy(&session->s_delegated_inos);
+ atomic_set(&session->s_num_deleg_inos, 0);
if (session->s_state == CEPH_MDS_SESSION_CLOSED ||
session->s_state == CEPH_MDS_SESSION_REJECTED) {
pr_info_client(cl, "mds%d skipping reconnect, session %s\n",
@@ -5834,9 +5885,11 @@ static void check_new_map(struct ceph_mds_client *mdsc,
ceph_mdsmap_get_addr(newmap, i),
sizeof(struct ceph_entity_addr))) {
/* just close it */
+ ceph_get_mds_session(s);
mutex_unlock(&mdsc->mutex);
mutex_lock(&s->s_mutex);
mutex_lock(&mdsc->mutex);
+ ceph_put_mds_session(s);
ceph_con_close(&s->s_con);
mutex_unlock(&s->s_mutex);
s->s_state = CEPH_MDS_SESSION_RESTARTING;
@@ -5851,6 +5904,7 @@ static void check_new_map(struct ceph_mds_client *mdsc,
newstate >= CEPH_MDS_STATE_RECONNECT) {
int rc;
+ ceph_get_mds_session(s);
mutex_unlock(&mdsc->mutex);
clear_bit(i, targets);
rc = send_mds_reconnect(mdsc, s);
@@ -5859,6 +5913,7 @@ static void check_new_map(struct ceph_mds_client *mdsc,
"mds%d reconnect failed: %d\n",
i, rc);
mutex_lock(&mdsc->mutex);
+ ceph_put_mds_session(s);
}
/*
@@ -5871,9 +5926,11 @@ static void check_new_map(struct ceph_mds_client *mdsc,
pr_info_client(cl, "mds%d recovery completed\n",
s->s_mds);
kick_requests(mdsc, i);
+ ceph_get_mds_session(s);
mutex_unlock(&mdsc->mutex);
mutex_lock(&s->s_mutex);
mutex_lock(&mdsc->mutex);
+ ceph_put_mds_session(s);
ceph_kick_flushing_caps(mdsc, s);
mutex_unlock(&s->s_mutex);
wake_up_session_caps(s, RECONNECT);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 0ece4c9e3529..3c62e3c3530b 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -300,6 +300,7 @@ struct ceph_mds_session {
struct list_head s_waiting; /* waiting requests */
struct list_head s_unsafe; /* unsafe requests */
struct xarray s_delegated_inos;
+ atomic_t s_num_deleg_inos;
};
/*
diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index 450a4dc9662e..53079ef34c3a 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_obj(*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/super.h b/fs/ceph/super.h
index 1d6aab060780..38f86294182c 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -641,6 +641,15 @@ static inline int ceph_ino_compare(struct inode *inode, void *data)
#define CEPH_MDS_INO_LOG_OFFSET (2 * CEPH_MAX_MDS)
#define CEPH_INO_SYSTEM_BASE ((6*CEPH_MAX_MDS) + (CEPH_MAX_MDS * CEPH_NUM_STRAY))
+/*
+ * Upper bound on the number of delegated inodes a single MDS session may
+ * hold. The MDS normally hands out a small preallocation window (the
+ * userspace mds_client_prealloc_inos option defaults to 1000) and refills
+ * it as the client consumes entries. This leaves generous headroom while
+ * bounding the CPU and memory a malformed delegation interval can consume.
+ */
+#define CEPH_MAX_DELEG_INOS 8192
+
static inline bool ceph_vino_is_reserved(const struct ceph_vino vino)
{
if (vino.ino >= CEPH_INO_SYSTEM_BASE ||
@@ -687,6 +696,10 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
#define CEPH_I_ASYNC_CREATE_BIT (12) /* async create in flight for this */
#define CEPH_I_SHUTDOWN_BIT (13) /* inode is no longer usable */
#define CEPH_I_ASYNC_CHECK_CAPS_BIT (14) /* check caps after async creating finishes */
+#define CEPH_I_FLUSH_FORCE_BIT (15) /* a revoke's response was deferred;
+ * force a cap message to the MDS once
+ * the deferred work completes
+ */
#define CEPH_I_DIR_ORDERED (1 << CEPH_I_DIR_ORDERED_BIT)
#define CEPH_I_FLUSH (1 << CEPH_I_FLUSH_BIT)
@@ -699,6 +712,7 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT)
#define CEPH_I_ASYNC_CREATE (1 << CEPH_I_ASYNC_CREATE_BIT)
#define CEPH_I_SHUTDOWN (1 << CEPH_I_SHUTDOWN_BIT)
+#define CEPH_I_FLUSH_FORCE (1 << CEPH_I_FLUSH_FORCE_BIT)
/*
* Masks of ceph inode work.
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 860fc8e1867d..cc4ffbbcb719 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -848,6 +848,7 @@ start:
name = p;
p += len;
ceph_decode_32_safe(&p, end, len, bad);
+ ceph_decode_need(&p, end, len, bad);
val = p;
p += len;
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index edd6aafbfbaa..08de6652a4f3 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -273,7 +273,8 @@ static int debugfs_locked_down(struct inode *inode,
(!real_fops ||
(!real_fops->unlocked_ioctl &&
!real_fops->compat_ioctl &&
- !real_fops->mmap)))
+ !real_fops->mmap &&
+ !real_fops->mmap_prepare)))
return 0;
if (security_locked_down(LOCKDOWN_DEBUGFS))
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 74b02b55e3f6..e67119b6029c 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1197,7 +1197,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 f4f56a92bd56..7d2488a10b17 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -580,7 +580,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 ebebc9551f1f..51651314b7a6 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -894,6 +894,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)
@@ -1384,10 +1390,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;
}
@@ -1537,7 +1553,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;
@@ -1704,6 +1720,7 @@ out:
* 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
@@ -1714,7 +1731,7 @@ out:
* 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;
@@ -1736,7 +1753,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:
@@ -1751,12 +1772,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 "
@@ -1768,6 +1793,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]; "
@@ -1793,6 +1822,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 03c60f0850ca..73c7b8215e66 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -166,6 +166,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);
@@ -174,6 +175,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);
@@ -284,9 +286,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 5a7d08149922..68804399a5df 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -360,7 +360,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) {
@@ -376,7 +376,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 3bc21d677564..686b2b4a9cb5 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -150,6 +150,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 733c19571f1c..8d33f11db2a1 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -89,12 +89,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/zutil.c b/fs/erofs/zutil.c
index a0ce9226bc02..6b9bfa2ef3b6 100644
--- a/fs/erofs/zutil.c
+++ b/fs/erofs/zutil.c
@@ -79,6 +79,8 @@ int z_erofs_gbuf_growsize(unsigned int nrpages)
for (i = 0; i < z_erofs_gbuf_count; ++i) {
gbuf = &z_erofs_gbufpool[i];
+ if (gbuf->nrpages >= nrpages)
+ continue;
tmp_pages = kzalloc_objs(*tmp_pages, nrpages);
if (!tmp_pages)
goto out;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 29808629cce5..269b1c9fba5f 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1258,12 +1258,9 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)
filemap_invalidate_unlock(inode->i_mapping);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- if (inode_needs_sync(inode)) {
- mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
+ mark_inode_dirty(inode);
+ if (inode_needs_sync(inode))
sync_inode_metadata(inode, 1);
- } else {
- mark_inode_dirty(inode);
- }
return 0;
}
diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c
index e55d16abf422..be63f89402a3 100644
--- a/fs/ext2/xattr.c
+++ b/fs/ext2/xattr.c
@@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
/* Update the inode. */
EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
inode_set_ctime_current(inode);
+ mark_inode_dirty(inode);
if (IS_SYNC(inode)) {
error = sync_inode_metadata(inode, 1);
/* In case sync failed due to ENOSPC the inode was actually
@@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
}
goto cleanup;
}
- } else
- mark_inode_dirty(inode);
+ }
error = 0;
if (old_bh && old_bh != new_bh) {
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 3027ef53af21..2a0fea26a99a 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/fuse/dev.c b/fs/fuse/dev.c
index 95df51880db2..e7da62a7293d 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -75,6 +75,9 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa
fch->minor = param->minor;
fch->max_write = param->max_write;
fch->max_pages = param->max_pages;
+
+ if (param->io_uring_enabled)
+ fuse_uring_conn_init(fch);
}
/* Make sure stores before this are seen on another CPU */
@@ -415,11 +418,6 @@ void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc)
fch->conn = fc;
}
-void fuse_chan_io_uring_enable(struct fuse_chan *fch)
-{
- fch->io_uring = 1;
-}
-
void fuse_pqueue_init(struct fuse_pqueue *fpq)
{
spin_lock_init(&fpq->lock);
diff --git a/fs/fuse/dev.h b/fs/fuse/dev.h
index aed69fd14c41..8d25378c0918 100644
--- a/fs/fuse/dev.h
+++ b/fs/fuse/dev.h
@@ -22,6 +22,7 @@ struct fuse_chan_param {
unsigned int minor;
unsigned int max_write;
unsigned int max_pages;
+ bool io_uring_enabled;
};
struct fuse_chan *fuse_chan_new(void);
@@ -34,7 +35,6 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val);
unsigned int fuse_chan_num_waiting(struct fuse_chan *fch);
void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc);
void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *param);
-void fuse_chan_io_uring_enable(struct fuse_chan *fch);
ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args);
int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags);
int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique);
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 6872266bae1b..82da15a9cc78 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -238,7 +238,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch)
{
struct fuse_ring *ring;
size_t nr_queues = num_possible_cpus();
- struct fuse_ring *res = NULL;
size_t max_payload_size;
ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT);
@@ -258,12 +257,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch)
spin_unlock(&fch->lock);
goto out_err;
}
- if (fch->ring) {
- /* race, another thread created the ring in the meantime */
- spin_unlock(&fch->lock);
- res = fch->ring;
- goto out_err;
- }
init_waitqueue_head(&ring->stop_waitq);
@@ -278,7 +271,13 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch)
out_err:
kfree(ring->queues);
kfree(ring);
- return res;
+ return NULL;
+}
+
+void fuse_uring_conn_init(struct fuse_chan *fch)
+{
+ if (fuse_uring_create(fch))
+ fch->io_uring = 1;
}
static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
@@ -746,6 +745,7 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
{
struct fuse_ring_queue *queue = ent->queue;
struct fuse_ring *ring = queue->ring;
+ struct fuse_in_header in_header;
int err;
err = -EIO;
@@ -767,8 +767,9 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
}
/* copy fuse_in_header */
- return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h,
- sizeof(req->in.h));
+ in_header = req->in.h;
+ return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &in_header,
+ sizeof(in_header));
}
static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
@@ -873,11 +874,13 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req,
unsigned int issue_flags)
{
struct fuse_ring *ring = ent->queue->ring;
+ struct fuse_out_header out_header;
ssize_t err = -EFAULT;
- if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h,
- sizeof(req->out.h)))
+ if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &out_header,
+ sizeof(out_header)))
goto out;
+ req->out.h = out_header;
err = fuse_uring_out_header_has_err(&req->out.h, req);
if (err) {
@@ -1178,15 +1181,10 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
struct fuse_ring *ring = smp_load_acquire(&fch->ring);
struct fuse_ring_queue *queue;
struct fuse_ring_ent *ent;
- int err;
unsigned int qid = READ_ONCE(cmd_req->qid);
- err = -ENOMEM;
- if (!ring) {
- ring = fuse_uring_create(fch);
- if (!ring)
- return err;
- }
+ if (!ring)
+ return -EINVAL;
if (qid >= ring->nr_queues) {
pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid);
@@ -1197,7 +1195,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
if (!queue) {
queue = fuse_uring_create_queue(ring, qid);
if (!queue)
- return err;
+ return -ENOMEM;
}
/*
diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h
index 55f8d04e4b0b..d721a4fc0215 100644
--- a/fs/fuse/dev_uring_i.h
+++ b/fs/fuse/dev_uring_i.h
@@ -135,6 +135,7 @@ struct fuse_ring {
bool ready;
};
+void fuse_uring_conn_init(struct fuse_chan *fch);
void fuse_uring_stop_queues(struct fuse_ring *ring);
void fuse_uring_abort_end_requests(struct fuse_ring *ring);
int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
@@ -174,6 +175,10 @@ static inline bool fuse_uring_ready(struct fuse_chan *fch)
#else /* CONFIG_FUSE_IO_URING */
+static inline void fuse_uring_conn_init(struct fuse_chan *fch)
+{
+}
+
static inline void fuse_uring_abort(struct fuse_chan *fch)
{
}
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index d975073c6029..ce4891a04a37 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1272,6 +1272,7 @@ static void process_init_reply(struct fuse_args *args, int error)
struct fuse_mount *fm = ia->fm;
struct fuse_conn *fc = fm->fc;
struct fuse_init_out *arg = &ia->out;
+ bool io_uring_enabled = false;
bool ok = true;
if (error || arg->major != FUSE_KERNEL_VERSION)
@@ -1402,7 +1403,7 @@ static void process_init_reply(struct fuse_args *args, int error)
ok = false;
}
if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled())
- fuse_chan_io_uring_enable(fc->chan);
+ io_uring_enabled = true;
if (flags & FUSE_REQUEST_TIMEOUT)
timeout = arg->request_timeout;
@@ -1432,6 +1433,7 @@ static void process_init_reply(struct fuse_args *args, int error)
.minor = fc->minor,
.max_write = fc->max_write,
.max_pages = fc->max_pages,
+ .io_uring_enabled = io_uring_enabled,
};
fuse_chan_set_initialized(fc->chan, &cp);
}
diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c
index dc8ac7e370a5..30ef78a66b4f 100644
--- a/fs/iomap/bio.c
+++ b/fs/iomap/bio.c
@@ -179,7 +179,7 @@ int iomap_bio_read_folio_range_sync(const struct iomap_iter *iter,
if (srcmap->flags & IOMAP_F_INTEGRITY)
fs_bio_integrity_alloc(&bio);
error = submit_bio_wait(&bio);
- if (srcmap->flags & IOMAP_F_INTEGRITY) {
+ if (bio_integrity(&bio)) {
if (!error)
error = fs_bio_integrity_verify(&bio, sector, len);
fs_bio_integrity_free(&bio);
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index e2cd5f92babe..01daed391856 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -358,6 +358,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,
iomap_max_bio_size(&iter->iomap), alignment);
else
ret = bio_iov_iter_get_pages(bio, dio->submit.iter,
+ bdev_dma_alignment(bio->bi_bdev),
alignment - 1);
if (unlikely(ret))
goto out_put_bio;
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 1508e2f54462..513273712010 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -358,15 +358,16 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
/*
* journal_shrink_one_cp_list
*
- * Find all the written-back checkpoint buffers in the given list
- * and try to release them. If the whole transaction is released, set
- * the 'released' parameter. Return the number of released checkpointed
- * buffers.
+ * Find written-back checkpoint buffers in the given list and try to release
+ * them. If 'nr_to_scan' is set, scan at most that many buffers. If the whole
+ * transaction is released, set the 'released' parameter. Return the number of
+ * released checkpointed buffers.
*
* Called with j_list_lock held.
*/
static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
enum jbd2_shrink_type type,
+ unsigned long *nr_to_scan,
bool *released)
{
struct journal_head *last_jh;
@@ -375,13 +376,15 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
int ret;
*released = false;
- if (!jh)
+ if (!jh || (nr_to_scan && !*nr_to_scan))
return 0;
last_jh = jh->b_cpprev;
do {
jh = next_jh;
next_jh = jh->b_cpnext;
+ if (nr_to_scan)
+ (*nr_to_scan)--;
if (type == JBD2_SHRINK_DESTROY) {
ret = __jbd2_journal_remove_checkpoint(jh);
@@ -389,7 +392,7 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
ret = jbd2_journal_try_remove_checkpoint(jh);
if (ret < 0) {
if (type == JBD2_SHRINK_BUSY_SKIP)
- continue;
+ goto next;
break;
}
}
@@ -400,9 +403,10 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
break;
}
+next:
if (need_resched())
break;
- } while (jh != last_jh);
+ } while (jh != last_jh && (!nr_to_scan || *nr_to_scan));
return nr_freed;
}
@@ -424,7 +428,6 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal,
tid_t first_tid = 0, last_tid = 0, next_tid = 0;
tid_t tid = 0;
unsigned long nr_freed = 0;
- unsigned long freed;
bool first_set = false;
again:
@@ -457,10 +460,9 @@ again:
next_transaction = transaction->t_cpnext;
tid = transaction->t_tid;
- freed = journal_shrink_one_cp_list(transaction->t_checkpoint_list,
- JBD2_SHRINK_BUSY_SKIP, &released);
- nr_freed += freed;
- (*nr_to_scan) -= min(*nr_to_scan, freed);
+ nr_freed += journal_shrink_one_cp_list(transaction->t_checkpoint_list,
+ JBD2_SHRINK_BUSY_SKIP,
+ nr_to_scan, &released);
if (*nr_to_scan == 0)
break;
if (need_resched() || spin_needbreak(&journal->j_list_lock))
@@ -516,7 +518,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal,
transaction = next_transaction;
next_transaction = transaction->t_cpnext;
journal_shrink_one_cp_list(transaction->t_checkpoint_list,
- type, &released);
+ type, NULL, &released);
/*
* This function only frees up some memory if possible so we
* dont have an obligation to finish processing. Bail out if
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index f06faf577cea..f8018bfe9c64 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -487,9 +487,12 @@ static const struct file_lock_operations nlmclnt_lock_ops = {
static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host)
{
fl->fl_u.nfs_fl.state = 0;
+ fl->fl_ops = NULL;
fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host,
fl->c.flc_owner);
INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);
+ if (!fl->fl_u.nfs_fl.owner)
+ return;
fl->fl_ops = &nlmclnt_lock_ops;
}
diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h
index e418a50c4180..14cc952fe81a 100644
--- a/fs/lockd/lockd.h
+++ b/fs/lockd/lockd.h
@@ -314,7 +314,7 @@ void nsm_release(struct nsm_handle *nsm);
* This is used in garbage collection and resource reclaim
* A return value != 0 means destroy the lock/block/share
*/
-typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
+typedef int (*nlm_host_match_fn_t)(void *owner, struct nlm_host *ref);
/*
* Server-side lock handling
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 490551369ef2..ee90e743064a 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -47,7 +47,7 @@
static struct svc_program nlmsvc_program;
-const struct nlmsvc_binding *nlmsvc_ops;
+const struct nlmsvc_binding __rcu *nlmsvc_ops;
EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
@@ -142,7 +142,7 @@ lockd(void *vrqstp)
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp, 0);
}
- if (nlmsvc_ops)
+ if (rcu_access_pointer(nlmsvc_ops))
nlmsvc_invalidate_all();
nlm_shutdown_hosts();
cancel_delayed_work_sync(&ln->grace_period_end);
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 78e675470c4b..b73004a7987e 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -128,7 +128,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
- if (!nlmsvc_ops)
+ if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -872,7 +872,8 @@ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
- host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+ host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data,
+ argp->xdrgen.alock.caller_name.len);
if (!host)
return rpc_system_err;
@@ -894,7 +895,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm4_res_wrapper *argp = rqstp->rq_argp;
- if (!nlmsvc_ops)
+ if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 386a881b520f..d410b8c69893 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -133,7 +133,7 @@ nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
- if (!nlmsvc_ops)
+ if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -924,7 +924,7 @@ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm_res_wrapper *argp = rqstp->rq_argp;
- if (!nlmsvc_ops)
+ if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index a0d1a6fbf61e..ffab76278bb2 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -90,22 +90,35 @@ int lock_to_openmode(struct file_lock *lock)
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
+ const struct nlmsvc_binding *ops;
__be32 nlmerr = nlm__int__failed;
__be32 deferred = 0;
int error;
int m;
+ rcu_read_lock();
+ ops = rcu_dereference(nlmsvc_ops);
+ if (!ops || !try_module_get(ops->owner)) {
+ rcu_read_unlock();
+ return nlm__int__failed;
+ }
+ rcu_read_unlock();
+
for (m = O_RDONLY; m <= O_WRONLY; m++) {
struct file **fp = &file->f_file[m];
if (mode != O_RDWR && mode != m)
continue;
- if (*fp)
+ if (*fp) {
+ module_put(ops->owner);
return nlm_granted;
+ }
- error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
- if (!error)
+ error = ops->fopen(rqstp, &file->f_handle, fp, m);
+ if (!error) {
+ module_put(ops->owner);
return nlm_granted;
+ }
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
@@ -122,6 +135,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
}
}
+ module_put(ops->owner);
return deferred ? deferred : nlmerr;
}
@@ -186,6 +200,33 @@ out_free:
}
/*
+ * Release the struct file references held by a nlm_file.
+ */
+static void nlm_release_files(struct nlm_file *file)
+{
+ const struct nlmsvc_binding *ops;
+ bool have_ops;
+
+ rcu_read_lock();
+ ops = rcu_dereference(nlmsvc_ops);
+ have_ops = ops && try_module_get(ops->owner);
+ rcu_read_unlock();
+
+ if (have_ops) {
+ if (file->f_file[O_RDONLY])
+ ops->fclose(file->f_file[O_RDONLY]);
+ if (file->f_file[O_WRONLY])
+ ops->fclose(file->f_file[O_WRONLY]);
+ module_put(ops->owner);
+ } else {
+ if (file->f_file[O_RDONLY])
+ fput(file->f_file[O_RDONLY]);
+ if (file->f_file[O_WRONLY])
+ fput(file->f_file[O_WRONLY]);
+ }
+}
+
+/*
* Delete a file after having released all locks, blocks and shares
*/
static inline void
@@ -194,10 +235,7 @@ nlm_delete_file(struct nlm_file *file)
nlm_debug_print_file("closing file", file);
if (!hlist_unhashed(&file->f_list)) {
hlist_del(&file->f_list);
- 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]);
+ nlm_release_files(file);
kfree(file);
} else {
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
@@ -312,12 +350,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);
}
/*
@@ -327,32 +363,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);
@@ -512,7 +557,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
static int
nlmsvc_match_ip(void *datap, struct nlm_host *host)
{
- return rpc_cmp_addr(nlm_srcaddr(host), datap);
+ return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host);
}
/**
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index ff4e9fd38e83..bc282b744f34 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -231,8 +231,9 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
cb_info->users++;
err_net:
if (!cb_info->users) {
+ xprt_svc_shutdown_bc(xprt);
svc_set_num_threads(cb_info->serv, 0, 0);
- svc_destroy(&cb_info->serv);
+ xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv);
}
err_create:
mutex_unlock(&nfs_callback_mutex);
@@ -254,6 +255,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
mutex_lock(&nfs_callback_mutex);
serv = cb_info->serv;
+ xprt_svc_shutdown_bc(xprt);
nfs_callback_down_net(minorversion, serv, net);
cb_info->users--;
if (cb_info->users == 0) {
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 71c271a1700a..819e435c3a42 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -915,20 +915,22 @@ static int nfs4_server_common_setup(struct nfs_server *server,
return error;
/* data servers support only a subset of NFSv4.1 */
- if (is_ds_only_client(server->nfs_client))
- return -EPROTONOSUPPORT;
+ if (is_ds_only_client(server->nfs_client)) {
+ error = -EPROTONOSUPPORT;
+ goto out_free_delegation_hash;
+ }
/* We must ensure the session is initialised first */
error = nfs4_init_session(server->nfs_client);
if (error < 0)
- return error;
+ goto out_free_delegation_hash;
nfs_server_set_init_caps(server);
/* Probe the root fh to retrieve its FSID and filehandle */
error = nfs4_get_rootfh(server, mntfh, auth_probe);
if (error < 0)
- return error;
+ goto out_free_delegation_hash;
dprintk("Server FSID: %llx:%llx\n",
(unsigned long long) server->fsid.major,
@@ -937,7 +939,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
error = nfs_probe_server(server, mntfh);
if (error < 0)
- return error;
+ goto out_free_delegation_hash;
nfs4_session_limit_rwsize(server);
nfs4_session_limit_xasize(server);
@@ -949,6 +951,11 @@ static int nfs4_server_common_setup(struct nfs_server *server,
server->mount_time = jiffies;
server->destroy = nfs4_destroy_server;
return 0;
+
+out_free_delegation_hash:
+ kfree(server->delegation_hash_table);
+ server->delegation_hash_table = NULL;
+ return error;
}
/*
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 7715e2bd5871..b1a30a16287f 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1389,7 +1389,7 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo)
return false;
return pnfs_mark_layout_stateid_return(lo, &lo->plh_return_segs,
lo->plh_return_iomode,
- lo->plh_return_seq) != EBUSY;
+ lo->plh_return_seq) != -EBUSY;
}
static void pnfs_layoutreturn_before_put_layout_hdr(struct pnfs_layout_hdr *lo)
@@ -2643,6 +2643,7 @@ out_forget:
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);
}
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index dd715cdb6c04..85aa03a7b020 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -292,8 +292,22 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred,
nfs_fh, pnf, fmode);
if (!IS_ERR(localio) && nfs_uuid_add_file(uuid, nfl) < 0) {
- /* Delete the cached file when racing with nfs_uuid_put() */
+ /*
+ * Delete the cached file when racing with nfs_uuid_put().
+ * Since nfl->nfs_uuid was never published via
+ * rcu_assign_pointer(), nfs_close_local_fh() will early-return
+ * and cannot clean up after us. Drop the slot's file ref and
+ * its paired net ref, then drop the caller-owned nfsd_file ref
+ * (+1) and the entry-time nfsd_net ref carried via nf->nf_net,
+ * and return -ENXIO so the caller never dereferences the
+ * now-cleared localio.
+ */
+ struct nfsd_file __rcu *tmp =
+ (struct nfsd_file __force __rcu *)localio;
+
nfs_to_nfsd_file_put_local(pnf);
+ nfs_to_nfsd_file_put_local(&tmp);
+ localio = ERR_PTR(-ENXIO);
}
nfs_to_nfsd_net_put(net);
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 24511c3208db..1ea2bfd51825 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -55,6 +55,17 @@
/* We only care about NFSD_MAY_READ/WRITE for this cache */
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
+/* If the shrinker runs between calls to list_lru_walk_node() in
+ * nfsd_file_gc(), the "remaining" count will be wrong. This could
+ * result in premature freeing of some files. This may not matter much
+ * but is easy to fix with this spinlock which temporarily disables
+ * the shrinker.
+ *
+ * It also serializes callers of nfsd_file_dispose_list_delayed()
+ * against per-net shutdown.
+ */
+static DEFINE_SPINLOCK(nfsd_gc_lock);
+
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations);
@@ -62,11 +73,6 @@ static DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
-struct nfsd_fcache_disposal {
- spinlock_t lock;
- struct list_head freeme;
-};
-
static struct kmem_cache *nfsd_file_slab;
static struct kmem_cache *nfsd_file_mark_slab;
static struct list_lru nfsd_file_lru;
@@ -327,8 +333,11 @@ static void nfsd_file_lru_add(struct nfsd_file *nf)
refcount_inc(&nf->nf_ref);
if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru))
trace_nfsd_file_lru_add(nf);
- else
- WARN_ON(1);
+ else {
+ refcount_dec(&nf->nf_ref);
+ WARN_ON_ONCE(1);
+ return;
+ }
nfsd_file_schedule_laundrette();
}
@@ -419,25 +428,31 @@ nfsd_file_dispose_list(struct list_head *dispose)
}
/**
- * nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list
+ * nfsd_file_dispose_list_delayed - queue dead files for nfsd thread disposal
* @dispose: list of nfsd_files to be disposed
*
- * Transfers each file to the "freeme" list for its nfsd_net, to eventually
- * be disposed of by the per-net garbage collector.
+ * Transfers each file to the dispose list in its nfsd_net and wakes an nfsd
+ * thread to do the actual close. This keeps the cost of fput() in the nfsd
+ * threads rather than in the shrinker or GC worker.
+ *
+ * All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net()
+ * can synchronize against them before draining the per-net dispose list.
+ * This guarantees nf_net is still live when we call net_generic().
*/
static void
nfsd_file_dispose_list_delayed(struct list_head *dispose)
{
- while(!list_empty(dispose)) {
+ lockdep_assert_held(&nfsd_gc_lock);
+
+ while (!list_empty(dispose)) {
struct nfsd_file *nf = list_first_entry(dispose,
struct nfsd_file, nf_gc);
struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
- struct nfsd_fcache_disposal *l = nn->fcache_disposal;
struct svc_serv *serv;
- spin_lock(&l->lock);
- list_move_tail(&nf->nf_gc, &l->freeme);
- spin_unlock(&l->lock);
+ spin_lock(&nn->fcache_dispose_lock);
+ list_move_tail(&nf->nf_gc, &nn->fcache_dispose_list);
+ spin_unlock(&nn->fcache_dispose_lock);
/*
* The filecache laundrette is shut down after the
@@ -461,21 +476,28 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
*/
void nfsd_file_net_dispose(struct nfsd_net *nn)
{
- struct nfsd_fcache_disposal *l = nn->fcache_disposal;
-
- if (!list_empty(&l->freeme)) {
+ if (!list_empty(&nn->fcache_dispose_list)) {
LIST_HEAD(dispose);
int i;
- spin_lock(&l->lock);
- for (i = 0; i < 8 && !list_empty(&l->freeme); i++)
- list_move(l->freeme.next, &dispose);
- spin_unlock(&l->lock);
- if (!list_empty(&l->freeme))
- /* Wake up another thread to share the work
+ spin_lock(&nn->fcache_dispose_lock);
+ for (i = 0; i < 8 && !list_empty(&nn->fcache_dispose_list); i++)
+ list_move(nn->fcache_dispose_list.next, &dispose);
+ spin_unlock(&nn->fcache_dispose_lock);
+ if (!list_empty(&nn->fcache_dispose_list)) {
+ /*
+ * Wake up another thread to share the work
* *before* doing any actual disposing.
+ *
+ * The filecache laundrette is shut down after
+ * the nn->nfsd_serv pointer is cleared, but
+ * before the svc_serv is freed.
*/
- svc_wake_up(nn->nfsd_serv);
+ struct svc_serv *serv = nn->nfsd_serv;
+
+ if (serv)
+ svc_wake_up(serv);
+ }
nfsd_file_dispose_list(&dispose);
}
}
@@ -552,13 +574,6 @@ nfsd_file_gc_cb(struct list_head *item, struct list_lru_one *lru,
return nfsd_file_lru_cb(item, lru, arg);
}
-/* If the shrinker runs between calls to list_lru_walk_node() in
- * nfsd_file_gc(), the "remaining" count will be wrong. This could
- * result in premature freeing of some files. This may not matter much
- * but is easy to fix with this spinlock which temporarily disables
- * the shrinker.
- */
-static DEFINE_SPINLOCK(nfsd_gc_lock);
static void
nfsd_file_gc(void)
{
@@ -581,9 +596,9 @@ nfsd_file_gc(void)
remaining = 0;
}
}
+ nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
- nfsd_file_dispose_list_delayed(&dispose);
}
static void
@@ -611,9 +626,9 @@ nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
nfsd_file_lru_cb, &dispose);
+ nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru));
- nfsd_file_dispose_list_delayed(&dispose);
return ret;
}
@@ -686,11 +701,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
}
/**
- * nfsd_file_close_inode - attempt a delayed close of a nfsd_file
+ * nfsd_file_close_inode - attempt a deferred close of a nfsd_file
* @inode: inode of the file to attempt to remove
*
* Close out any open nfsd_files that can be reaped for @inode. The
- * actual freeing is deferred to the dispose_list_delayed infrastructure.
+ * actual freeing is deferred to the nfsd service threads.
*
* This is used by the fsnotify callbacks and setlease notifier.
*/
@@ -699,8 +714,10 @@ nfsd_file_close_inode(struct inode *inode)
{
LIST_HEAD(dispose);
+ spin_lock(&nfsd_gc_lock);
nfsd_file_queue_for_close(inode, &dispose);
nfsd_file_dispose_list_delayed(&dispose);
+ spin_unlock(&nfsd_gc_lock);
}
/**
@@ -940,42 +957,14 @@ __nfsd_file_cache_purge(struct net *net)
nfsd_file_dispose_list(&dispose);
}
-static struct nfsd_fcache_disposal *
-nfsd_alloc_fcache_disposal(void)
-{
- struct nfsd_fcache_disposal *l;
-
- l = kmalloc_obj(*l);
- if (!l)
- return NULL;
- spin_lock_init(&l->lock);
- INIT_LIST_HEAD(&l->freeme);
- return l;
-}
-
-static void
-nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
-{
- nfsd_file_dispose_list(&l->freeme);
- kfree(l);
-}
-
-static void
-nfsd_free_fcache_disposal_net(struct net *net)
-{
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- struct nfsd_fcache_disposal *l = nn->fcache_disposal;
-
- nfsd_free_fcache_disposal(l);
-}
-
int
nfsd_file_cache_start_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nn->fcache_disposal = nfsd_alloc_fcache_disposal();
- return nn->fcache_disposal ? 0 : -ENOMEM;
+ spin_lock_init(&nn->fcache_dispose_lock);
+ INIT_LIST_HEAD(&nn->fcache_dispose_list);
+ return 0;
}
/**
@@ -994,8 +983,18 @@ nfsd_file_cache_purge(struct net *net)
void
nfsd_file_cache_shutdown_net(struct net *net)
{
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
nfsd_file_cache_purge(net);
- nfsd_free_fcache_disposal_net(net);
+ /*
+ * Ensure any in-progress shrinker, GC, or fsnotify/lease callback
+ * (all of which hold nfsd_gc_lock while calling
+ * nfsd_file_dispose_list_delayed()) has fully completed before
+ * draining the per-net dispose list.
+ */
+ spin_lock(&nfsd_gc_lock);
+ spin_unlock(&nfsd_gc_lock);
+ nfsd_file_dispose_list(&nn->fcache_dispose_list);
}
void
@@ -1227,7 +1226,7 @@ open_file:
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
if (type != S_IFREG || nf->nf_mark) {
- if (file) {
+ if (file && (file->f_mode & FMODE_OPENED)) {
get_file(file);
nf->nf_file = file;
status = nfs_ok;
@@ -1374,12 +1373,12 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
* @rqstp: the RPC transaction being executed
* @fhp: the NFS filehandle of the file just created
* @may_flags: NFSD_MAY_ settings for the file
- * @file: cached, already-open file (may be NULL)
+ * @file: cached, already-open file (may be NULL or not yet opened)
* @pnf: OUT: new or found "struct nfsd_file" object
*
* Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
- * and @file is non-NULL, use it to instantiate a new nfsd_file instead of
- * opening a new one.
+ * and @file has FMODE_OPENED set, use it to instantiate a new nfsd_file
+ * instead of opening a new one.
*
* Return values:
* %nfs_ok - @pnf points to an nfsd_file with its reference
diff --git a/fs/nfsd/flexfilelayoutxdr.c b/fs/nfsd/flexfilelayoutxdr.c
index f9f7e38cba13..374e52d3064a 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/localio.c b/fs/nfsd/localio.c
index be710d809a3b..c3eb0557b3e1 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -97,11 +97,15 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
}
nfsd_file_get(localio);
again:
+ rcu_read_lock();
new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
if (new) {
/* Some other thread installed an nfsd_file */
- if (nfsd_file_get(new) == NULL)
+ if (nfsd_file_get(new) == NULL) {
+ rcu_read_unlock();
goto again;
+ }
+ rcu_read_unlock();
/*
* Drop the ref we were going to install (both file and
* net) and the one we were going to return (only file).
@@ -110,6 +114,8 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
nfsd_net_put(net);
nfsd_file_put(localio);
localio = new;
+ } else {
+ rcu_read_unlock();
}
} else
nfsd_net_put(net);
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 6fe1325815e0..72a5b499839d 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -92,6 +92,7 @@ nlm_fclose(struct file *filp)
}
static const struct nlmsvc_binding nfsd_nlm_ops = {
+ .owner = THIS_MODULE,
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
@@ -100,11 +101,12 @@ void
nfsd_lockd_init(void)
{
dprintk("nfsd: initializing lockd\n");
- nlmsvc_ops = &nfsd_nlm_ops;
+ rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
}
void
nfsd_lockd_shutdown(void)
{
- nlmsvc_ops = NULL;
+ RCU_INIT_POINTER(nlmsvc_ops, NULL);
+ synchronize_rcu();
}
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 27da1a3edacb..a7bd7b67fa4f 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -28,6 +28,16 @@ struct cld_net;
struct nfsd_net_cb;
struct nfsd4_client_tracking_ops;
+enum nfsd_net_flag {
+ NFSD_NET_GRACE_ENDED,
+ NFSD_NET_GRACE_END_FORCED,
+ NFSD_NET_IN_GRACE,
+ NFSD_NET_SOMEBODY_RECLAIMED,
+ NFSD_NET_TRACK_RECLAIM_COMPLETES,
+ NFSD_NET_UP,
+ NFSD_NET_LOCKD_UP,
+};
+
enum {
/* cache misses due only to checksum comparison failures */
NFSD_STATS_PAYLOAD_MISSES,
@@ -66,9 +76,9 @@ struct nfsd_net {
struct cache_detail *nametoid_cache;
struct lock_manager nfsd4_manager;
- bool grace_ended;
- bool grace_end_forced;
+ unsigned long flags;
time64_t boot_time;
+ time64_t boot_time_bt; /* same instant in CLOCK_BOOTTIME */
struct dentry *nfsd_client_dir;
@@ -84,6 +94,7 @@ struct nfsd_net {
*/
struct list_head *reclaim_str_hashtbl;
int reclaim_str_hashtbl_size;
+ struct rw_semaphore reclaim_str_hashtbl_lock;
struct list_head *conf_id_hashtbl;
struct rb_root conf_name_tree;
struct list_head *unconf_id_hashtbl;
@@ -96,12 +107,16 @@ struct nfsd_net {
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*
- * All of the above fields are protected by the client_mutex.
+ * reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by
+ * reclaim_str_hashtbl_lock.
+ *
+ * All of the remaining fields are protected by the client_lock.
*/
struct list_head client_lru;
struct list_head close_lru;
- /* protects del_recall_lru and delegation hash/unhash */
+ /* protects del_recall_lru and delegation hash/unhash;
+ * nests outside client_lock */
spinlock_t deleg_lock ____cacheline_aligned;
struct list_head del_recall_lru;
@@ -110,26 +125,21 @@ struct nfsd_net {
struct delayed_work laundromat_work;
- /* client_lock protects the client lru list and session hash table */
+ /* client_lock protects the client lru list and session hash
+ * table; nests inside deleg_lock */
spinlock_t client_lock;
/* protects blocked_locks_lru */
spinlock_t blocked_locks_lock;
struct file *rec_file;
- bool in_grace;
const struct nfsd4_client_tracking_ops *client_tracking_ops;
time64_t nfsd4_lease;
time64_t nfsd4_grace;
- bool somebody_reclaimed;
- bool track_reclaim_completes;
atomic_t nr_reclaim_complete;
- bool nfsd_net_up;
- bool lockd_up;
-
seqlock_t writeverf_lock;
unsigned char writeverf[8];
@@ -209,7 +219,8 @@ struct nfsd_net {
/* utsname taken from the process that starts the server */
char nfsd_name[UNX_MAXNODENAME+1];
- struct nfsd_fcache_disposal *fcache_disposal;
+ spinlock_t fcache_dispose_lock;
+ struct list_head fcache_dispose_list;
siphash_key_t siphash_key;
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 76305b86c1a9..2998640f259d 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 e87731380be8..a87f9d7f32be 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/nfs3proc.c b/fs/nfsd/nfs3proc.c
index aeda7a802bdf..617a70d13292 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -29,6 +29,25 @@ static int nfs3_ftypes[] = {
S_IFIFO, /* NF3FIFO */
};
+/*
+ * Reject a client-supplied atime or mtime whose nanoseconds field is out
+ * of range. Such a value is well-formed on the wire but is not a valid
+ * timespec64, and storing it verbatim can corrupt on-disk timestamps.
+ * tv_nsec is a long, so it is cast to unsigned long (the same width) to
+ * catch both an over-large value and one that became negative when an
+ * out-of-range u32 wire nseconds was assigned to a 32-bit long.
+ */
+static bool nfsd3_time_in_range(const struct iattr *iap)
+{
+ if ((iap->ia_valid & ATTR_ATIME_SET) &&
+ (unsigned long)iap->ia_atime.tv_nsec >= NSEC_PER_SEC)
+ return false;
+ if ((iap->ia_valid & ATTR_MTIME_SET) &&
+ (unsigned long)iap->ia_mtime.tv_nsec >= NSEC_PER_SEC)
+ return false;
+ return true;
+}
+
static __be32 nfsd3_map_status(__be32 status)
{
switch (status) {
@@ -101,9 +120,14 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
+ if (!nfsd3_time_in_range(&argp->attrs)) {
+ resp->status = nfserr_inval;
+ goto out;
+ }
if (argp->check_guard)
guardtime = &argp->guardtime;
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime);
+out:
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
@@ -265,6 +289,8 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
trace_nfsd_vfs_create(rqstp, fhp, S_IFREG, argp->name, argp->len);
+ if (!nfsd3_time_in_range(iap))
+ return nfserr_inval;
if (isdotent(argp->name, argp->len))
return nfserr_exist;
if (!(iap->ia_valid & ATTR_MODE))
@@ -400,8 +426,13 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
argp->attrs.ia_valid &= ~ATTR_SIZE;
fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE);
+ if (!nfsd3_time_in_range(&argp->attrs)) {
+ resp->status = nfserr_inval;
+ goto out;
+ }
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, S_IFDIR, 0, &resp->fh);
+out:
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
@@ -415,6 +446,10 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
.na_iattr = &argp->attrs,
};
+ if (!nfsd3_time_in_range(&argp->attrs)) {
+ resp->status = nfserr_inval;
+ goto out;
+ }
if (argp->tlen == 0) {
resp->status = nfserr_inval;
goto out;
@@ -471,6 +506,11 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
goto out;
}
+ if (!nfsd3_time_in_range(&argp->attrs)) {
+ resp->status = nfserr_inval;
+ goto out;
+ }
+
type = nfs3_ftypes[argp->ftype];
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, type, rdev, &resp->fh);
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 50827405468d..7c868afc329e 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -108,6 +108,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access))
return -EIO;
+ if (access.nseconds >= NSEC_PER_SEC)
+ return -EIO;
fattr->ncf_cb_atime.tv_sec = access.seconds;
fattr->ncf_cb_atime.tv_nsec = access.nseconds;
@@ -117,6 +119,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify))
return -EIO;
+ if (modify.nseconds >= NSEC_PER_SEC)
+ return -EIO;
fattr->ncf_cb_mtime.tv_sec = modify.seconds;
fattr->ncf_cb_mtime.tv_nsec = modify.nseconds;
@@ -456,13 +460,20 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
const struct nfsd4_callback *cb,
struct nfs4_cb_compound_hdr *hdr)
{
- struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
+ struct nfsd4_session *session;
struct nfsd4_referring_call_list *rcl;
__be32 *p;
if (hdr->minorversion == 0)
return;
+ rcu_read_lock();
+ session = rcu_dereference(cb->cb_clp->cl_cb_session);
+ if (!session) {
+ rcu_read_unlock();
+ return;
+ }
+
encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE);
encode_sessionid4(xdr, session);
@@ -478,6 +489,7 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
encode_referring_call_list4(xdr, rcl);
hdr->nops++;
+ rcu_read_unlock();
}
static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
@@ -529,21 +541,32 @@ static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
static int decode_cb_sequence4resok(struct xdr_stream *xdr,
struct nfsd4_callback *cb)
{
- struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
+ struct nfsd4_session *session;
int status = -ESERVERFAULT;
__be32 *p;
u32 seqid, slotid, target;
+ rcu_read_lock();
+ session = rcu_dereference(cb->cb_clp->cl_cb_session);
+ if (!session) {
+ rcu_read_unlock();
+ cb->cb_seq_status = -NFS4ERR_BADSESSION;
+ return -NFS4ERR_BADSESSION;
+ }
+
/*
* If the server returns different values for sessionID, slotID or
* sequence number, the server is looney tunes.
*/
p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4);
- if (unlikely(p == NULL))
+ if (unlikely(p == NULL)) {
+ rcu_read_unlock();
goto out_overflow;
+ }
if (memcmp(p, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
dprintk("NFS: %s Invalid session id\n", __func__);
+ rcu_read_unlock();
goto out;
}
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
@@ -551,12 +574,14 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
seqid = be32_to_cpup(p++);
if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) {
dprintk("NFS: %s Invalid sequence number\n", __func__);
+ rcu_read_unlock();
goto out;
}
slotid = be32_to_cpup(p++);
if (slotid != cb->cb_held_slot) {
dprintk("NFS: %s Invalid slotid\n", __func__);
+ rcu_read_unlock();
goto out;
}
@@ -564,6 +589,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
target = be32_to_cpup(p++);
update_cb_slot_table(session, target);
+ rcu_read_unlock();
status = 0;
out:
cb->cb_seq_status = status;
@@ -1150,9 +1176,8 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
} else {
if (!conn->cb_xprt || !ses)
return -EINVAL;
- clp->cl_cb_session = ses;
args.bc_xprt = conn->cb_xprt;
- args.prognumber = clp->cl_cb_session->se_cb_prog;
+ args.prognumber = ses->se_cb_prog;
args.protocol = conn->cb_xprt->xpt_class->xcl_ident |
XPRT_TRANSPORT_BC;
args.authflavor = ses->se_cb_sec.flavor;
@@ -1170,8 +1195,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
return -ENOMEM;
}
- if (clp->cl_minorversion != 0)
+ if (clp->cl_minorversion != 0) {
clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
+ rcu_assign_pointer(clp->cl_cb_session, ses);
+ }
clp->cl_cb_client = client;
clp->cl_cb_cred = cred;
rcu_read_lock();
@@ -1278,18 +1305,33 @@ static int grab_slot(struct nfsd4_session *ses)
static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
{
struct nfs4_client *clp = cb->cb_clp;
- struct nfsd4_session *ses = clp->cl_cb_session;
+ struct nfsd4_session *ses;
if (cb->cb_held_slot >= 0)
return true;
+
+ rcu_read_lock();
+ ses = rcu_dereference(clp->cl_cb_session);
+ if (!ses) {
+ rcu_read_unlock();
+ rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
+ return false;
+ }
cb->cb_held_slot = grab_slot(ses);
if (cb->cb_held_slot < 0) {
+ rcu_read_unlock();
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
/* Race breaker */
- cb->cb_held_slot = grab_slot(ses);
+ rcu_read_lock();
+ ses = rcu_dereference(clp->cl_cb_session);
+ if (ses)
+ cb->cb_held_slot = grab_slot(ses);
+ rcu_read_unlock();
if (cb->cb_held_slot < 0)
return false;
rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
+ } else {
+ rcu_read_unlock();
}
return true;
}
@@ -1297,12 +1339,17 @@ static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
static void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
- struct nfsd4_session *ses = clp->cl_cb_session;
+ struct nfsd4_session *ses;
if (cb->cb_held_slot >= 0) {
- spin_lock(&ses->se_lock);
- ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
- spin_unlock(&ses->se_lock);
+ rcu_read_lock();
+ ses = rcu_dereference(clp->cl_cb_session);
+ if (ses) {
+ spin_lock(&ses->se_lock);
+ ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
+ spin_unlock(&ses->se_lock);
+ }
+ rcu_read_unlock();
cb->cb_held_slot = -1;
rpc_wake_up_next(&clp->cl_cb_waitq);
}
@@ -1434,22 +1481,35 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
trace_nfsd_cb_rpc_prepare(clp);
cb->cb_seq_status = 1;
cb->cb_status = 0;
- if (minorversion && !nfsd41_cb_get_slot(cb, task))
- return;
+ if (minorversion) {
+ if (!rcu_access_pointer(clp->cl_cb_session)) {
+ rpc_exit(task, -EIO);
+ return;
+ }
+ if (!nfsd41_cb_get_slot(cb, task))
+ return;
+ }
rpc_call_start(task);
}
/* Returns true if CB_COMPOUND processing should continue */
static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb)
{
- struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
+ struct nfsd4_session *session;
bool ret = false;
if (cb->cb_held_slot < 0)
goto requeue;
+ rcu_read_lock();
+ session = rcu_dereference(cb->cb_clp->cl_cb_session);
+ if (!session) {
+ rcu_read_unlock();
+ goto requeue;
+ }
+
/* This is the operation status code for CB_SEQUENCE */
- trace_nfsd_cb_seq_status(task, cb);
+ trace_nfsd_cb_seq_status(task, cb, session);
switch (cb->cb_seq_status) {
case 0:
/*
@@ -1481,12 +1541,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
fallthrough;
case -NFS4ERR_BADSESSION:
nfsd4_mark_cb_fault(cb->cb_clp);
+ rcu_read_unlock();
goto requeue;
case -NFS4ERR_DELAY:
cb->cb_seq_status = 1;
- if (RPC_SIGNALLED(task) || !rpc_restart_call(task))
+ if (RPC_SIGNALLED(task) || !rpc_restart_call(task)) {
+ rcu_read_unlock();
goto requeue;
+ }
rpc_delay(task, 2 * HZ);
+ rcu_read_unlock();
return false;
case -NFS4ERR_SEQ_MISORDERED:
case -NFS4ERR_BADSLOT:
@@ -1498,11 +1562,13 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
*/
nfsd4_mark_cb_fault(cb->cb_clp);
cb->cb_held_slot = -1;
+ rcu_read_unlock();
goto retry_nowait;
default:
nfsd4_mark_cb_fault(cb->cb_clp);
}
- trace_nfsd_cb_free_slot(task, cb);
+ trace_nfsd_cb_free_slot(task, cb, session);
+ rcu_read_unlock();
nfsd41_cb_release_slot(cb);
return ret;
retry_nowait:
@@ -1624,7 +1690,15 @@ static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
* Note there isn't a lot of locking in this code; instead we depend on
* the fact that it is run from clp->cl_callback_wq, which won't run two
* work items at once. So, for example, clp->cl_callback_wq handles all
- * access of cl_cb_client and all calls to rpc_create or rpc_shutdown_client.
+ * access of cl_cb_client, and all calls to rpc_create or
+ * rpc_shutdown_client.
+ *
+ * cl_cb_session is written only from cl_callback_wq (via
+ * rcu_assign_pointer) and read from rpciod under rcu_read_lock (via
+ * rcu_dereference) by encode_cb_sequence4args(), decode_cb_sequence4resok(),
+ * nfsd4_cb_sequence_done(), and the cb-slot helpers. Sessions are freed
+ * with kfree_rcu() so that rpciod readers in an RCU read-side critical
+ * section never dereference a freed session.
*/
static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
{
@@ -1676,6 +1750,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
nfsd4_mark_cb_down(clp);
if (c)
svc_xprt_put(c->cn_xprt);
+ rcu_assign_pointer(clp->cl_cb_session, ses);
return;
}
}
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index f34320e4c2f4..279ff1e9dffb 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -247,13 +247,20 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
nfsd4_init_cb(&ls->ls_recall, clp, &nfsd4_cb_layout_ops,
NFSPROC4_CLNT_CB_LAYOUT);
- if (parent->sc_type == SC_TYPE_DELEG)
+ if (parent->sc_type == SC_TYPE_DELEG) {
+ spin_lock(&fp->fi_lock);
ls->ls_file = nfsd_file_get(fp->fi_deleg_file);
- else
+ spin_unlock(&fp->fi_lock);
+ } else {
ls->ls_file = find_any_file(fp);
- BUG_ON(!ls->ls_file);
+ }
+ if (!ls->ls_file) {
+ nfs4_put_stid(stp);
+ return NULL;
+ }
ls->ls_fenced = false;
+ ls->ls_fence_inflight = false;
ls->ls_fence_delay = 0;
INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
@@ -791,15 +798,6 @@ nfsd4_layout_fence_worker(struct work_struct *work)
struct nfs4_client *clp;
struct nfsd_net *nn;
- /*
- * The workqueue clears WORK_STRUCT_PENDING before invoking
- * this callback. Re-arm immediately so that
- * delayed_work_pending() returns true while the fence
- * operation is in progress, preventing
- * lm_breaker_timedout() from taking a duplicate reference.
- */
- mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0);
-
spin_lock(&ls->ls_lock);
if (list_empty(&ls->ls_layouts)) {
spin_unlock(&ls->ls_lock);
@@ -809,6 +807,9 @@ dispose:
nfsd4_close_layout(ls);
ls->ls_fenced = true;
+ spin_lock(&ls->ls_lock);
+ ls->ls_fence_inflight = false;
+ spin_unlock(&ls->ls_lock);
nfs4_put_stid(&ls->ls_stid);
return;
}
@@ -894,18 +895,26 @@ nfsd4_layout_lm_breaker_timedout(struct file_lease *fl)
if ((!nfsd4_layout_ops[ls->ls_layout_type]->fence_client) ||
ls->ls_fenced)
return true;
- if (delayed_work_pending(&ls->ls_fence_work))
- return false;
/*
* Make sure layout has not been returned yet before
- * taking a reference count on the layout stateid.
+ * taking a reference count on the layout stateid. The
+ * ls_fence_inflight flag is set together with the sc_count
+ * increment under ls_lock so that a fence worker invocation
+ * already in progress (which has cleared WORK_STRUCT_PENDING
+ * but not yet reached dispose:) cannot be coalesced with a
+ * fresh schedule that takes an extra unmatched reference.
*/
spin_lock(&ls->ls_lock);
+ if (ls->ls_fence_inflight) {
+ spin_unlock(&ls->ls_lock);
+ return false;
+ }
if (list_empty(&ls->ls_layouts) ||
!refcount_inc_not_zero(&ls->ls_stid.sc_count)) {
spin_unlock(&ls->ls_lock);
return true;
}
+ ls->ls_fence_inflight = true;
spin_unlock(&ls->ls_lock);
mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8561540ab2db..e3c3249e63be 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -306,10 +306,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}
- status = fh_compose(resfhp, fhp->fh_export, child, fhp);
- if (status != nfs_ok)
- goto out;
-
v_mtime = 0;
v_atime = 0;
if (nfsd4_create_is_exclusive(open->op_createmode)) {
@@ -335,6 +331,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (status != nfs_ok)
goto out;
+ status = fh_compose(resfhp, fhp->fh_export, child, fhp);
+ if (status != nfs_ok)
+ goto out;
+
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
if (!d_is_reg(child))
@@ -385,6 +385,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
open->op_created = true;
fh_fill_post_attrs(fhp);
+ status = fh_compose(resfhp, fhp->fh_export, child, fhp);
+ if (status != nfs_ok)
+ goto out;
+
/* A newly created file already has a file size of zero. */
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;
@@ -663,7 +667,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n",
be32_to_cpu(status));
if (reclaim && !status)
- nn->somebody_reclaimed = true;
+ set_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags);
out:
if (open->op_filp) {
fput(open->op_filp);
@@ -677,8 +681,6 @@ out:
nfsd4_cleanup_open_state(cstate, open);
nfsd4_bump_seqid(cstate, status);
out_err:
- posix_acl_release(open->op_dpacl);
- posix_acl_release(open->op_pacl);
return status;
}
@@ -700,6 +702,13 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
return nfsd4_open(rqstp, cstate, &op->u);
}
+static void
+nfsd4_open_release(union nfsd4_op_u *u)
+{
+ posix_acl_release(u->open.op_dpacl);
+ posix_acl_release(u->open.op_pacl);
+}
+
/*
* filehandle-manipulating ops.
*/
@@ -839,6 +848,20 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out_aftermask;
+ /* Sanitize cr_type to avoid returning ATTRNOTSUPP. */
+ switch (create->cr_type) {
+ case NF4LNK:
+ case NF4BLK:
+ case NF4CHR:
+ case NF4SOCK:
+ case NF4FIFO:
+ case NF4DIR:
+ break;
+ default:
+ status = nfserr_badtype;
+ goto out_aftermask;
+ }
+
if (create->cr_acl) {
if (attrs.na_dpacl || attrs.na_pacl) {
status = nfserr_inval;
@@ -846,6 +869,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl,
&attrs);
+ if (status != nfs_ok)
+ goto out_aftermask;
}
current->fs->umask = create->cr_umask;
switch (create->cr_type) {
@@ -1253,7 +1278,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (deleg_attrs) {
status = nfserr_bad_stateid;
- if (st->sc_type & SC_TYPE_DELEG) {
+ if (st && (st->sc_type & SC_TYPE_DELEG)) {
struct nfs4_delegation *dp = delegstateid(st);
/* Only for *_ATTRS_DELEG flavors */
@@ -1494,6 +1519,9 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
{
if (!refcount_dec_and_test(&copy->refcount))
return;
+ /* Drop the task_struct pinned in nfsd4_copy(); NULL on sync copies. */
+ if (copy->copy_task)
+ put_task_struct(copy->copy_task);
kfree(copy->cp_src);
kfree(copy);
}
@@ -1503,20 +1531,18 @@ static void release_copy_files(struct nfsd4_copy *copy);
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
{
trace_nfsd_copy_async_cancel(copy);
- if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
- kthread_stop(copy->copy_task);
- if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
- copy->nfserr = nfs_ok;
- set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
- }
-
/*
- * The copy was removed from async_copies before this function
- * was called, so the reaper cannot clean it up. Release files
- * here regardless of who won the STOPPED race. If the thread
- * set STOPPED, it has finished using the files. If STOPPED
- * was set here, kthread_stop() waited for the thread to exit.
+ * Join the kthread before releasing its resources. The task_struct is
+ * pinned in nfsd4_copy(), so kthread_stop() is safe even after the
+ * one-shot kthread has exited. The caller already unlinked the copy,
+ * so this runs once per copy.
*/
+ set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
+ kthread_stop(copy->copy_task);
+ if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
+ copy->nfserr = nfs_ok;
+ set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
+
release_copy_files(copy);
nfs4_put_copy(copy);
}
@@ -1530,7 +1556,13 @@ static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
copy = list_first_entry(&clp->async_copies, struct nfsd4_copy,
copies);
refcount_inc(&copy->refcount);
- copy->cp_clp = NULL;
+ /*
+ * Unlinking hides the copy from the reaper, so drop its
+ * s2s_cp_stateids entry here while cp_clp is still valid.
+ */
+ nfs4_free_copy_state(copy);
+ /* Pairs with smp_load_acquire() in nfsd4_send_cb_offload(). */
+ smp_store_release(&copy->cp_clp, NULL);
if (!list_empty(&copy->copies))
list_del_init(&copy->copies);
}
@@ -1542,8 +1574,11 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
{
struct nfsd4_copy *copy;
- while ((copy = nfsd4_unhash_copy(clp)) != NULL)
+ while ((copy = nfsd4_unhash_copy(clp)) != NULL) {
nfsd4_stop_copy(copy);
+ /* Reaper can't reach the unhashed copy; drop its membership ref. */
+ nfs4_put_copy(copy);
+ }
}
static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
@@ -1606,7 +1641,11 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
struct nfs4_client *clp = copy->cp_clp;
list_del_init(&copy->copies);
+ /* Reaper can't reach it; drop the s2s entry while cp_clp is valid. */
+ nfs4_free_copy_state(copy);
nfsd4_stop_copy(copy);
+ /* Drop the membership ref the reaper would have dropped. */
+ nfs4_put_copy(copy);
nfsd4_put_client(clp);
}
}
@@ -1901,6 +1940,8 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
nfsd4_put_client(cb->cb_clp);
+ /* Drop the copy reference taken in nfsd4_send_cb_offload(). */
+ nfs4_put_copy(copy);
}
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
@@ -1950,6 +1991,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 {
/* Only async copies can be stopped here */
if (kthread_should_stop())
@@ -1965,13 +2007,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, &copy->cp_flags);
+ else if (status != -EAGAIN && status != -ESTALE)
+ nfsd_reset_write_verifier(copy->cp_nn);
}
return bytes_copied;
}
@@ -2030,28 +2073,29 @@ static void release_copy_files(struct nfsd4_copy *copy)
}
}
+/*
+ * Called from the reaper and from nfsd4_copy()'s error path; in both
+ * cases the copy is already unreachable from clp->async_copies.
+ */
static void cleanup_async_copy(struct nfsd4_copy *copy)
{
nfs4_free_copy_state(copy);
release_copy_files(copy);
- if (copy->cp_clp) {
- spin_lock(&copy->cp_clp->async_lock);
- if (!list_empty(&copy->copies))
- list_del_init(&copy->copies);
- spin_unlock(&copy->cp_clp->async_lock);
- }
nfs4_put_copy(copy);
}
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
{
struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload;
- struct nfs4_client *clp = copy->cp_clp;
+ struct nfs4_client *clp;
/*
- * cp_clp is NULL when called via nfsd4_shutdown_copy() during
- * client destruction. Skip the callback; the client is gone.
+ * Pairs with smp_store_release(&cp_clp) in find_async_copy() and
+ * nfsd4_unhash_copy(); the set_bit/clear_bit writers are unordered.
+ * cp_clp is NULL once the copy was canceled; skip the callback, the
+ * canceling path owns the notification.
*/
+ clp = smp_load_acquire(&copy->cp_clp);
if (!clp) {
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
return;
@@ -2063,10 +2107,12 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
cbo->co_retries = 5;
/*
- * Hold a reference on the client while the callback is in flight.
- * Released in nfsd4_cb_offload_release().
+ * Hold the client and the copy across the in-flight callback; co_cb is
+ * embedded in the copy, so it must outlive the callback. Both are
+ * dropped in nfsd4_cb_offload_release().
*/
kref_get(&clp->cl_nfsdfs.cl_ref);
+ refcount_inc(&copy->refcount);
nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops,
NFSPROC4_CLNT_CB_OFFLOAD);
@@ -2118,16 +2164,20 @@ static int nfsd4_do_async_copy(void *data)
do_callback:
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
copy->nfserr = nfserr;
- /* The kthread exits forthwith. Ensure that a subsequent
- * OFFLOAD_CANCEL won't try to kill it again. */
- set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
-
+ /*
+ * Don't set NFSD4_COPY_F_STOPPED here: it tells a teardown caller it
+ * may skip kthread_stop(), which would then release nf_dst and the
+ * client while still in use. Only nfsd4_stop_copy() sets it, after
+ * joining.
+ */
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
trace_nfsd_copy_async_done(copy);
atomic_dec(&copy->cp_nn->pending_async_copies);
if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update)
nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0);
nfsd4_send_cb_offload(copy);
+ /* Drop the kthread's reference (taken in nfsd4_copy()); copy may be freed after this. */
+ nfs4_put_copy(copy);
return 0;
}
@@ -2153,21 +2203,21 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
status = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
if (status) {
- trace_nfsd_copy_done(copy, status);
- return nfserr_offload_denied;
+ status = nfserr_offload_denied;
+ goto out;
}
} else {
trace_nfsd_copy_intra(copy);
status = nfsd4_setup_intra_ssc(rqstp, cstate, copy);
- if (status) {
- trace_nfsd_copy_done(copy, status);
- return status;
- }
+ if (status)
+ goto out;
}
memcpy(&copy->fh, &cstate->current_fh.fh_handle,
sizeof(struct knfsd_fh));
if (nfsd4_copy_is_async(copy)) {
+ struct task_struct *task;
+
async_copy = kzalloc_obj(struct nfsd4_copy);
if (!async_copy)
goto out_err;
@@ -2182,11 +2232,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
async_copy->cp_src = kmalloc_obj(*async_copy->cp_src);
if (!async_copy->cp_src)
goto out_dec_async_copy_err;
- if (!nfs4_init_copy_state(nn, copy))
+ dup_copy_fields(copy, async_copy);
+
+ if (!nfs4_init_copy_state(nn, async_copy))
goto out_dec_async_copy_err;
- memcpy(&result->cb_stateid, &copy->cp_stateid.cs_stid,
+ memcpy(&result->cb_stateid, &async_copy->cp_stateid.cs_stid,
sizeof(result->cb_stateid));
- dup_copy_fields(copy, async_copy);
if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) &
FMODE_NOCMTIME) != 0)
async_copy->attr_update = true;
@@ -2195,15 +2246,27 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
NFS4_MAX_SESSIONID_LEN);
async_copy->cp_cb_offload.co_referring_slotid = cstate->slot->sl_index;
async_copy->cp_cb_offload.co_referring_seqno = cstate->slot->sl_seqid;
- async_copy->copy_task = kthread_create(nfsd4_do_async_copy,
- async_copy, "%s", "copy thread");
- if (IS_ERR(async_copy->copy_task))
+ task = kthread_create(nfsd4_do_async_copy, async_copy,
+ "%s", "copy thread");
+ if (IS_ERR(task))
goto out_dec_async_copy_err;
+ /*
+ * Pin the task_struct so kthread_stop() is safe after this
+ * one-shot kthread exits. Released by nfs4_put_copy().
+ */
+ get_task_struct(task);
+ async_copy->copy_task = task;
+ /*
+ * Take the kthread's ref and wake it before publishing, so the
+ * publisher touches async_copy no further and teardown can
+ * drain it.
+ */
+ refcount_inc(&async_copy->refcount);
+ wake_up_process(async_copy->copy_task);
spin_lock(&async_copy->cp_clp->async_lock);
list_add(&async_copy->copies,
&async_copy->cp_clp->async_copies);
spin_unlock(&async_copy->cp_clp->async_lock);
- wake_up_process(async_copy->copy_task);
status = nfs_ok;
} else {
status = nfsd4_do_copy(copy, copy->nf_src->nf_file,
@@ -2257,8 +2320,18 @@ find_async_copy(struct nfs4_client *clp, stateid_t *stateid)
spin_lock(&clp->async_lock);
copy = find_async_copy_locked(clp, stateid);
- if (copy)
+ if (copy) {
refcount_inc(&copy->refcount);
+ nfs4_free_copy_state(copy);
+ /*
+ * Mirror nfsd4_unhash_copy(): unlink and clear cp_clp under
+ * async_lock so the reaper can't reach it. Caller drops the
+ * membership ref after nfsd4_stop_copy().
+ */
+ smp_store_release(&copy->cp_clp, NULL);
+ if (!list_empty(&copy->copies))
+ list_del_init(&copy->copies);
+ }
spin_unlock(&clp->async_lock);
return copy;
}
@@ -2277,8 +2350,11 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp,
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
return manage_cpntf_state(nn, &os->stateid, clp, NULL);
- } else
+ } else {
nfsd4_stop_copy(copy);
+ /* find_async_copy() unlinked it from the reaper; drop the membership ref. */
+ nfs4_put_copy(copy);
+ }
return nfs_ok;
}
@@ -2292,7 +2368,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 = NULL;
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,
@@ -2306,12 +2381,14 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
cn->cpn_lease_time.tv_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.
@@ -2320,10 +2397,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;
@@ -3119,9 +3197,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;
}
@@ -3185,6 +3276,9 @@ encode_op:
status = op->status;
}
+ if (op->opdesc && op->opdesc->op_release)
+ op->opdesc->op_release(&op->u);
+
trace_nfsd_compound_status(args->client_opcnt, resp->opcnt,
status, nfsd4_op_name(op->opnum));
@@ -3507,7 +3601,7 @@ static u32 nfsd4_get_dir_delegation_rsize(const struct svc_rqst *rqstp,
op_encode_stateid_maxsz +
2 /* gddr_notification */ +
2 /* gddr_child_attributes */ +
- 2 /* gddr_dir_attributes */);
+ 2 /* gddr_dir_attributes */) * sizeof(__be32);
}
#ifdef CONFIG_NFSD_PNFS
@@ -3684,6 +3778,7 @@ static const struct nfsd4_operation nfsd4_ops[] = {
},
[OP_OPEN] = {
.op_func = nfsd4_open,
+ .op_release = nfsd4_open_release,
.op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
.op_name = "OP_OPEN",
.op_rsize_bop = nfsd4_open_rsize,
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 6ea25a52d2f4..d513971fb119 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -167,7 +167,7 @@ out_end:
end_creating(dentry);
out:
if (status == 0) {
- if (nn->in_grace)
+ if (test_bit(NFSD_NET_IN_GRACE, &nn->flags))
__nfsd4_create_reclaim_record_grace(clp, dname, nn);
vfs_fsync(nn->rec_file, 0);
} else {
@@ -285,10 +285,12 @@ __nfsd4_remove_reclaim_record_grace(const char *dname, int len,
return;
}
name.len = len;
+ down_write(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(name, nn);
- kfree(name.data);
if (crp)
nfs4_remove_reclaim_record(crp, nn);
+ up_write(&nn->reclaim_str_hashtbl_lock);
+ kfree(name.data);
}
static void
@@ -317,7 +319,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
nfs4_reset_creds(original_cred);
if (status == 0) {
vfs_fsync(nn->rec_file, 0);
- if (nn->in_grace)
+ if (test_bit(NFSD_NET_IN_GRACE, &nn->flags))
__nfsd4_remove_reclaim_record_grace(dname,
HEXDIR_LEN, nn);
}
@@ -373,7 +375,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn)
{
int status;
- nn->in_grace = false;
+ clear_bit(NFSD_NET_IN_GRACE, &nn->flags);
if (!nn->rec_file)
return;
status = mnt_want_write_file(nn->rec_file);
@@ -455,7 +457,7 @@ nfsd4_init_recdir(struct net *net)
nfs4_reset_creds(original_cred);
if (!status)
- nn->in_grace = true;
+ set_bit(NFSD_NET_IN_GRACE, &nn->flags);
return status;
}
@@ -484,6 +486,7 @@ nfs4_legacy_state_init(struct net *net)
for (i = 0; i < CLIENT_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
nn->reclaim_str_hashtbl_size = 0;
+ init_rwsem(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -598,13 +601,16 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
goto out_enoent;
}
name.len = HEXDIR_LEN;
+ down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(name, nn);
- kfree(name.data);
if (crp) {
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
crp->cr_clp = clp;
- return 0;
}
+ up_read(&nn->reclaim_str_hashtbl_lock);
+ kfree(name.data);
+ if (crp)
+ return 0;
out_enoent:
return -ENOENT;
@@ -1176,6 +1182,7 @@ nfsd4_cld_check(struct nfs4_client *clp)
return 0;
/* look for it in the reclaim hashtable otherwise */
+ down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
if (crp)
goto found;
@@ -1191,6 +1198,7 @@ nfsd4_cld_check(struct nfs4_client *clp)
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__);
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
name.len = HEXDIR_LEN;
@@ -1201,9 +1209,11 @@ nfsd4_cld_check(struct nfs4_client *clp)
}
#endif
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
found:
crp->cr_clp = clp;
+ up_read(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -1215,6 +1225,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
struct cld_net *cn = nn->cld_net;
#endif
struct nfs4_client_reclaim *crp;
+ unsigned int princhashlen;
char *principal = NULL;
/* did we already find that this client is stable? */
@@ -1222,6 +1233,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
return 0;
/* look for it in the reclaim hashtable otherwise */
+ down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
if (crp)
goto found;
@@ -1237,6 +1249,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data\n",
__func__);
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
}
name.len = HEXDIR_LEN;
@@ -1247,23 +1260,31 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
}
#endif
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
found:
- if (crp->cr_princhash.len) {
+ princhashlen = crp->cr_princhash.len;
+ if (princhashlen) {
u8 digest[SHA256_DIGEST_SIZE];
+ u8 *pdata;
if (clp->cl_cred.cr_raw_principal)
principal = clp->cl_cred.cr_raw_principal;
else if (clp->cl_cred.cr_principal)
principal = clp->cl_cred.cr_principal;
- if (principal == NULL)
+ if (principal == NULL) {
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
+ }
sha256(principal, strlen(principal), digest);
- if (memcmp(crp->cr_princhash.data, digest,
- crp->cr_princhash.len))
+ pdata = crp->cr_princhash.data;
+ if (memcmp(pdata, digest, princhashlen)) {
+ up_read(&nn->reclaim_str_hashtbl_lock);
return -ENOENT;
+ }
}
crp->cr_clp = clp;
+ up_read(&nn->reclaim_str_hashtbl_lock);
return 0;
}
@@ -1362,7 +1383,8 @@ nfs4_cld_state_init(struct net *net)
for (i = 0; i < CLIENT_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
nn->reclaim_str_hashtbl_size = 0;
- nn->track_reclaim_completes = true;
+ init_rwsem(&nn->reclaim_str_hashtbl_lock);
+ set_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags);
atomic_set(&nn->nr_reclaim_complete, 0);
return 0;
@@ -1373,7 +1395,7 @@ nfs4_cld_state_shutdown(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nn->track_reclaim_completes = false;
+ clear_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags);
kfree(nn->reclaim_str_hashtbl);
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a42f34842d77..386ca748522b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -939,7 +939,7 @@ out_free:
* 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;
@@ -949,19 +949,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, &copy->cp_stateid, NFS4_COPY_STID);
+ return nfs4_init_cp_state(nn, &copy->cp_stateid, NFS4_COPY_STID, NULL);
}
struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
@@ -972,13 +987,21 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
cps = kzalloc_obj(struct nfs4_cpntf_state);
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);
@@ -998,18 +1021,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);
}
@@ -1889,9 +1960,19 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
struct nfs4_client *clp;
retry:
list_for_each_entry(clp, head, cl_idhash) {
- struct nfs4_stid *stid = find_one_sb_stid(clp, sb,
- sc_types);
+ struct nfs4_stid *stid;
+
+ /*
+ * force_expire_client() ignores cl_rpc_users once
+ * its wait_event() has passed, so pinning cannot
+ * keep an already-expiring client alive; the
+ * expiry path revokes its states instead.
+ */
+ if (is_client_expired(clp))
+ continue;
+ stid = find_one_sb_stid(clp, sb, sc_types);
if (stid) {
+ atomic_inc(&clp->cl_rpc_users);
spin_unlock(&nn->client_lock);
revoke_one_stid(nn, clp, stid);
nfs4_put_stid(stid);
@@ -1904,6 +1985,9 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
*/
nn->nfs40_last_revoke =
ktime_get_boottime_seconds();
+ if (atomic_dec_and_test(&clp->cl_rpc_users) &&
+ is_client_expired(clp))
+ wake_up_all(&expiry_wq);
goto retry;
}
}
@@ -2295,7 +2379,7 @@ static void __free_session(struct nfsd4_session *ses)
{
free_session_slots(ses, 0);
xa_destroy(&ses->se_slots);
- kfree(ses);
+ kfree_rcu(ses, rcu_head);
}
static void free_session(struct nfsd4_session *ses)
@@ -2641,14 +2725,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);
@@ -2675,16 +2769,23 @@ static void inc_reclaim_complete(struct nfs4_client *clp)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
- if (!nn->track_reclaim_completes)
+ if (!test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags))
return;
- if (!nfsd4_find_reclaim_client(clp->cl_name, nn))
+
+ down_read(&nn->reclaim_str_hashtbl_lock);
+ if (!nfsd4_find_reclaim_client(clp->cl_name, nn)) {
+ up_read(&nn->reclaim_str_hashtbl_lock);
return;
+ }
if (atomic_inc_return(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_size) {
+ up_read(&nn->reclaim_str_hashtbl_lock);
printk(KERN_INFO "NFSD: all clients done reclaiming, ending NFSv4 grace period (net %x)\n",
clp->net->ns.inum);
nfsd4_end_grace(nn);
+ return;
}
+ up_read(&nn->reclaim_str_hashtbl_lock);
}
static void expire_client(struct nfs4_client *clp)
@@ -3259,6 +3360,11 @@ static void force_expire_client(struct nfs4_client *clp)
trace_nfsd_clid_admin_expired(&clp->cl_clientid);
+ /*
+ * cl_time is cleared under client_lock before the wait so a
+ * revocation walk pinning cl_rpc_users under it either skips
+ * this client or is seen by this wait_event().
+ */
spin_lock(&nn->client_lock);
clp->cl_time = 0;
spin_unlock(&nn->client_lock);
@@ -3414,7 +3520,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
clp->cl_time = ktime_get_boottime_seconds();
copy_verf(clp, verf);
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
- clp->cl_cb_session = NULL;
+ RCU_INIT_POINTER(clp->cl_cb_session, NULL);
clp->net = net;
clp->cl_nfsd_dentry = nfsd_client_mkdir(
nn, &clp->cl_nfsdfs,
@@ -4496,6 +4602,19 @@ static void nfsd4_construct_sequence_response(struct nfsd4_session *session,
seq->status_flags |= SEQ4_STATUS_ADMIN_STATE_REVOKED;
}
+static bool nfsd4_slots_inuse(struct nfsd4_session *ses, int from)
+{
+ int i;
+
+ for (i = from; i < ses->se_fchannel.maxreqs; i++) {
+ struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
+
+ if (slot->sl_flags & NFSD4_SLOT_INUSE)
+ return true;
+ }
+ return false;
+}
+
__be32
nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
@@ -4575,7 +4694,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (session->se_target_maxslots < session->se_fchannel.maxreqs &&
slot->sl_generation == session->se_slot_gen &&
- seq->maxslots <= session->se_target_maxslots)
+ seq->maxslots <= session->se_target_maxslots &&
+ seq->slotid < session->se_target_maxslots &&
+ !nfsd4_slots_inuse(session, session->se_target_maxslots))
/* Client acknowledged our reduce maxreqs */
free_session_slots(session, session->se_target_maxslots);
@@ -5018,8 +5139,6 @@ nfsd4_init_leases_net(struct nfsd_net *nn)
nn->nfsd4_lease = 90; /* default lease time */
nn->nfsd4_grace = 90;
- nn->somebody_reclaimed = false;
- nn->track_reclaim_completes = false;
nn->clverifier_counter = get_random_u32();
nn->clientid_base = get_random_u32();
nn->clientid_counter = nn->clientid_base + 1;
@@ -5172,6 +5291,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s)
case SC_TYPE_DELEG:
dp = delegstateid(s);
list_del_init(&dp->dl_recall_lru);
+ s->sc_status |= SC_STATUS_FREED;
spin_unlock(&cl->cl_lock);
nfs4_put_stid(s);
break;
@@ -5615,8 +5735,10 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
refcount_inc(&dp->dl_stid.sc_count);
queued = nfsd4_run_cb(&dp->dl_recall);
WARN_ON_ONCE(!queued);
- if (!queued)
+ if (!queued) {
refcount_dec(&dp->dl_stid.sc_count);
+ clear_bit(NFSD4_CALLBACK_RUNNING, &dp->dl_recall.cb_flags);
+ }
}
/* Called from break_lease() with flc_lock held. */
@@ -6728,12 +6850,21 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
static void
nfsd4_end_grace(struct nfsd_net *nn)
{
- /* do nothing if grace period already ended */
- if (nn->grace_ended)
+ /*
+ * nfsd4_end_grace() can be entered concurrently from the
+ * laundromat workqueue and from an nfsd compound thread
+ * handling RECLAIM_COMPLETE. Without serialization, both
+ * callers can observe NFSD_NET_GRACE_ENDED clear and proceed
+ * into nfsd4_record_grace_done(). For tracking ops whose
+ * grace_done drains reclaim_str_hashtbl, that results in
+ * list corruption and a double free of every
+ * nfs4_client_reclaim entry. Use an atomic test-and-set so
+ * exactly one caller proceeds.
+ */
+ if (test_and_set_bit(NFSD_NET_GRACE_ENDED, &nn->flags))
return;
trace_nfsd_grace_complete(nn);
- nn->grace_ended = true;
/*
* If the server goes down again right now, an NFSv4
* client will still be allowed to reclaim after it comes back up,
@@ -6774,10 +6905,10 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn)
{
if (!nn->client_tracking_ops)
return false;
- if (READ_ONCE(nn->grace_ended))
+ if (test_bit(NFSD_NET_GRACE_ENDED, &nn->flags))
return false;
/* laundromat_work must be initialised now, though it might be disabled */
- WRITE_ONCE(nn->grace_end_forced, true);
+ set_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags);
/* mod_delayed_work() doesn't queue work after
* nfs4_state_shutdown_net() has called disable_delayed_work_sync()
*/
@@ -6791,18 +6922,22 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn)
*/
static bool clients_still_reclaiming(struct nfsd_net *nn)
{
- time64_t double_grace_period_end = nn->boot_time +
+ time64_t double_grace_period_end = nn->boot_time_bt +
2 * nn->nfsd4_lease;
- if (READ_ONCE(nn->grace_end_forced))
+ if (test_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags))
return false;
- if (nn->track_reclaim_completes &&
- atomic_read(&nn->nr_reclaim_complete) ==
- nn->reclaim_str_hashtbl_size)
- return false;
- if (!nn->somebody_reclaimed)
+ if (test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags)) {
+ int size;
+
+ down_read(&nn->reclaim_str_hashtbl_lock);
+ size = nn->reclaim_str_hashtbl_size;
+ up_read(&nn->reclaim_str_hashtbl_lock);
+ if (atomic_read(&nn->nr_reclaim_complete) == size)
+ return false;
+ }
+ if (!test_and_clear_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags))
return false;
- nn->somebody_reclaimed = false;
/*
* If we've given them *two* lease times to reclaim, and they're
* still not done, give up:
@@ -6859,30 +6994,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);
@@ -7032,16 +7173,22 @@ retry:
if (atomic_read(&clp->cl_admin_revoked) == 0)
continue;
+ if (is_client_expired(clp))
+ continue;
spin_lock(&clp->cl_lock);
idr_for_each_entry_ul(&clp->cl_stateids, stid, tmp, id)
if (stid->sc_status & SC_STATUS_ADMIN_REVOKED) {
refcount_inc(&stid->sc_count);
+ atomic_inc(&clp->cl_rpc_users);
spin_unlock(&nn->client_lock);
/* this function drops ->cl_lock */
nfsd4_drop_revoked_stid(stid);
nfs4_put_stid(stid);
spin_lock(&nn->client_lock);
+ if (atomic_dec_and_test(&clp->cl_rpc_users) &&
+ is_client_expired(clp))
+ wake_up_all(&expiry_wq);
goto retry;
}
spin_unlock(&clp->cl_lock);
@@ -7062,6 +7209,7 @@ nfs4_laundromat(struct nfsd_net *nn)
.new_timeo = nn->nfsd4_lease
};
struct nfs4_cpntf_state *cps;
+ struct nfs4_client *clp;
copy_stateid_t *cps_t;
int i;
@@ -7076,7 +7224,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(&lt, cps->cpntf_time))
- _free_cpntf_state_locked(nn, cps);
+ revoke_cpntf_state_locked(nn, cps);
}
spin_unlock(&nn->s2s_cp_lock);
nfsd4_async_copy_reaper(nn);
@@ -7090,6 +7238,18 @@ nfs4_laundromat(struct nfsd_net *nn)
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (!state_expired(&lt, dp->dl_time))
break;
+ clp = dp->dl_stid.sc_client;
+ spin_lock(&nn->client_lock);
+ if (is_client_expired(clp)) {
+ spin_unlock(&nn->client_lock);
+ continue;
+ }
+ /*
+ * Pin without reviving: get_client_locked() would
+ * flip a courtesy client back to NFSD4_ACTIVE.
+ */
+ atomic_inc(&clp->cl_rpc_users);
+ spin_unlock(&nn->client_lock);
refcount_inc(&dp->dl_stid.sc_count);
unhash_delegation_locked(dp, SC_STATUS_REVOKED);
list_add(&dp->dl_recall_lru, &reaplist);
@@ -7098,8 +7258,18 @@ nfs4_laundromat(struct nfsd_net *nn)
while (!list_empty(&reaplist)) {
dp = list_first_entry(&reaplist, struct nfs4_delegation,
dl_recall_lru);
+ clp = dp->dl_stid.sc_client;
list_del_init(&dp->dl_recall_lru);
revoke_delegation(dp);
+ /*
+ * Unpin without renewing: put_client_renew() would
+ * renew the reaped client's lease.
+ */
+ if (atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock)) {
+ if (is_client_expired(clp))
+ wake_up_all(&expiry_wq);
+ spin_unlock(&nn->client_lock);
+ }
}
spin_lock(&nn->client_lock);
@@ -7195,12 +7365,12 @@ deleg_reaper(struct nfsd_net *nn)
continue;
if (atomic_read(&clp->cl_delegs_in_recall))
continue;
- if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags))
- continue;
if (ktime_get_boottime_seconds() - clp->cl_ra_time < 5)
continue;
if (clp->cl_cb_state != NFSD4_CB_UP)
continue;
+ if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags))
+ continue;
/* release in nfsd4_cb_recall_any_release */
kref_get(&clp->cl_nfsdfs.cl_ref);
@@ -7463,16 +7633,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
@@ -7497,10 +7665,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);
@@ -7787,7 +7965,7 @@ retry:
return status;
stp = openlockstateid(s);
if (nfsd4_cstate_assign_replay(cstate, stp->st_stateowner) == -EAGAIN) {
- nfs4_put_stateowner(stp->st_stateowner);
+ nfs4_put_stid(&stp->st_stid);
goto retry;
}
@@ -8036,6 +8214,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);
destroy_delegation(dp);
smp_mb__after_atomic();
@@ -8506,6 +8688,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)
flags |= FL_RECLAIM;
@@ -8542,10 +8727,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}
- if (lock->lk_type & (NFS4_READW_LT | NFS4_WRITEW_LT) &&
- nfsd4_has_session(cstate) &&
- locks_can_async_lock(nf->nf_file->f_op))
- flags |= FL_SLEEP;
+ if ((lock->lk_type == NFS4_READW_LT ||
+ lock->lk_type == NFS4_WRITEW_LT) &&
+ nfsd4_has_session(cstate) &&
+ locks_can_async_lock(nf->nf_file->f_op))
+ flags |= FL_SLEEP;
nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
if (!nbl) {
@@ -8587,7 +8773,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
status = 0;
if (lock->lk_reclaim)
- nn->somebody_reclaimed = true;
+ set_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags);
break;
case FILE_LOCK_DEFERRED:
kref_put(&nbl->nbl_kref, free_nbl);
@@ -8963,9 +9149,13 @@ bool
nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn)
{
struct nfs4_client_reclaim *crp;
+ bool found;
+ down_read(&nn->reclaim_str_hashtbl_lock);
crp = nfsd4_find_reclaim_client(name, nn);
- return (crp && crp->cr_clp);
+ found = (crp && crp->cr_clp);
+ up_read(&nn->reclaim_str_hashtbl_lock);
+ return found;
}
/*
@@ -8978,10 +9168,39 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
unsigned int strhashval;
struct nfs4_client_reclaim *crp;
+ down_write(&nn->reclaim_str_hashtbl_lock);
+
+ /*
+ * A reclaim record for this client name may already exist (for
+ * example, populated at boot from the recovery directory before
+ * an in-grace RECLAIM_COMPLETE or an nfsdcld downcall delivers
+ * the same name). Dedup here so reclaim_str_hashtbl_size stays
+ * equal to the number of distinct client names; inc_reclaim_complete
+ * relies on that equality to end the grace period via the fast path.
+ */
+ crp = nfsd4_find_reclaim_client(name, nn);
+ if (crp) {
+ if (princhash.len && crp->cr_princhash.len == 0) {
+ void *pdata = kmemdup(princhash.data, princhash.len,
+ GFP_KERNEL);
+ if (pdata) {
+ crp->cr_princhash.data = pdata;
+ crp->cr_princhash.len = princhash.len;
+ } else {
+ dprintk("%s: failed to allocate memory for princhash.data!\n",
+ __func__);
+ crp = NULL;
+ }
+ }
+ up_write(&nn->reclaim_str_hashtbl_lock);
+ return crp;
+ }
+
name.data = kmemdup(name.data, name.len, GFP_KERNEL);
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__);
+ up_write(&nn->reclaim_str_hashtbl_lock);
return NULL;
}
if (princhash.len) {
@@ -8990,6 +9209,7 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
dprintk("%s: failed to allocate memory for princhash.data!\n",
__func__);
kfree(name.data);
+ up_write(&nn->reclaim_str_hashtbl_lock);
return NULL;
}
} else
@@ -9009,6 +9229,7 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
kfree(name.data);
kfree(princhash.data);
}
+ up_write(&nn->reclaim_str_hashtbl_lock);
return crp;
}
@@ -9028,6 +9249,7 @@ nfs4_release_reclaim(struct nfsd_net *nn)
struct nfs4_client_reclaim *crp = NULL;
int i;
+ down_write(&nn->reclaim_str_hashtbl_lock);
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
while (!list_empty(&nn->reclaim_str_hashtbl[i])) {
crp = list_entry(nn->reclaim_str_hashtbl[i].next,
@@ -9036,6 +9258,7 @@ nfs4_release_reclaim(struct nfsd_net *nn)
}
}
WARN_ON_ONCE(nn->reclaim_str_hashtbl_size);
+ up_write(&nn->reclaim_str_hashtbl_lock);
}
/*
@@ -9113,8 +9336,9 @@ static int nfs4_state_create_net(struct net *net)
nn->conf_name_tree = RB_ROOT;
nn->unconf_name_tree = RB_ROOT;
nn->boot_time = ktime_get_real_seconds();
- nn->grace_ended = false;
- nn->grace_end_forced = false;
+ nn->boot_time_bt = ktime_get_boottime_seconds();
+ clear_bit(NFSD_NET_GRACE_ENDED, &nn->flags);
+ clear_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags);
nn->nfsd4_manager.block_opens = true;
INIT_LIST_HEAD(&nn->nfsd4_manager.list);
INIT_LIST_HEAD(&nn->client_lru);
@@ -9200,7 +9424,8 @@ nfs4_state_start_net(struct net *net)
nfsd4_client_tracking_init(net);
/* safe for laundromat to run now */
enable_delayed_work(&nn->laundromat_work);
- if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
+ if (test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags) &&
+ nn->reclaim_str_hashtbl_size == 0)
goto skip_grace;
printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n",
nn->nfsd4_grace, net->ns.inum);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index e17488a911f7..6a0a97d39bf6 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -449,9 +449,18 @@ nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
return nfserr_bad_xdr;
+ /*
+ * The NFSv4 POSIX ACL draft doesn't define a max number of ACE's, but
+ * the NFSACL spec does. For NFSv4, cap the number of entries to the v3
+ * limit, as we want to ensure that ACLs set via NFSv4 POSIX ACL
+ * extensions are retrievable via NFSACL.
+ */
+ if (count > NFS_ACL_MAX_ENTRIES)
+ return nfserr_inval;
+
*acl = posix_acl_alloc(count, GFP_KERNEL);
if (*acl == NULL)
- return nfserr_resource;
+ return nfserr_jukebox;
(*acl)->a_count = count;
for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) {
@@ -628,6 +637,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access))
return nfserr_bad_xdr;
+ if (access.nseconds >= NSEC_PER_SEC)
+ return nfserr_inval;
iattr->ia_atime.tv_sec = access.seconds;
iattr->ia_atime.tv_nsec = access.nseconds;
iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG;
@@ -637,6 +648,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify))
return nfserr_bad_xdr;
+ if (modify.nseconds >= NSEC_PER_SEC)
+ return nfserr_inval;
iattr->ia_mtime.tv_sec = modify.seconds;
iattr->ia_mtime.tv_nsec = modify.nseconds;
iattr->ia_ctime.tv_sec = modify.seconds;
@@ -955,6 +968,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;
@@ -2106,6 +2123,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;
@@ -2135,6 +2153,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;
}
@@ -6390,9 +6420,6 @@ status:
write_bytes_to_xdr_buf(xdr->buf, op_status_offset,
&op->status, XDR_UNIT);
release:
- if (opdesc && opdesc->op_release)
- opdesc->op_release(&op->u);
-
/*
* Account for pages consumed while encoding this operation.
* The xdr_stream primitives don't manage rq_next_page.
@@ -6424,9 +6451,12 @@ void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
{
struct nfsd4_compoundargs *args = rqstp->rq_argp;
+ args->opcnt = 0;
if (args->ops != args->iops) {
- vfree(args->ops);
+ void *old_ops = args->ops;
+
args->ops = args->iops;
+ kvfree_rcu_mightsleep(old_ops);
}
while (args->to_free) {
struct svcxdr_tmpbuf *tb = args->to_free;
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index 154468ceccdc..a9fa9dd410b2 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -200,14 +200,14 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
nn->nfsd_reply_cache_shrinker->seeks = 1;
nn->nfsd_reply_cache_shrinker->private_data = nn;
- shrinker_register(nn->nfsd_reply_cache_shrinker);
-
for (i = 0; i < hashsize; i++) {
INIT_LIST_HEAD(&nn->drc_hashtbl[i].lru_head);
spin_lock_init(&nn->drc_hashtbl[i].cache_lock);
}
nn->drc_hashsize = hashsize;
+ shrinker_register(nn->nfsd_reply_cache_shrinker);
+
return 0;
out_shrinker:
kvfree(nn->drc_hashtbl);
@@ -275,7 +275,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/nfsctl.c b/fs/nfsd/nfsctl.c
index fa92e31d19d6..caf59421f8f4 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -296,14 +296,15 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
* 2. Is that directory a mount point, or
* 3. Is that directory the root of an exported file system?
*/
- nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
nn = net_generic(netns(file), nfsd_net_id);
- if (nn->nfsd_serv)
+ if (nn->nfsd_serv) {
+ nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
nfsd4_revoke_states(nn, path.dentry->d_sb);
- else
+ } else {
error = -EINVAL;
+ }
mutex_unlock(&nfsd_mutex);
path_put(&path);
@@ -420,6 +421,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
char *mesg = buf;
int rv;
struct net *net = netns(file);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (size > 0) {
int newthreads;
@@ -430,7 +432,10 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads);
mutex_lock(&nfsd_mutex);
- rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
+ if (newthreads > 0 || nn->nfsd_serv != NULL)
+ rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
+ else
+ rv = 0;
mutex_unlock(&nfsd_mutex);
if (rv < 0)
return rv;
@@ -1111,7 +1116,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
}
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
- nn->grace_ended ? 'Y' : 'N');
+ test_bit(NFSD_NET_GRACE_ENDED, &nn->flags) ? 'Y' : 'N');
}
#endif
@@ -1414,8 +1419,8 @@ static int create_proc_exports_entry(void)
unsigned int nfsd_net_id;
struct nfsd_genl_rqstp {
- struct sockaddr rq_daddr;
- struct sockaddr rq_saddr;
+ struct sockaddr_storage rq_daddr;
+ struct sockaddr_storage rq_saddr;
unsigned long rq_flags;
ktime_t rq_stime;
__be32 rq_xid;
@@ -1448,9 +1453,9 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME,
ktime_to_us(genl_rqstp->rq_stime),
NFSD_A_RPC_STATUS_PAD))
- return -ENOBUFS;
+ goto out_cancel;
- switch (genl_rqstp->rq_saddr.sa_family) {
+ switch (genl_rqstp->rq_saddr.ss_family) {
case AF_INET: {
const struct sockaddr_in *s_in, *d_in;
@@ -1464,7 +1469,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
s_in->sin_port) ||
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
d_in->sin_port))
- return -ENOBUFS;
+ goto out_cancel;
break;
}
case AF_INET6: {
@@ -1480,7 +1485,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
s_in->sin6_port) ||
nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT,
d_in->sin6_port))
- return -ENOBUFS;
+ goto out_cancel;
break;
}
}
@@ -1488,10 +1493,14 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb,
for (i = 0; i < genl_rqstp->rq_opcnt; i++)
if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS,
genl_rqstp->rq_opnum[i]))
- return -ENOBUFS;
+ goto out_cancel;
genlmsg_end(skb, hdr);
return 0;
+
+out_cancel:
+ genlmsg_cancel(skb, hdr);
+ return -ENOBUFS;
}
/**
@@ -1519,18 +1528,28 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) {
struct svc_rqst *rqstp;
+ long thread_skip = 0;
if (i < cb->args[0]) /* already consumed */
continue;
+ /*
+ * The saved thread index only applies to the pool the dump
+ * was resumed in. Subsequent pools must start from thread 0,
+ * otherwise their first cb->args[1] threads are silently
+ * skipped.
+ */
+ if (i == cb->args[0])
+ thread_skip = cb->args[1];
+
rqstp_index = 0;
list_for_each_entry_rcu(rqstp,
&nn->nfsd_serv->sv_pools[i].sp_all_threads,
rq_all) {
- struct nfsd_genl_rqstp genl_rqstp;
+ struct nfsd_genl_rqstp genl_rqstp = {};
unsigned int status_counter;
- if (rqstp_index++ < cb->args[1]) /* already consumed */
+ if (rqstp_index++ < thread_skip) /* already consumed */
continue;
/*
* Acquire rq_status_counter before parsing the rqst
@@ -1551,9 +1570,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
genl_rqstp.rq_stime = rqstp->rq_stime;
genl_rqstp.rq_opcnt = 0;
memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp),
- sizeof(struct sockaddr));
+ sizeof(struct sockaddr_storage));
memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp),
- sizeof(struct sockaddr));
+ sizeof(struct sockaddr_storage));
#ifdef CONFIG_NFSD_V4
if (rqstp->rq_vers == NFS4_VERSION &&
@@ -1572,17 +1591,26 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
#endif /* CONFIG_NFSD_V4 */
/*
- * Acquire rq_status_counter before reporting the rqst
- * fields to the user.
+ * Read-side load-load fence: order the field reads
+ * above before the counter re-read below, mirroring
+ * the smp_rmb() in the standard seqcount retry. The
+ * begin-side smp_load_acquire() above pairs with the
+ * smp_store_release() in nfsd_dispatch().
*/
- if (smp_load_acquire(&rqstp->rq_status_counter) !=
- status_counter)
+ smp_rmb();
+ if (READ_ONCE(rqstp->rq_status_counter) != status_counter)
continue;
ret = nfsd_genl_rpc_status_compose_msg(skb, cb,
&genl_rqstp);
- if (ret)
+ if (ret) {
+ if (skb->len) {
+ cb->args[0] = i;
+ cb->args[1] = rqstp_index - 1;
+ ret = skb->len;
+ }
goto out;
+ }
}
}
@@ -1944,6 +1972,60 @@ err_free_msg:
}
/**
+ * nfsd_nl_validate_listeners - sanity-check the listener list from userland
+ * @info: netlink metadata and command arguments
+ *
+ * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry
+ * is well-formed: it parses against the policy, carries both an address and
+ * a transport name, and the address is long enough for its family. Doing
+ * this up front lets the callers below assume every entry is valid and
+ * guarantees we make no changes when the request is malformed.
+ *
+ * Return: 0 if every entry is valid, or a negative errno otherwise.
+ */
+static int nfsd_nl_validate_listeners(struct genl_info *info)
+{
+ const struct nlattr *attr;
+ int rem;
+
+ nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
+ GENL_HDRLEN, rem) {
+ struct nlattr *tb[NFSD_A_SOCK_MAX + 1];
+ struct sockaddr *sa;
+ int err;
+
+ err = nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
+ nfsd_sock_nl_policy, info->extack);
+ if (err < 0)
+ return err;
+
+ if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
+ return -EINVAL;
+
+ sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
+ if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family))
+ return -EINVAL;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (nla_len(tb[NFSD_A_SOCK_ADDR]) <
+ sizeof(struct sockaddr_in))
+ return -EINVAL;
+ break;
+ case AF_INET6:
+ if (nla_len(tb[NFSD_A_SOCK_ADDR]) <
+ sizeof(struct sockaddr_in6))
+ return -EINVAL;
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+ }
+
+ return 0;
+}
+
+/**
* nfsd_nl_listener_set_doit - set the nfs running sockets
* @skb: reply buffer
* @info: netlink metadata and command arguments
@@ -1961,6 +2043,15 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
bool delete = false;
int err, rem;
+ /*
+ * Validate the entire listener list before making any changes, so a
+ * malformed request fails cleanly without creating a serv or touching
+ * the existing listeners.
+ */
+ err = nfsd_nl_validate_listeners(info);
+ if (err)
+ return err;
+
mutex_lock(&nfsd_mutex);
err = nfsd_create_serv(net);
@@ -1987,16 +2078,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
const char *xcl_name;
struct sockaddr *sa;
+ /* validated up front in nfsd_nl_validate_listeners() */
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0)
continue;
- if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
- continue;
-
- if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
- continue;
-
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
@@ -2048,16 +2134,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
struct sockaddr *sa;
int ret;
+ /* validated up front in nfsd_nl_validate_listeners() */
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0)
continue;
- if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
- continue;
-
- if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
- continue;
-
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
@@ -2512,11 +2593,12 @@ static int __init init_nfsd(void)
{
int retval;
- nfsd_debugfs_init();
-
retval = nfsd4_init_slabs();
if (retval)
return retval;
+
+ nfsd_debugfs_init();
+
retval = nfsd4_init_pnfs();
if (retval)
goto out_free_slabs;
@@ -2561,8 +2643,8 @@ out_free_lockd:
out_free_pnfs:
nfsd4_exit_pnfs();
out_free_slabs:
- nfsd4_free_slabs();
nfsd_debugfs_exit();
+ nfsd4_free_slabs();
return retval;
}
@@ -2577,9 +2659,9 @@ static void __exit exit_nfsd(void)
unregister_pernet_subsys(&nfsd_net_ops);
nfsd_drc_slab_free();
nfsd_lockd_shutdown();
- nfsd4_free_slabs();
nfsd4_exit_pnfs();
nfsd_debugfs_exit();
+ nfsd4_free_slabs();
}
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 429ca5c6ec08..ab53de1c280d 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -70,10 +70,8 @@ nfsd_mode_check(struct dentry *dentry, umode_t requested)
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;
}
if (mode == S_IFLNK) {
@@ -344,15 +342,19 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
fhp->fh_no_wcc = true;
fhp->fh_64bit_cookies = true;
- if (exp->ex_flags & NFSEXP_V4ROOT)
+ if (exp->ex_flags & NFSEXP_V4ROOT) {
+ dput(dentry);
goto out;
+ }
break;
case NFS_FHSIZE:
fhp->fh_no_wcc = true;
if (EX_WGATHER(exp))
fhp->fh_use_wgather = true;
- if (exp->ex_flags & NFSEXP_V4ROOT)
+ if (exp->ex_flags & NFSEXP_V4ROOT) {
+ dput(dentry);
goto out;
+ }
}
fhp->fh_dentry = dentry;
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 8873033d1e82..a73d5c259cd9 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -82,6 +82,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),
@@ -117,6 +118,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 4f1ab3222a4d..5e0a0c1cbb91 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -351,7 +351,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int ret;
- if (nn->nfsd_net_up)
+ if (test_bit(NFSD_NET_UP, &nn->flags))
return 0;
ret = nfsd_startup_generic();
@@ -364,11 +364,11 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
goto out_socks;
}
- if (nfsd_needs_lockd(nn) && !nn->lockd_up) {
+ if (nfsd_needs_lockd(nn) && !test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
ret = lockd_up(net, cred);
if (ret)
goto out_socks;
- nn->lockd_up = true;
+ set_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
ret = nfsd_file_cache_start_net(net);
@@ -386,7 +386,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
if (ret)
goto out_reply_cache;
- nn->nfsd_net_up = true;
+ set_bit(NFSD_NET_UP, &nn->flags);
return 0;
out_reply_cache:
@@ -394,9 +394,9 @@ out_reply_cache:
out_filecache:
nfsd_file_cache_shutdown_net(net);
out_lockd:
- if (nn->lockd_up) {
+ if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
lockd_down(net);
- nn->lockd_up = false;
+ clear_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
out_socks:
nfsd_shutdown_generic();
@@ -407,7 +407,7 @@ static void nfsd_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- if (nn->nfsd_net_up) {
+ if (test_bit(NFSD_NET_UP, &nn->flags)) {
percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done);
wait_for_completion(&nn->nfsd_net_confirm_done);
@@ -415,18 +415,18 @@ static void nfsd_shutdown_net(struct net *net)
nfs4_state_shutdown_net(net);
nfsd_reply_cache_shutdown(nn);
nfsd_file_cache_shutdown_net(net);
- if (nn->lockd_up) {
+ if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) {
lockd_down(net);
- nn->lockd_up = false;
+ clear_bit(NFSD_NET_LOCKD_UP, &nn->flags);
}
wait_for_completion(&nn->nfsd_net_free_done);
}
percpu_ref_exit(&nn->nfsd_net_ref);
- if (nn->nfsd_net_up)
+ if (test_bit(NFSD_NET_UP, &nn->flags))
nfsd_shutdown_generic();
- nn->nfsd_net_up = false;
+ clear_bit(NFSD_NET_UP, &nn->flags);
}
static DEFINE_SPINLOCK(nfsd_notifier_lock);
@@ -815,7 +815,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;
@@ -825,7 +825,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;
@@ -960,6 +960,20 @@ nfsd(void *vrqstp)
return 0;
}
+/*
+ * Set rq_status_counter back to an even value, indicating that the rqstp
+ * fields are no longer meaningful to a lockless reader. This pairs with the
+ * odd-valued store made once the request has been decoded, and must run on
+ * every return path that follows it so that the seq-lock like protocol used
+ * by nfsd_nl_rpc_status_get_dumpit() is not left permanently odd. The store
+ * also advances the counter so a concurrent reader detects the transition.
+ */
+static void nfsd_status_counter_set_idle(struct svc_rqst *rqstp)
+{
+ smp_store_release(&rqstp->rq_status_counter,
+ (rqstp->rq_status_counter | 1) + 1);
+}
+
/**
* nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request
* @rqstp: incoming request
@@ -1022,14 +1036,9 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
goto out_encode_err;
- /*
- * Release rq_status_counter setting it to an even value after the rpc
- * request has been properly processed.
- */
- smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
-
nfsd_cache_update(rqstp, rp, ntli->ntli_cachetype, nfs_reply);
out_cached_reply:
+ nfsd_status_counter_set_idle(rqstp);
return 1;
out_decode_err:
@@ -1040,12 +1049,14 @@ out_decode_err:
out_update_drop:
nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
out_dropit:
+ nfsd_status_counter_set_idle(rqstp);
return 0;
out_encode_err:
trace_nfsd_cant_encode_err(rqstp);
nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
*statp = rpc_system_err;
+ nfsd_status_counter_set_idle(rqstp);
return 1;
}
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index ae71e0621317..86c9dd4b334b 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -10,6 +10,16 @@
#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
*/
static const u32 nfs_ftypes[] = {
@@ -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/state.h b/fs/nfsd/state.h
index dec83e92650d..f44ea672670f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -384,6 +384,7 @@ struct nfsd4_session {
u16 se_slot_gen;
bool se_dead;
u32 se_target_maxslots;
+ struct rcu_head rcu_head;
};
/* formatted contents of nfs4_sessionid */
@@ -496,7 +497,7 @@ struct nfs4_client {
#define NFSD4_CB_FAULT 3
int cl_cb_state;
struct nfsd4_callback cl_cb_null;
- struct nfsd4_session *cl_cb_session;
+ struct nfsd4_session __rcu *cl_cb_session;
/* for all client information that callback code might need: */
spinlock_t cl_lock;
@@ -754,6 +755,7 @@ struct nfs4_layout_stateid {
struct delayed_work ls_fence_work;
unsigned int ls_fence_delay;
bool ls_fenced;
+ bool ls_fence_inflight;
};
static inline struct nfs4_layout_stateid *layoutstateid(struct nfs4_stid *s)
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 1c5a1e50f946..33953d38314e 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -271,7 +271,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify,
TP_CONDITION(rqstp != NULL),
TP_STRUCT__entry(
__field(unsigned int, netns_ino)
- __sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
+ __sockaddr(server, rqstp->rq_xprt->xpt_locallen)
__sockaddr(client, rqstp->rq_xprt->xpt_remotelen)
__field(u32, xid)
__field(u32, fh_hash)
@@ -310,7 +310,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify_err,
TP_CONDITION(rqstp != NULL && error),
TP_STRUCT__entry(
__field(unsigned int, netns_ino)
- __sockaddr(server, rqstp->rq_xprt->xpt_remotelen)
+ __sockaddr(server, rqstp->rq_xprt->xpt_locallen)
__sockaddr(client, rqstp->rq_xprt->xpt_remotelen)
__field(u32, xid)
__field(u32, fh_hash)
@@ -1727,9 +1727,10 @@ DEFINE_NFSD_CB_LIFETIME_EVENT(bc_shutdown);
TRACE_EVENT(nfsd_cb_seq_status,
TP_PROTO(
const struct rpc_task *task,
- const struct nfsd4_callback *cb
+ const struct nfsd4_callback *cb,
+ const struct nfsd4_session *session
),
- TP_ARGS(task, cb),
+ TP_ARGS(task, cb, session),
TP_STRUCT__entry(
__field(unsigned int, task_id)
__field(unsigned int, client_id)
@@ -1741,8 +1742,6 @@ TRACE_EVENT(nfsd_cb_seq_status,
__field(int, seq_status)
),
TP_fast_assign(
- const struct nfs4_client *clp = cb->cb_clp;
- const struct nfsd4_session *session = clp->cl_cb_session;
const struct nfsd4_sessionid *sid =
(struct nfsd4_sessionid *)&session->se_sessionid;
@@ -1768,9 +1767,10 @@ TRACE_EVENT(nfsd_cb_seq_status,
TRACE_EVENT(nfsd_cb_free_slot,
TP_PROTO(
const struct rpc_task *task,
- const struct nfsd4_callback *cb
+ const struct nfsd4_callback *cb,
+ const struct nfsd4_session *session
),
- TP_ARGS(task, cb),
+ TP_ARGS(task, cb, session),
TP_STRUCT__entry(
__field(unsigned int, task_id)
__field(unsigned int, client_id)
@@ -1781,8 +1781,6 @@ TRACE_EVENT(nfsd_cb_free_slot,
__field(u32, slot_seqno)
),
TP_fast_assign(
- const struct nfs4_client *clp = cb->cb_clp;
- const struct nfsd4_session *session = clp->cl_cb_session;
const struct nfsd4_sessionid *sid =
(struct nfsd4_sessionid *)&session->se_sessionid;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 1e89c7ff9493..f73012bc742f 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -137,8 +137,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 */
@@ -419,21 +421,22 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
}
static __be32
-nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
- struct iattr *iap)
+nfsd_may_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct iattr *iap)
{
struct inode *inode = d_inode(fhp->fh_dentry);
- if (iap->ia_size < inode->i_size) {
- __be32 err;
+ if (iap->ia_size >= i_size_read(inode))
+ return nfs_ok;
- err = nfsd_permission(&rqstp->rq_cred,
- fhp->fh_export, fhp->fh_dentry,
- NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
- if (err)
- return err;
- }
- return nfserrno(get_write_access(inode));
+ return nfsd_permission(&rqstp->rq_cred, fhp->fh_export, fhp->fh_dentry,
+ NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
+}
+
+static __be32
+nfsd_get_write_access(struct svc_fh *fhp)
+{
+ return nfserrno(get_write_access(d_inode(fhp->fh_dentry)));
}
static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
@@ -560,12 +563,17 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
* setattr call.
*/
if (size_change) {
- err = nfsd_get_write_access(rqstp, fhp, iap);
+ err = nfsd_get_write_access(fhp);
if (err)
return err;
}
inode_lock(inode);
+ if (size_change) {
+ err = nfsd_may_truncate(rqstp, fhp, iap);
+ if (err)
+ goto out_unlock;
+ }
err = fh_fill_pre_attrs(fhp);
if (err)
goto out_unlock;
@@ -1374,6 +1382,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file = nf->nf_file;
unsigned int nsegs, i;
ssize_t host_err;
+ size_t expected;
nsegs = nfsd_write_dio_iters_init(nf, rqstp->rq_bvec, nvecs,
kiocb, *cnt, segments);
@@ -1395,11 +1404,13 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
kiocb->ki_flags |= IOCB_DONTCACHE;
}
+ expected = iov_iter_count(&segments[i].iter);
+
host_err = vfs_iocb_iter_write(file, kiocb, &segments[i].iter);
if (host_err < 0)
return host_err;
*cnt += host_err;
- if (host_err < segments[i].iter.count)
+ if (host_err < (ssize_t)expected)
break; /* partial write */
}
diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c
index 5f0f1f283af0..83f6ea30cc8b 100644
--- a/fs/nilfs2/bmap.c
+++ b/fs/nilfs2/bmap.c
@@ -175,7 +175,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 4656df392722..a72f3c308a5d 100644
--- a/fs/nilfs2/bmap.h
+++ b/fs/nilfs2/bmap.h
@@ -63,7 +63,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/btree.c b/fs/nilfs2/btree.c
index 64d5f7c5ab44..64bac66af25b 100644
--- a/fs/nilfs2/btree.c
+++ b/fs/nilfs2/btree.c
@@ -1425,6 +1425,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)
@@ -1435,7 +1457,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;
@@ -1522,15 +1544,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;
}
}
@@ -1581,7 +1605,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;
@@ -1601,7 +1625,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 8bd0b1374e25..b8643d3aa2f8 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/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index a3555bebad63..b05b6d3abb87 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -600,8 +600,7 @@ static struct fanotify_event *fanotify_alloc_perm_event(const void *data,
pevent->hdr.len = 0;
pevent->state = FAN_EVENT_INIT;
pevent->path = *path;
- /* NULL ppos means no range info */
- pevent->ppos = range ? &range->pos : NULL;
+ pevent->pos = range ? range->pos : FANOTIFY_NO_RANGE;
pevent->count = range ? range->count : 0;
path_get(path);
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index a0619e7694d5..3710543dbf82 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -428,6 +428,8 @@ FANOTIFY_ME(struct fanotify_event *event)
return container_of(event, struct fanotify_mnt_event, fae);
}
+#define FANOTIFY_NO_RANGE ((loff_t)-1)
+
/*
* Structure for permission fanotify events. It gets allocated and freed in
* fanotify_handle_event() since we wait there for user response. When the
@@ -438,7 +440,7 @@ FANOTIFY_ME(struct fanotify_event *event)
struct fanotify_perm_event {
struct fanotify_event fae;
struct path path;
- const loff_t *ppos; /* optional file range info */
+ loff_t pos; /* FANOTIFY_NO_RANGE if unavailable */
size_t count;
u32 response; /* userspace answer to the event */
unsigned short state; /* state of the event */
@@ -468,7 +470,7 @@ static inline bool fanotify_event_has_access_range(struct fanotify_event *event)
if (!(event->mask & FANOTIFY_PRE_CONTENT_EVENTS))
return false;
- return FANOTIFY_PERM(event)->ppos;
+ return FANOTIFY_PERM(event)->pos != FANOTIFY_NO_RANGE;
}
static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index b604e3da58ad..7278286f5a6d 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -675,12 +675,9 @@ static size_t copy_range_info_to_user(struct fanotify_event *event,
if (WARN_ON_ONCE(info_len > count))
return -EFAULT;
- if (WARN_ON_ONCE(!pevent->ppos))
- return -EINVAL;
-
info.hdr.info_type = FAN_EVENT_INFO_TYPE_RANGE;
info.hdr.len = info_len;
- info.offset = *(pevent->ppos);
+ info.offset = pevent->pos;
info.count = pevent->count;
if (copy_to_user(buf, &info, info_len))
@@ -1316,16 +1313,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 ed37491c1618..5f19c24ec187 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -539,7 +539,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;
@@ -555,27 +554,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 & ~READ_ONCE(inode->i_fsnotify_mask));
-
- /* update the inode with this new fsn_mark */
- if (dropped || do_inode)
- fsnotify_recalc_mask(fsn_mark->connector);
-
- }
+ fsnotify_recalc_mask(fsn_mark->connector);
/* return the wd */
ret = i_mark->wd;
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 58f32aac5f61..0c147b99d4ee 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -693,6 +693,8 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
u32 attr_len;
u32 min_len;
u16 mp_offset;
+ u16 name_offset;
+ u32 name_end;
attr_len = le32_to_cpu(a->length);
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
@@ -701,7 +703,27 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
return false;
mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
- return mp_offset >= min_len && mp_offset <= attr_len;
+ if (mp_offset < min_len || mp_offset > attr_len)
+ return false;
+
+ if (a->name_length) {
+ name_offset = le16_to_cpu(a->name_offset);
+
+ if (name_offset < min_len || name_offset >= attr_len)
+ return false;
+
+ name_end = name_offset + a->name_length * sizeof(__le16);
+ if (name_end > attr_len || name_end > mp_offset)
+ return false;
+ }
+
+ /* Ensure there's room for the compressed_size field if needed. */
+ if (!(a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) &&
+ attr_len - mp_offset <
+ sizeof(a->data.non_resident.compressed_size))
+ return false;
+
+ return true;
}
static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol,
diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c
index 835a041023a2..aa2e017a4384 100644
--- a/fs/ntfs/lcnalloc.c
+++ b/fs/ntfs/lcnalloc.c
@@ -298,7 +298,12 @@ struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 st
clusters = count;
rlpos = rlsize = 0;
mapping = lcnbmp_vi->i_mapping;
- i_size = i_size_read(lcnbmp_vi);
+ /*
+ * lcn_empty_bits_per_page is sized from nr_clusters, but $Bitmap can
+ * cover more clusters than that; bound the scan by the array.
+ */
+ i_size = min_t(s64, i_size_read(lcnbmp_vi),
+ ((s64)vol->nr_clusters + 7) >> 3);
while (1) {
ntfs_debug("Start of outer while loop: done_zones 0x%x, search_zone %i, pass %i, zone_start 0x%llx, zone_end 0x%llx, bmp_initial_pos 0x%llx, bmp_pos 0x%llx, rlpos %i, rlsize %i.",
done_zones, search_zone, pass,
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
index 8e0fd400e7f7..00373e450ea7 100644
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -772,6 +772,25 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
return ERR_PTR(-EIO);
}
+ /*
+ * An empty mapping-pairs array is valid only for a zero-length
+ * attribute.
+ */
+ if (!*buf &&
+ (vcn ||
+ le64_to_cpu(attr->data.non_resident.highest_vcn) !=
+ (u64)(vcn - 1) ||
+ le64_to_cpu(attr->data.non_resident.allocated_size) ||
+ le64_to_cpu(attr->data.non_resident.data_size) ||
+ le64_to_cpu(attr->data.non_resident.initialized_size))) {
+ ntfs_error(vol->sb, "Invalid empty mapping pairs array.");
+ return ERR_PTR(-EIO);
+ }
+ if (!vcn && !*buf && old_runlist && old_runlist->rl) {
+ *new_rl_count = old_runlist->count;
+ return old_runlist->rl;
+ }
+
/* Current position in runlist array. */
rlpos = 0;
/* Allocate first page and set current runlist size to one page. */
@@ -884,12 +903,41 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
ntfs_error(vol->sb, "lcn == -1");
}
#endif
+ /* Check lcn is within the volume. */
+ if (unlikely(lcn >= (s64)vol->nr_clusters)) {
+ ntfs_error(vol->sb,
+ "LCN >= nr_clusters in mapping pairs array.");
+ goto err_out;
+ }
+
/* Check lcn is not below -1. */
if (unlikely(lcn < -1)) {
ntfs_error(vol->sb, "Invalid s64 < -1 in mapping pairs array.");
goto err_out;
}
+ if (lcn >= 0) {
+ s64 run_end;
+
+ /*
+ * Ensure that the run stays within the volume.
+ * A valid starting LCN is not sufficient because
+ * the run length comes from disk.
+ */
+ if (unlikely(check_add_overflow(lcn,
+ rl[rlpos].length,
+ &run_end))) {
+ ntfs_error(vol->sb,
+ "Run length overflow in mapping pairs array.");
+ goto err_out;
+ }
+ if (unlikely(run_end > (s64)vol->nr_clusters)) {
+ ntfs_error(vol->sb,
+ "Run extends beyond volume boundary.");
+ goto err_out;
+ }
+ }
+
/* chkdsk accepts zero-sized runs only for holes */
if ((lcn != -1) && !rl[rlpos].length) {
ntfs_error(vol->sb,
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 8abe7bee4c0d..cd8fa2c13370 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -645,7 +645,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
{
unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sects;
int clusters_per_mft_record, clusters_per_index_record;
- s64 ll;
+ u64 ll;
vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
vol->sector_size_bits = ffs(vol->sector_size) - 1;
@@ -755,23 +755,23 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
* the same as it is much faster on 32-bit CPUs.
*/
ll = le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
- if ((u64)ll >= 1ULL << 32) {
+ if (ll >= 1ULL << 32) {
ntfs_error(vol->sb, "Cannot handle 64-bit clusters.");
return false;
}
vol->nr_clusters = ll;
ntfs_debug("vol->nr_clusters = 0x%llx", vol->nr_clusters);
ll = le64_to_cpu(b->mft_lcn);
- if (ll >= vol->nr_clusters) {
- ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of volume. Weird.",
+ if (ll >= (u64)vol->nr_clusters) {
+ ntfs_error(vol->sb, "MFT LCN (%llu, 0x%llx) is beyond end of volume. Weird.",
ll, ll);
return false;
}
vol->mft_lcn = ll;
ntfs_debug("vol->mft_lcn = 0x%llx", vol->mft_lcn);
ll = le64_to_cpu(b->mftmirr_lcn);
- if (ll >= vol->nr_clusters) {
- ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end of volume. Weird.",
+ if (ll >= (u64)vol->nr_clusters) {
+ ntfs_error(vol->sb, "MFTMirr LCN (%llu, 0x%llx) is beyond end of volume. Weird.",
ll, ll);
return false;
}
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 2b49bc077558..a751661cdca9 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -2443,6 +2443,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;
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index f038c799e7ac..c759841b7430 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -648,6 +648,14 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c)
}
/*
+ * 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.
*/
static inline struct DIR_PAGE_ENTRY *find_dp(struct RESTART_TABLE *dptbl,
@@ -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.
*/
@@ -4295,6 +4317,11 @@ check_dirty_page_table:
goto out;
}
+ if (!check_dp_table(rt)) {
+ err = -EINVAL;
+ goto out;
+ }
+
dptbl = kmemdup(rt, t32, GFP_NOFS);
if (!dptbl) {
err = -ENOMEM;
@@ -5087,6 +5114,13 @@ find_dirty_page:
/* 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/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c
index d12784aaaa4b..ea68dc0887f6 100644
--- a/fs/ocfs2/cluster/heartbeat.c
+++ b/fs/ocfs2/cluster/heartbeat.c
@@ -43,6 +43,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);
@@ -138,7 +146,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.
@@ -2142,6 +2150,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)
@@ -2149,10 +2158,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,
@@ -2290,48 +2300,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(&reg->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(&reg->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(&reg->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(&reg->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;
}
@@ -2376,12 +2451,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;
}
/*
@@ -2393,16 +2469,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 */
@@ -2421,6 +2504,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,
diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c
index 402563154550..21154e4baddf 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -777,6 +777,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/dir.c b/fs/ocfs2/dir.c
index d7fc3cccf2f4..5ee894966b12 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -625,6 +625,28 @@ static int ocfs2_validate_dx_root(struct super_block *sb,
le16_to_cpu(el->l_count));
goto bail;
}
+ } else {
+ struct ocfs2_dx_entry_list *dl_list = &dx_root->dr_entries;
+
+ if (le16_to_cpu(dl_list->de_count) !=
+ ocfs2_dx_entries_per_root(sb)) {
+ ret = ocfs2_error(sb,
+ "Dir Index Root # %llu has invalid de_count %u (expected %u)\n",
+ (unsigned long long)le64_to_cpu(dx_root->dr_blkno),
+ le16_to_cpu(dl_list->de_count),
+ ocfs2_dx_entries_per_root(sb));
+ goto bail;
+ }
+
+ if (le16_to_cpu(dl_list->de_num_used) >
+ le16_to_cpu(dl_list->de_count)) {
+ ret = ocfs2_error(sb,
+ "Dir Index Root # %llu has invalid de_num_used %u (de_count %u)\n",
+ (unsigned long long)le64_to_cpu(dx_root->dr_blkno),
+ le16_to_cpu(dl_list->de_num_used),
+ le16_to_cpu(dl_list->de_count));
+ goto bail;
+ }
}
bail:
@@ -664,10 +686,25 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb,
return ret;
}
- if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) {
- ret = ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n",
- 7, dx_leaf->dl_signature);
- }
+ if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf))
+ return ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n",
+ 7, dx_leaf->dl_signature);
+
+ if (le16_to_cpu(dx_leaf->dl_list.de_count) !=
+ ocfs2_dx_entries_per_leaf(sb))
+ return ocfs2_error(sb,
+ "Dir Index Leaf # %llu has invalid de_count %u (expected %u)\n",
+ (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno),
+ le16_to_cpu(dx_leaf->dl_list.de_count),
+ ocfs2_dx_entries_per_leaf(sb));
+
+ if (le16_to_cpu(dx_leaf->dl_list.de_num_used) >
+ le16_to_cpu(dx_leaf->dl_list.de_count))
+ return ocfs2_error(sb,
+ "Dir Index Leaf # %llu has invalid de_num_used %u (de_count %u)\n",
+ (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno),
+ le16_to_cpu(dx_leaf->dl_list.de_num_used),
+ le16_to_cpu(dx_leaf->dl_list.de_count));
return ret;
}
@@ -1917,7 +1954,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 aee3b4c56dcc..612969867ff9 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -3099,6 +3099,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 9b97bf73df22..9d4a2695b959 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1357,6 +1357,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/refcounttree.c b/fs/ocfs2/refcounttree.c
index 7323bde70caa..d9f22b4a2654 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;
}
@@ -3360,10 +3387,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;
}
@@ -3846,10 +3872,9 @@ unlock:
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
@@ -4135,10 +4160,9 @@ out_unlock_refcount:
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;
}
@@ -4691,10 +4715,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/suballoc.c b/fs/ocfs2/suballoc.c
index a4a2b87a45fe..20c3aec6b987 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -2759,7 +2759,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle,
fe->i_clusters = cpu_to_le32(tmp_used - le16_to_cpu(cl->cl_cpg));
spin_lock(&OCFS2_I(alloc_inode)->ip_lock);
- OCFS2_I(alloc_inode)->ip_clusters -= le32_to_cpu(fe->i_clusters);
+ OCFS2_I(alloc_inode)->ip_clusters = le32_to_cpu(fe->i_clusters);
fe->i_size = cpu_to_le64(ocfs2_clusters_to_bytes(alloc_inode->i_sb,
le32_to_cpu(fe->i_clusters)));
spin_unlock(&OCFS2_I(alloc_inode)->ip_lock);
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 5989351aff93..f1b3aaab6a71 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -7241,10 +7241,9 @@ out_unlock:
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;
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 69bd73a2b556..9f94919a6bc6 100644
--- a/fs/orangefs/orangefs-debugfs.c
+++ b/fs/orangefs/orangefs-debugfs.c
@@ -569,6 +569,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/overlayfs/dir.c b/fs/overlayfs/dir.c
index a033743dbf51..a8d47b1e644b 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -188,7 +188,6 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
if (!err && ofs->casefold != ovl_dentry_casefolded(newdentry)) {
pr_warn_ratelimited("wrong inherited casefold (%pd2)\n",
newdentry);
- end_creating(newdentry);
err = -EINVAL;
}
break;
diff --git a/fs/pidfs.c b/fs/pidfs.c
index b57ecc96e967..a6a643f15d08 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -532,6 +532,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct task_struct *task __free(put_task) = NULL;
struct nsproxy *nsp __free(put_nsproxy) = NULL;
struct ns_common *ns_common = NULL;
+ int error;
if (!pidfs_ioctl_valid(cmd))
return -ENOIOCTLCMD;
@@ -555,20 +556,33 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (arg)
return -EINVAL;
+ /*
+ * We're trying to open a file descriptor to the namespace so perform a
+ * filesystem cred ptrace check. Hold @task's exec_update_lock for the
+ * duration of the ptrace check and the namespace lookup so that the
+ * credentials used for the access decision match those of @task at the
+ * time its namespace is read, preventing a concurrent execve() from
+ * swapping the task's credentials in between the check and the use. We
+ * mirror nsfs behavior.
+ */
+ error = down_read_killable(&task->signal->exec_update_lock);
+ if (error)
+ return error;
+
+ if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
+ error = -EACCES;
+ goto out_unlock;
+ }
+
scoped_guard(task_lock, task) {
nsp = task->nsproxy;
if (nsp)
get_nsproxy(nsp);
}
- if (!nsp)
- return -ESRCH; /* just pretend it didn't exist */
-
- /*
- * We're trying to open a file descriptor to the namespace so perform a
- * filesystem cred ptrace check. Also, we mirror nsfs behavior.
- */
- if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
- return -EACCES;
+ if (!nsp) {
+ error = -ESRCH; /* just pretend it didn't exist */
+ goto out_unlock;
+ }
switch (cmd) {
/* Namespaces that hang of nsproxy. */
@@ -650,11 +664,16 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#endif
break;
default:
- return -ENOIOCTLCMD;
+ error = -ENOIOCTLCMD;
}
- if (!ns_common)
- return -EOPNOTSUPP;
+ if (!error && !ns_common)
+ error = -EOPNOTSUPP;
+
+out_unlock:
+ up_read(&task->signal->exec_update_lock);
+ if (error)
+ return error;
/* open_namespace() unconditionally consumes the reference */
return open_namespace(ns_common);
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index a1dacc7d8f74..cb3a397b109c 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1413,8 +1413,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/cifsfs.h b/fs/smb/client/cifsfs.h
index 854e672a4e37..651670c19c2b 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -147,6 +147,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
void cifs_setsize(struct inode *inode, loff_t offset);
+void cifs_resize_file_locked(struct inode *inode, loff_t offset);
struct fs_context;
struct smb3_fs_context;
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 1f77512252e7..f5aad5f61dce 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response);
bytes_left = get_bcc(smb_buffer_response);
+ if (bytes_left < 2) {
+ rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small,
+ bytes_left, 2);
+ goto out;
+ }
length = strnlen(bcc_ptr, bytes_left - 2);
if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
@@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
reset_cifs_unix_caps(xid, tcon, NULL, NULL);
}
}
+out:
cifs_buf_release(smb_buffer);
return rc;
}
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 8cd93cd2f00f..86dba25b7a5a 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)
@@ -869,13 +871,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:
@@ -915,10 +926,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);
@@ -959,7 +977,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/file.c b/fs/smb/client/file.c
index ac89c1ba56b1..389083f9ce00 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -1016,6 +1016,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
+ cifs_invalidate_cache(inode, 0);
}
}
if (cfile)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 0afff761aab9..aec8ed8881a6 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3057,9 +3057,27 @@ void cifs_setsize(struct inode *inode, loff_t offset)
inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ if (offset > old_size)
+ pagecache_isize_extended(inode, old_size, offset);
truncate_pagecache(inode, offset);
netfs_wait_for_outstanding_io(inode);
- fscache_resize_cookie(cifs_inode_cookie(inode), offset);
+}
+
+void cifs_resize_file_locked(struct inode *inode, loff_t offset)
+{
+ struct fscache_cookie *cookie = cifs_inode_cookie(inode);
+
+ lockdep_assert_held_write(&inode->i_rwsem);
+
+ netfs_resize_file(netfs_inode(inode), offset, true);
+ cifs_setsize(inode, offset);
+
+ if (!cookie)
+ return;
+
+ fscache_use_cookie(cookie, true);
+ fscache_resize_cookie(cookie, offset);
+ cifs_fscache_unuse_inode_cookie(inode, true);
}
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
@@ -3100,6 +3118,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
size, false);
cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
cifsFileInfo_put(open_file);
+ tcon = NULL;
}
}
@@ -3125,10 +3144,8 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
cifs_put_tlink(tlink);
set_size_out:
- if (rc == 0) {
- netfs_resize_file(&cifsInode->netfs, size, true);
- cifs_setsize(inode, size);
- }
+ if (rc == 0)
+ cifs_resize_file_locked(inode, size);
return rc;
}
@@ -3214,9 +3231,13 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
}
- /* skip mode change if it's just for clearing setuid/setgid */
- if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
- attrs->ia_valid &= ~ATTR_MODE;
+ /*
+ * This function is only called when Unix extensions are in effect,
+ * so the mode is always sent to and stored on the server. Do not
+ * skip the mode change when clearing setuid/setgid bits: dropping
+ * ATTR_MODE here would leave those bits set on the server after a
+ * write, which is a security issue.
+ */
args = kmalloc_obj(*args);
if (args == NULL) {
@@ -3425,8 +3446,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
}
- /* skip mode change if it's just for clearing setuid/setgid */
- if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ /*
+ * Skip the mode change if it is only being done to clear the
+ * setuid/setgid bits *and* the mode is emulated via the DOS
+ * read-only attribute (the default, non-ACL case), which cannot
+ * represent the setuid/setgid bits anyway.
+ *
+ * When the mode is instead stored on the server - i.e. with the
+ * cifsacl or modefromsid mount options (via an ACL) or with the
+ * SMB3.1.1 POSIX extensions - the cleared mode must be pushed to
+ * the server. Dropping ATTR_MODE here would leave the setuid/
+ * setgid bit set on the server after a write, which is a security
+ * issue (the bits are not stripped as they are on local
+ * filesystems).
+ */
+ if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
+ !((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions))
attrs->ia_valid &= ~ATTR_MODE;
if (attrs->ia_valid & ATTR_MODE) {
diff --git a/fs/smb/client/smb1misc.c b/fs/smb/client/smb1misc.c
index ba56023010d8..cdfbbff24b72 100644
--- a/fs/smb/client/smb1misc.c
+++ b/fs/smb/client/smb1misc.c
@@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
(struct smb_com_transaction_change_notify_rsp *)buf;
struct file_notify_information *pnotify;
__u32 data_offset = 0;
- size_t len = srv->total_read - srv->pdu_size;
+ /* total_read excludes the RFC1002 preamble */
+ size_t len = srv->total_read;
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index dc5a8c1da623..954c1a8ef724 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -717,7 +717,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/smb1transport.c b/fs/smb/client/smb1transport.c
index 966f2cf83a51..42e95cc1bd89 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -375,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
- /* validate target area */
data_area_of_src = (char *)&pSMBs->hdr.Protocol +
get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
data_area_of_tgt += total_in_tgt;
+ /*
+ * DataOffset fields are server-supplied and not validated against
+ * buffer bounds; check both data pointers before mutating the
+ * target header.
+ */
+ if (data_area_of_tgt < (char *)target_hdr +
+ sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+ data_area_of_tgt + total_in_src >
+ (char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__);
+ return -EPROTO;
+ }
+ if (data_area_of_src < second_buf +
+ sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+ data_area_of_src + total_in_src >
+ second_buf + smbCalcSize((struct smb_hdr *)second_buf)) {
+ cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__);
+ return -EPROTO;
+ }
+
total_in_tgt += total_in_src;
/* is the result too big for the field? */
if (total_in_tgt > USHRT_MAX) {
@@ -430,10 +449,18 @@ bool
cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
char *buf, int malformed)
{
- if (malformed)
- return false;
- if (check2ndT2(buf) <= 0)
+ if (malformed || check2ndT2(buf) <= 0) {
+ /* mid->multiRsp blocks the server buf detach in handle_mid();
+ * returning false here would leak resp_buf and leave a dangling
+ * server->smallbuf/bigbuf after the user thread frees resp_buf.
+ */
+ if (mid->multiRsp) {
+ mid->multiEnd = true;
+ dequeue_mid(server, mid, true);
+ return true;
+ }
return false;
+ }
mid->multiRsp = true;
if (mid->resp_buf) {
/* merge response - fix up 1st*/
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index f35b6488d810..fb2fccbe8667 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -61,7 +61,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/smb2inode.c b/fs/smb/client/smb2inode.c
index 213bc298cdf2..2946391bb992 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -40,9 +40,11 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
buf = (struct reparse_data_buffer *)((u8 *)io + off);
len = sizeof(*buf);
- rdlen = le16_to_cpu(buf->ReparseDataLength);
+ if (count < len)
+ return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0));
- if (count < len || count < rdlen + len)
+ rdlen = le16_to_cpu(buf->ReparseDataLength);
+ if (count < rdlen + len)
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
return buf;
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 192649fec25d..0e872d58fae7 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2222,8 +2222,7 @@ smb2_duplicate_extents(const unsigned int xid,
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
- netfs_resize_file(netfs_inode(inode), dest_off + len, true);
- cifs_setsize(inode, dest_off + len);
+ cifs_resize_file_locked(inode, dest_off + len);
}
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid,
@@ -3776,8 +3775,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
new_eof = off + len;
- netfs_resize_file(&cifsi->netfs, new_eof, true);
- cifs_setsize(inode, new_eof);
+ cifs_resize_file_locked(inode, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
@@ -3825,8 +3823,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc)
goto out;
- netfs_resize_file(&cifsi->netfs, new_eof, true);
- cifs_setsize(inode, new_eof);
+ cifs_resize_file_locked(inode, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 5b21ad3c15fb..12241abb8e2e 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -133,6 +133,7 @@
EM(smb_eio_trace_sym_slash, "sym_slash") \
EM(smb_eio_trace_sym_target_len, "sym_target_len") \
EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \
+ EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \
EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \
EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \
EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \
@@ -213,7 +214,7 @@
#define EM(a, b) a,
#define E_(a, b) a
-enum smb_eio_trace { smb_eio_traces } __mode(byte);
+enum smb_eio_trace { smb_eio_traces };
enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte);
enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index a141025581af..9bd8dce660af 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -1722,7 +1722,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
down_write(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
lockdep_is_held(&ci->m_lock)) {
- if (op->conn)
+ if (op->conn || op->o_fp != fp)
continue;
op->conn = ksmbd_conn_get(fp->conn);
op->sess = work->sess;
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index a52458435327..9ce620a31016 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -438,6 +438,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;
}
@@ -725,8 +727,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);
scoped_guard(mutex, &eventfs_mutex) {
if (!parent->is_freed)
@@ -798,9 +798,6 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
ei->attr.uid = uid;
ei->attr.gid = 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 a4a0158f712d..1f770795ee70 100644
--- a/fs/ubifs/auth.c
+++ b/fs/ubifs/auth.c
@@ -217,7 +217,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 67bcf83758c8..c519644acfd7 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -531,7 +531,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/include/linux/bio.h b/include/linux/bio.h
index 8f33f717b14f..ce34ea49ef35 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -477,7 +477,7 @@ int bdev_rw_virt(struct block_device *bdev, sector_t sector, void *data,
size_t len, enum req_op op);
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
- unsigned len_align_mask);
+ unsigned mem_align_mask, unsigned len_align_mask);
void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter);
void __bio_release_pages(struct bio *bio, bool mark_dirty);
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index f29ef0653546..66a2f70e9e01 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -58,6 +58,7 @@ enum compact_result {
};
struct alloc_context; /* in mm/internal.h */
+struct capture_control; /* in mm/internal.h */
/*
* Number of free order-0 pages that should be available above given watermark
@@ -92,7 +93,7 @@ extern int fragmentation_index(struct zone *zone, unsigned int order);
extern enum compact_result try_to_compact_pages(gfp_t gfp_mask,
unsigned int order, unsigned int alloc_flags,
const struct alloc_context *ac, enum compact_priority prio,
- struct page **page);
+ struct capture_control *capc);
extern void reset_isolation_suitable(pg_data_t *pgdat);
extern bool compaction_suitable(struct zone *zone, int order,
unsigned long watermark, int highest_zoneidx);
diff --git a/include/linux/dm-io.h b/include/linux/dm-io.h
index 7b2968612b7e..674683894064 100644
--- a/include/linux/dm-io.h
+++ b/include/linux/dm-io.h
@@ -27,7 +27,7 @@ struct page_list {
struct page *page;
};
-typedef void (*io_notify_fn)(unsigned int long error, void *context);
+typedef void (*io_notify_fn)(unsigned long int error, unsigned long int unsup, void *context);
enum dm_io_mem_type {
DM_IO_PAGE_LIST,/* Page list */
@@ -80,8 +80,8 @@ void dm_io_client_destroy(struct dm_io_client *client);
* error occurred doing io to the corresponding region.
*/
int dm_io(struct dm_io_request *io_req, unsigned int num_regions,
- struct dm_io_region *region, unsigned int long *sync_error_bits,
- unsigned short ioprio);
+ struct dm_io_region *region, unsigned long int *sync_error_bits,
+ unsigned long int *sync_unsup_bits, unsigned short ioprio);
#endif /* __KERNEL__ */
#endif /* _LINUX_DM_IO_H */
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index 416a3352261f..f7740c5f6967 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -96,6 +96,9 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
ret = arch_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/include/linux/font.h b/include/linux/font.h
index 6845f02d739a..877efe165d2a 100644
--- a/include/linux/font.h
+++ b/include/linux/font.h
@@ -49,6 +49,8 @@ static inline unsigned int font_glyph_pitch(unsigned int width)
* scanlines, which is usually the glyph's height in scanlines. Fonts
* coming from user space can sometimes have a different vertical pitch
* with empty scanlines between two adjacent glyphs.
+ *
+ * Returns: the number of bytes per glyph
*/
static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpitch)
{
@@ -60,7 +62,7 @@ static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpit
*/
/**
- * font_data_t - Raw font data
+ * typedef font_data_t - Raw font data
*
* Values of type font_data_t store a pointer to raw font data. The format
* is monochrome. Each bit sets a pixel of a stored glyph. Font data does
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2abaf99321e9..f883c03bd965 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -171,6 +171,7 @@ extern int movable_gigantic_pages __read_mostly;
extern int sysctl_hugetlb_shm_group __read_mostly;
extern struct list_head huge_boot_pages[MAX_NUMNODES];
+void hugetlb_bootmem_struct_page_init(void);
void hugetlb_bootmem_alloc(void);
extern nodemask_t hugetlb_bootmem_nodes;
void hugetlb_bootmem_set_nodes(void);
@@ -1261,6 +1262,8 @@ static inline void hugetlb_count_sub(long l, struct mm_struct *mm)
{
}
+pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
+
static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
@@ -1293,6 +1296,10 @@ static inline bool hugetlbfs_pagecache_present(
static inline void hugetlb_bootmem_alloc(void)
{
}
+
+static inline void hugetlb_bootmem_struct_page_init(void)
+{
+}
#endif /* CONFIG_HUGETLB_PAGE */
static inline spinlock_t *huge_pte_lock(struct hstate *h,
diff --git a/include/linux/list.h b/include/linux/list.h
index 09d979976b3b..59f8aa0905d3 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -150,10 +150,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;
@@ -171,8 +174,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/lockd/bind.h b/include/linux/lockd/bind.h
index b614e0deea72..db8207d4059f 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -16,17 +16,23 @@ struct svc_rqst;
struct rpc_task;
struct rpc_clnt;
struct super_block;
+struct module;
-/*
- * This is the set of functions for lockd->nfsd communication
+/**
+ * struct nlmsvc_binding - lockd -> nfsd callback table
+ * @owner: module that provides this binding.
+ * @fopen: open a file by NFS file handle on behalf of an NLM request.
+ * @fclose: close a file that was previously opened via @fopen.
+ * Implementations MUST be semantically equivalent to fput().
*/
struct nlmsvc_binding {
+ struct module *owner;
int (*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f,
struct file **filp, int flags);
void (*fclose)(struct file *filp);
};
-extern const struct nlmsvc_binding *nlmsvc_ops;
+extern const struct nlmsvc_binding __rcu *nlmsvc_ops;
/*
* Similar to nfs_client_initdata, but without the NFS-specific
diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h
index 98939cb664cf..59d0cc889beb 100644
--- a/include/linux/sunrpc/bc_xprt.h
+++ b/include/linux/sunrpc/bc_xprt.h
@@ -32,6 +32,7 @@ int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs);
void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs);
void xprt_free_bc_rqst(struct rpc_rqst *req);
unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt);
+void xprt_svc_shutdown_bc(struct rpc_xprt *xprt);
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv);
/*
@@ -71,6 +72,10 @@ static inline void xprt_free_bc_request(struct rpc_rqst *req)
{
}
+static inline void xprt_svc_shutdown_bc(struct rpc_xprt *xprt)
+{
+}
+
static inline void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
svc_destroy(serv);
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/uio.h b/include/linux/uio.h
index a9bc5b3067e3..fe2e985d74d2 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -389,9 +389,17 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages,
size_t maxsize, unsigned int maxpages,
iov_iter_extraction_t extraction_flags,
size_t *offset0);
+/*
+ * Block-layer consumers (e.g. bio_iov_iter_get_pages()) require that the
+ * segments of an ITER_BVEC iterator are already aligned to the target device's
+ * DMA alignment, and forward them as-is. In-kernel users that build their own
+ * bvecs must not create sub-aligned segments; iov_iter_extract_bvecs() enforces
+ * the same for the segments it extracts via @mem_align_mask.
+ */
ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
size_t max_size, unsigned short *nr_vecs,
- unsigned short max_vecs, iov_iter_extraction_t extraction_flags);
+ unsigned short max_vecs, unsigned mem_align_mask,
+ iov_iter_extraction_t extraction_flags);
/**
* iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained
diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h
index f7f5cfbdef12..9b46a6bc762c 100644
--- a/include/linux/usb/tcpci.h
+++ b/include/linux/usb/tcpci.h
@@ -144,6 +144,7 @@
#define TCPC_RX_BUF_FRAME_TYPE 0x31
#define TCPC_RX_BUF_FRAME_TYPE_SOP 0
#define TCPC_RX_BUF_FRAME_TYPE_SOP1 1
+#define TCPC_RX_BUF_FRAME_TYPE_MASK GENMASK(2, 0)
#define TCPC_RX_HDR 0x32
#define TCPC_RX_DATA 0x34 /* through 0x4f */
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index d87dc7f77f4e..a39c20c77efc 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -62,7 +62,7 @@ struct vm_struct {
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
unsigned int page_order;
#endif
- unsigned int nr_pages;
+ unsigned long nr_pages;
phys_addr_t phys_addr;
const void *caller;
unsigned long requested_size;
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index d708b66e55cd..85e3455cea25 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -629,8 +629,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().
@@ -640,6 +639,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/sctp/structs.h b/include/net/sctp/structs.h
index cccc662561aa..b21f23b736fd 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -2057,7 +2057,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/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 7ffe2ef127ee..9c1102ebf06e 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -351,6 +351,7 @@ struct landlock_net_port_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/io_uring/query.c b/io_uring/query.c
index 88a325736992..f2b5e19e5af6 100644
--- a/io_uring/query.c
+++ b/io_uring/query.c
@@ -76,6 +76,9 @@ static int io_handle_query_entry(union io_query_data *data, void __user *uhdr,
if (copy_from_user(&hdr, uhdr, sizeof(hdr)))
return -EFAULT;
+ /* copy_struct_to_user() zeros up to usize bytes */
+ if (hdr.size > PAGE_SIZE)
+ return -E2BIG;
usize = hdr.size;
hdr.size = min(hdr.size, IO_MAX_QUERY_SIZE);
udata = u64_to_user_ptr(hdr.query_data);
diff --git a/io_uring/waitid.c b/io_uring/waitid.c
index 76af129ba8ca..2b756017423f 100644
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -125,7 +125,7 @@ static void io_waitid_remove_wq(struct io_kiocb *req)
}
}
-static void io_waitid_complete(struct io_kiocb *req, int ret)
+static void io_waitid_complete(struct io_kiocb *req, int ret, bool copy_si)
{
struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid);
@@ -137,13 +137,16 @@ static void io_waitid_complete(struct io_kiocb *req, int ret)
hlist_del_init(&req->hash_node);
io_waitid_remove_wq(req);
- ret = io_waitid_finish(req, ret);
+ if (copy_si)
+ ret = io_waitid_finish(req, ret);
+ else
+ io_waitid_free(req);
if (ret < 0)
req_set_fail(req);
io_req_set_res(req, ret, 0);
}
-static bool __io_waitid_cancel(struct io_kiocb *req)
+static bool __io_waitid_cancel(struct io_kiocb *req, bool copy_si)
{
struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid);
@@ -159,21 +162,32 @@ static bool __io_waitid_cancel(struct io_kiocb *req)
if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
return false;
- io_waitid_complete(req, -ECANCELED);
+ io_waitid_complete(req, -ECANCELED, copy_si);
io_req_queue_tw_complete(req, -ECANCELED);
return true;
}
+static bool io_waitid_cancel_cb(struct io_kiocb *req)
+{
+ return __io_waitid_cancel(req, true);
+}
+
+static bool io_waitid_cancel_nocopy_cb(struct io_kiocb *req)
+{
+ return __io_waitid_cancel(req, false);
+}
+
int io_waitid_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd,
unsigned int issue_flags)
{
- return io_cancel_remove(ctx, cd, issue_flags, &ctx->waitid_list, __io_waitid_cancel);
+ return io_cancel_remove(ctx, cd, issue_flags, &ctx->waitid_list, io_waitid_cancel_cb);
}
bool io_waitid_remove_all(struct io_ring_ctx *ctx, struct io_uring_task *tctx,
bool cancel_all)
{
- return io_cancel_remove_all(ctx, tctx, &ctx->waitid_list, cancel_all, __io_waitid_cancel);
+ return io_cancel_remove_all(ctx, tctx, &ctx->waitid_list, cancel_all,
+ tctx ? io_waitid_cancel_cb : io_waitid_cancel_nocopy_cb);
}
static inline bool io_waitid_drop_issue_ref(struct io_kiocb *req)
@@ -202,6 +216,11 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw)
int ret;
io_tw_lock(ctx, tw);
+ if (unlikely(tw.cancel)) {
+ io_waitid_complete(req, -ECANCELED, false);
+ io_req_task_complete(tw_req, tw);
+ return;
+ }
ret = __do_wait(&iwa->wo);
@@ -229,7 +248,7 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw)
}
}
- io_waitid_complete(req, ret);
+ io_waitid_complete(req, ret, true);
io_req_task_complete(tw_req, tw);
}
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 7f791afe5791..f52645625214 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1023,7 +1023,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
@@ -1071,9 +1070,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;
}
@@ -1158,6 +1154,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 b73336c976b7..c6e7559b07de 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/stackmap.c b/kernel/bpf/stackmap.c
index 41fe87d7302f..443d2c3bd45d 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -718,6 +718,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
max_depth = stack_map_calculate_max_depth(size, elem_size, flags);
+ preempt_disable();
if (may_fault)
rcu_read_lock(); /* need RCU for perf's callchain below */
@@ -734,6 +735,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
if (unlikely(!trace) || trace->nr < skip) {
if (may_fault)
rcu_read_unlock();
+ preempt_enable();
goto err_fault;
}
@@ -754,6 +756,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
/* trace/ips should not be dereferenced after this point */
if (may_fault)
rcu_read_unlock();
+ preempt_enable();
if (user_build_id)
stack_map_get_build_id_offset(buf, trace_nr, user, may_fault);
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index f754079a287d..66093460584e 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -140,21 +140,22 @@ static bool numa_cma_configured __initdata;
static int __init early_numa_cma(char *p)
{
int nid, count = 0;
- unsigned long tmp;
+ unsigned long node;
+ phys_addr_t size;
char *s = p;
while (*s) {
- if (sscanf(s, "%lu%n", &tmp, &count) != 1)
+ if (sscanf(s, "%lu%n", &node, &count) != 1)
break;
if (s[count] == ':') {
- if (tmp >= MAX_NUMNODES)
+ if (node >= MAX_NUMNODES)
break;
- nid = array_index_nospec(tmp, MAX_NUMNODES);
+ nid = array_index_nospec(node, MAX_NUMNODES);
s += count + 1;
- tmp = memparse(s, &s);
- numa_cma_size[nid] = tmp;
+ size = memparse(s, &s);
+ numa_cma_size[nid] = size;
if (*s == ',')
s++;
@@ -207,9 +208,10 @@ static void __init dma_numa_cma_reserve(void)
pernuma_size_bytes = cma_get_size(dma_contiguous_default_area);
for_each_node(nid) {
- int size, ret;
+ phys_addr_t size;
char name[CMA_MAX_NAME];
struct cma **cma;
+ int ret;
if (!node_online(nid)) {
if (pernuma_size_bytes || numa_cma_size[nid])
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 175c08a6e41e..6fad9152387a 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -501,7 +501,7 @@ static int __init kho_preserved_memory_reserve(phys_addr_t phys,
struct page *page;
u64 sz;
- sz = 1 << (order + PAGE_SHIFT);
+ sz = 1UL << (order + PAGE_SHIFT);
page = kho_get_preserved_page(phys, order);
/* Reserve the memory preserved in KHO in memblock */
diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c
index 0fc11e45df9b..f23126d804b2 100644
--- a/kernel/module/kallsyms.c
+++ b/kernel/module/kallsyms.c
@@ -258,17 +258,25 @@ static const char *find_kallsyms_symbol(struct module *mod,
unsigned int i, best = 0;
unsigned long nextval, bestval;
struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
- struct module_memory *mod_mem;
+ struct module_memory *mod_mem = NULL;
- /* At worse, next value is at end of module */
- if (within_module_init(addr, mod))
- mod_mem = &mod->mem[MOD_INIT_TEXT];
- else
- mod_mem = &mod->mem[MOD_TEXT];
+ for_each_mod_mem_type(type) {
+#ifndef CONFIG_KALLSYMS_ALL
+ if (!mod_mem_type_is_text(type))
+ continue;
+#endif
+ if (within_module_mem_type(addr, mod, type)) {
+ mod_mem = &mod->mem[type];
+ break;
+ }
+ }
- nextval = (unsigned long)mod_mem->base + mod_mem->size;
+ if (!mod_mem)
+ return NULL;
- bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);
+ /* Initialize bounds within memory region the address belongs to. */
+ nextval = (unsigned long)mod_mem->base + mod_mem->size;
+ bestval = (unsigned long)mod_mem->base - 1;
/*
* Scan for closest preceding symbol, and next symbol. (ELF
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..7cbc8f0e28c6 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2011,6 +2011,7 @@ static int elf_validity_cache_sechdrs(struct load_info *info)
* Specifically checks:
*
* * Section name table index is inbounds of section headers
+ * * Section name table type is SHT_STRTAB
* * Section name table is not empty
* * Section name table is NUL terminated
* * All section name offsets are inbounds of the section
@@ -2038,6 +2039,11 @@ static int elf_validity_cache_secstrings(struct load_info *info)
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
+ if (strhdr->sh_type != SHT_STRTAB) {
+ pr_err("Invalid ELF section name table type: %u\n", strhdr->sh_type);
+ return -ENOEXEC;
+ }
+
/*
* The section name table must be NUL-terminated, as required
* by the spec. This makes strcmp and pr_* calls that access
@@ -2204,7 +2210,7 @@ static int elf_validity_cache_index_sym(struct load_info *info)
* Must have &load_info->index.sym populated.
*
* Looks at the symbol table's associated string table, makes sure it is
- * in-bounds, and caches it.
+ * in-bounds and of type SHT_STRTAB, and caches it.
*
* Return: %0 if valid, %-ENOEXEC on failure.
*/
@@ -2218,6 +2224,12 @@ static int elf_validity_cache_index_str(struct load_info *info)
return -ENOEXEC;
}
+ if (info->sechdrs[str_idx].sh_type != SHT_STRTAB) {
+ pr_err("Invalid ELF symbol string table type: %u\n",
+ info->sechdrs[str_idx].sh_type);
+ return -ENOEXEC;
+ }
+
info->index.str = str_idx;
return 0;
}
diff --git a/kernel/params.c b/kernel/params.c
index a668863a4bb6..939ef84a46ff 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -261,6 +261,7 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax);
int param_set_charp(const char *val, const struct kernel_param *kp)
{
+ char *tmp;
size_t len, maxlen = 1024;
len = strnlen(val, maxlen + 1);
@@ -269,19 +270,20 @@ int param_set_charp(const char *val, const struct kernel_param *kp)
return -ENOSPC;
}
- maybe_kfree_parameter(*(char **)kp->arg);
-
/*
* This is a hack. We can't kmalloc() in early boot, and we
* don't need to; this mangled commandline is preserved.
*/
if (slab_is_available()) {
- *(char **)kp->arg = kmalloc_parameter(len + 1);
- if (!*(char **)kp->arg)
+ tmp = kmalloc_parameter(len + 1);
+ if (!tmp)
return -ENOMEM;
- strcpy(*(char **)kp->arg, val);
+ memcpy(tmp, val, len + 1);
} else
- *(const char **)kp->arg = val;
+ tmp = (char *)val;
+
+ maybe_kfree_parameter(*(char **)kp->arg);
+ *(char **)kp->arg = tmp;
return 0;
}
diff --git a/kernel/pid.c b/kernel/pid.c
index d01d0dd7114b..95b8ccfa8269 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -766,29 +766,6 @@ static struct ctl_table_root pid_table_root = {
.set_ownership = pid_table_root_set_ownership,
};
-static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer,
- size_t *lenp, loff_t *ppos)
-{
- struct pid *new_pid;
- pid_t tmp_pid;
- int r;
- struct ctl_table tmp_table = *table;
-
- tmp_pid = pid_vnr(cad_pid);
- tmp_table.data = &tmp_pid;
-
- r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
- if (r || !write)
- return r;
-
- new_pid = find_get_pid(tmp_pid);
- if (!new_pid)
- return -ESRCH;
-
- put_pid(xchg(&cad_pid, new_pid));
- return 0;
-}
-
static const struct ctl_table pid_table[] = {
{
.procname = "pid_max",
@@ -799,14 +776,6 @@ static const struct ctl_table pid_table[] = {
.extra1 = &pid_max_min,
.extra2 = &pid_max_max,
},
-#ifdef CONFIG_PROC_SYSCTL
- {
- .procname = "cad_pid",
- .maxlen = sizeof(int),
- .mode = 0600,
- .proc_handler = proc_do_cad_pid,
- },
-#endif
};
#endif
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 695c33e75efd..f070c5c1103a 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -1366,6 +1366,29 @@ static struct attribute *reboot_attrs[] = {
};
#ifdef CONFIG_SYSCTL
+static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table tmp_table = *table;
+ struct pid *new_pid;
+ pid_t tmp_pid;
+ int r;
+
+ tmp_pid = pid_vnr(cad_pid);
+ tmp_table.data = &tmp_pid;
+
+ r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
+ if (r || !write)
+ return r;
+
+ new_pid = find_get_pid(tmp_pid);
+ if (!new_pid)
+ return -ESRCH;
+
+ put_pid(xchg(&cad_pid, new_pid));
+ return 0;
+}
+
static const struct ctl_table kern_reboot_table[] = {
{
.procname = "poweroff_cmd",
@@ -1381,6 +1404,12 @@ static const struct ctl_table kern_reboot_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "cad_pid",
+ .maxlen = sizeof(int),
+ .mode = 0600,
+ .proc_handler = proc_do_cad_pid,
+ },
};
static void __init kernel_reboot_sysctls_init(void)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f6..145eea2d99a1 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -443,6 +443,17 @@ static void __sched_core_flip(bool enabled)
sched_core_lock(cpu, &flags);
+ /*
+ * A core-wide selection may have the shared rq lock temporarily
+ * released by a lock-dropping ->pick_task(). Flipping would
+ * rebind rq_lockp() under it. Wait it out.
+ */
+ while (cpu_rq(cpu)->core->core_pick_in_flight) {
+ sched_core_unlock(cpu, &flags);
+ cpu_relax();
+ sched_core_lock(cpu, &flags);
+ }
+
for_each_cpu(t, smt_mask)
cpu_rq(t)->core_enabled = enabled;
@@ -6223,7 +6234,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf)
unsigned long cookie;
int i, cpu, occ = 0;
struct rq *rq_i;
- bool need_sync;
+ bool need_sync = false;
if (!sched_core_enabled(rq))
return __pick_next_task(rq, rf);
@@ -6242,6 +6253,8 @@ pick_next_task(struct rq *rq, struct rq_flags *rf)
return __pick_next_task(rq, rf);
}
+ rq->core->core_pick_in_flight++;
+
/*
* If there were no {en,de}queues since we picked (IOW, the task
* pointers are all still valid), and we haven't scheduled the last
@@ -6266,7 +6279,9 @@ pick_next_task(struct rq *rq, struct rq_flags *rf)
prev_balance(rq, rf);
smt_mask = cpu_smt_mask(cpu);
- need_sync = !!rq->core->core_cookie;
+
+restart:
+ need_sync |= !!rq->core->core_cookie;
/* reset state */
rq->core->core_cookie = 0UL;
@@ -6301,10 +6316,15 @@ pick_next_task(struct rq *rq, struct rq_flags *rf)
* and there are no cookied tasks running on siblings.
*/
if (!need_sync) {
-restart_single:
next = pick_task(rq, rf);
- if (unlikely(next == RETRY_TASK))
- goto restart_single;
+ if (unlikely(next == RETRY_TASK)) {
+ /* rq lock may have been dropped, clocks invalidated */
+ core_clock_updated = false;
+ if (!(rq->clock_update_flags & RQCF_UPDATED))
+ update_rq_clock(rq);
+ goto restart;
+ }
+
if (!next->core_cookie) {
rq->core_pick = NULL;
rq->core_dl_server = NULL;
@@ -6324,7 +6344,6 @@ restart_single:
*
* Tie-break prio towards the current CPU
*/
-restart_multi:
max = NULL;
for_each_cpu_wrap(i, smt_mask, cpu) {
rq_i = cpu_rq(i);
@@ -6338,8 +6357,13 @@ restart_multi:
update_rq_clock(rq_i);
p = pick_task(rq_i, rf);
- if (unlikely(p == RETRY_TASK))
- goto restart_multi;
+ if (unlikely(p == RETRY_TASK)) {
+ /* rq lock may have been dropped, clocks invalidated */
+ core_clock_updated = false;
+ if (!(rq->clock_update_flags & RQCF_UPDATED))
+ update_rq_clock(rq);
+ goto restart;
+ }
rq_i->core_pick = p;
rq_i->core_dl_server = rq_i->dl_server;
@@ -6445,6 +6469,7 @@ restart_multi:
}
out_set_next:
+ rq->core->core_pick_in_flight--;
put_prev_set_next_task(rq, rq->donor, next);
if (rq->core->core_forceidle_count && next == rq->idle)
queue_core_balance(rq);
@@ -6640,6 +6665,13 @@ static void sched_core_cpu_deactivate(unsigned int cpu)
core_rq->core_forceidle_occupation = rq->core_forceidle_occupation;
/*
+ * A stale leftover would bias the count forever if this CPU later
+ * returns as its own leader. Move, don't copy.
+ */
+ core_rq->core_pick_in_flight = rq->core_pick_in_flight;
+ rq->core_pick_in_flight = 0;
+
+ /*
* Accounting edge for forced idle is handled in pick_next_task().
* Don't need another one here, since the hotplug thread shouldn't
* have a cookie.
@@ -9053,6 +9085,7 @@ void __init sched_init(void)
rq->core_forceidle_count = 0;
rq->core_forceidle_occupation = 0;
rq->core_forceidle_start = 0;
+ rq->core_pick_in_flight = 0;
rq->core_cookie = 0UL;
#endif
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index dff4ee04694c..614ff0d33c01 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -61,6 +61,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;
@@ -606,7 +617,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;
}
@@ -848,7 +859,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/ext/ext.c b/kernel/sched/ext/ext.c
index 18183062f751..d7c4b62e712e 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -479,12 +479,27 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags)
*/
DEFINE_PER_CPU(struct rq *, scx_locked_rq_state);
+/*
+ * Under core scheduling, a pick that releases the rq lock invalidates the
+ * core-wide selection it is part of. Count the releases so that the core-sched
+ * pick can tell whether one happened across dispatch.
+ */
+static void scx_rq_lock_drop(struct rq *rq)
+{
+ lockdep_assert_rq_held(rq);
+#ifdef CONFIG_SCHED_CORE
+ if (sched_core_enabled(rq))
+ rq->scx.lock_drop_seq++;
+#endif
+}
+
static void switch_rq_lock(struct rq *from, struct rq *to)
{
bool tracked = scx_locked_rq() == from;
if (tracked)
update_locked_rq(NULL);
+ scx_rq_lock_drop(from);
raw_spin_rq_unlock(from);
raw_spin_rq_lock(to);
if (tracked)
@@ -1133,6 +1148,7 @@ static void deferred_irq_workfn(struct irq_work *irq_work)
raw_spin_rq_lock(rq);
run_deferred(rq);
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock(rq);
}
@@ -2430,12 +2446,14 @@ static bool consume_remote_task(struct rq *this_rq,
struct task_struct *p, u64 enq_flags,
struct scx_dispatch_q *dsq, struct rq *src_rq)
{
+ scx_rq_lock_drop(this_rq);
raw_spin_rq_unlock(this_rq);
if (unlink_dsq_and_lock_src_rq(p, dsq, src_rq)) {
move_remote_task_to_local_dsq(p, enq_flags, src_rq, this_rq);
return true;
} else {
+ scx_rq_lock_drop(src_rq);
raw_spin_rq_unlock(src_rq);
raw_spin_rq_lock(this_rq);
return false;
@@ -2737,7 +2755,7 @@ retry:
BUG_ON(!(p->scx.flags & SCX_TASK_QUEUED));
- dsq = find_dsq_for_dispatch(sch, this_rq(), dsq_id, task_cpu(p));
+ dsq = find_dsq_for_dispatch(sch, rq, dsq_id, task_cpu(p));
if (dsq->id == SCX_DSQ_LOCAL)
dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags);
@@ -2774,12 +2792,20 @@ static inline void maybe_queue_balance_callback(struct rq *rq)
rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
}
+/* what dispatch concluded, consumed by the pick that follows */
+enum scx_dsp_verdict {
+ SCX_DSP_NONE, /* nothing to run */
+ SCX_DSP_LOCAL, /* local DSQ has tasks */
+ SCX_DSP_PREV, /* keep running @prev */
+ SCX_DSP_RETRY, /* pick helpers only: restart the pick */
+};
+
/*
* One user of this function is scx_bpf_dispatch() which can be called
* recursively as sub-sched dispatches nest. Always inline to reduce stack usage
* from the call frame.
*/
-static __always_inline bool
+static __always_inline enum scx_dsp_verdict
scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
struct task_struct *prev, bool nested)
{
@@ -2790,12 +2816,15 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
scx_task_on_sched(sch, prev);
if (consume_global_dsq(sch, rq))
- return true;
+ return SCX_DSP_LOCAL;
if (bypass_dsp_enabled(sch)) {
/* if @sch is bypassing, only the bypass DSQs are active */
- if (scx_bypassing(sch, cpu))
- return consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0);
+ if (scx_bypassing(sch, cpu)) {
+ if (consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0))
+ return SCX_DSP_LOCAL;
+ return SCX_DSP_NONE;
+ }
#ifdef CONFIG_EXT_SUB_SCHED
/*
@@ -2815,13 +2844,13 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
if (!(pcpu->bypass_host_seq++ % SCX_BYPASS_HOST_NTH) &&
consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0)) {
__scx_add_event(sch, SCX_EV_SUB_BYPASS_DISPATCH, 1);
- return true;
+ return SCX_DSP_LOCAL;
}
#endif /* CONFIG_EXT_SUB_SCHED */
}
if (unlikely(!SCX_HAS_OP(sch, dispatch)) || !scx_rq_online(rq))
- return false;
+ return SCX_DSP_NONE;
dspc->rq = rq;
@@ -2848,14 +2877,12 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
flush_dispatch_buf(sch, rq);
- if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice) {
- rq->scx.flags |= SCX_RQ_BAL_KEEP;
- return true;
- }
+ if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice)
+ return SCX_DSP_PREV;
if (rq->scx.local_dsq.nr)
- return true;
+ return SCX_DSP_LOCAL;
if (consume_global_dsq(sch, rq))
- return true;
+ return SCX_DSP_LOCAL;
/*
* ops.dispatch() can trap us in this loop by repeatedly
@@ -2877,20 +2904,20 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
* queued. Without this fallback, bypassed tasks could stall if the host
* scheduler's ops.dispatch() doesn't yield any tasks.
*/
- if (bypass_dsp_enabled(sch))
- return consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0);
+ if (bypass_dsp_enabled(sch) && consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0))
+ return SCX_DSP_LOCAL;
- return false;
+ return SCX_DSP_NONE;
}
-static int balance_one(struct rq *rq, struct task_struct *prev)
+static enum scx_dsp_verdict balance_one(struct rq *rq, struct task_struct *prev)
{
struct scx_sched *sch = scx_root;
+ enum scx_dsp_verdict verdict;
s32 cpu = cpu_of(rq);
lockdep_assert_rq_held(rq);
rq->scx.flags |= SCX_RQ_IN_BALANCE;
- rq->scx.flags &= ~SCX_RQ_BAL_KEEP;
if ((sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) &&
unlikely(rq->scx.cpu_released)) {
@@ -2920,16 +2947,19 @@ static int balance_one(struct rq *rq, struct task_struct *prev)
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice &&
!scx_bypassing(sch, cpu)) {
- rq->scx.flags |= SCX_RQ_BAL_KEEP;
+ verdict = SCX_DSP_PREV;
goto has_tasks;
}
}
/* if there already are tasks to run, nothing to do */
- if (rq->scx.local_dsq.nr)
+ if (rq->scx.local_dsq.nr) {
+ verdict = SCX_DSP_LOCAL;
goto has_tasks;
+ }
- if (scx_dispatch_sched(sch, rq, prev, false))
+ verdict = scx_dispatch_sched(sch, rq, prev, false);
+ if (verdict != SCX_DSP_NONE)
goto has_tasks;
/*
@@ -2938,12 +2968,12 @@ static int balance_one(struct rq *rq, struct task_struct *prev)
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) &&
(!(sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(sch, cpu))) {
- rq->scx.flags |= SCX_RQ_BAL_KEEP;
__scx_add_event(sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
+ verdict = SCX_DSP_PREV;
goto has_tasks;
}
rq->scx.flags &= ~SCX_RQ_IN_BALANCE;
- return false;
+ return SCX_DSP_NONE;
has_tasks:
/*
@@ -2960,7 +2990,7 @@ has_tasks:
schedule_reenq_local(rq, 0);
rq->scx.flags &= ~SCX_RQ_IN_BALANCE;
- return true;
+ return verdict;
}
static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
@@ -3113,12 +3143,12 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
* ops.enqueue() that @p is the only one available for this cpu,
* which should trigger an explicit follow-up scheduling event.
*
- * Core scheduling can force this CPU idle while @p stays
- * runnable. @p's cookie then won't match the core's, so skip
- * the warning in that case.
+ * Under core scheduling, a pick dispatches only when nothing is
+ * locally runnable and can legitimately go idle with @p still
+ * runnable (see do_pick_task_scx()).
*/
if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
- WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) &&
+ WARN_ON_ONCE(!sched_core_enabled(rq) &&
!(sch->ops.flags & SCX_OPS_ENQ_LAST));
do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
} else {
@@ -3133,12 +3163,27 @@ switch_class:
static void kick_sync_wait_bal_cb(struct rq *rq)
{
- struct scx_kick_syncs __rcu *ks = __this_cpu_read(scx_kick_syncs);
- unsigned long *ksyncs = rcu_dereference_sched(ks)->syncs;
+ struct scx_kick_syncs __rcu *ks;
+ unsigned long *ksyncs;
bool waited;
s32 cpu;
/*
+ * This callback is queued and normally flushed within @rq's own
+ * scheduling pass. However, dispatch can drop the rq lock while it sits
+ * queued, and lock takers in that window (the sched class change paths,
+ * the scx task iterator) flush pending balance callbacks on release,
+ * running this one on a foreign CPU whose snapshots are unrelated. The
+ * kicked CPUs are already on their way to advance the kick_syncs being
+ * waited on. Don't get in the way.
+ */
+ if (unlikely(cpu_of(rq) != smp_processor_id()))
+ return;
+
+ ks = __this_cpu_read(scx_kick_syncs);
+ ksyncs = rcu_dereference_sched(ks)->syncs;
+
+ /*
* Drop rq lock and enable IRQs while waiting. IRQs must be enabled
* — a target CPU may be waiting for us to process an IPI (e.g. TLB
* flush) while we wait for its kick_sync to advance.
@@ -3160,6 +3205,7 @@ retry:
continue;
}
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock_irq(rq);
while (READ_ONCE(cpu_rq(cpu)->scx.kick_sync) == ksyncs[cpu]) {
smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
@@ -3179,27 +3225,23 @@ static struct task_struct *first_local_task(struct rq *rq)
struct task_struct, scx.dsq_list.node);
}
-static struct task_struct *
-do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
+/*
+ * Run dispatch and queue the follow-up work for a pick.
+ */
+static enum scx_dsp_verdict dispatch_pick(struct rq *rq, struct rq_flags *rf,
+ struct task_struct *prev)
{
- struct task_struct *prev = rq->curr;
- bool keep_prev;
- struct task_struct *p;
-
- /* see kick_sync_wait_bal_cb() */
- smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
-
- rq_modified_begin(rq, &ext_sched_class);
+ enum scx_dsp_verdict verdict;
rq_unpin_lock(rq, rf);
- balance_one(rq, prev);
+ verdict = balance_one(rq, prev);
rq_repin_lock(rq, rf);
maybe_queue_balance_callback(rq);
/*
- * Defer to a balance callback which can drop rq lock and enable
- * IRQs. Waiting directly in the pick path would deadlock against
- * CPUs sending us IPIs (e.g. TLB flushes) while we wait for them.
+ * Defer to a balance callback which can drop rq lock and enable IRQs.
+ * Waiting directly in the pick path would deadlock against CPUs sending
+ * us IPIs (e.g. TLB flushes) while we wait for them.
*/
if (unlikely(rq->scx.kick_sync_pending)) {
rq->scx.kick_sync_pending = false;
@@ -3207,10 +3249,102 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
kick_sync_wait_bal_cb);
}
+ if (unlikely(verdict == SCX_DSP_PREV && prev->sched_class != &ext_sched_class)) {
+ WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED);
+ verdict = SCX_DSP_LOCAL;
+ }
+
+ return verdict;
+}
+
+#ifdef CONFIG_SCHED_CORE
+/*
+ * Dispatch for a pick when core scheduling is enabled. The selection picks for
+ * all SMT siblings and the rq_i->core_pick state it builds must stay atomic
+ * throughout. If the dispatch released the rq lock, anything can have happened
+ * in between - return %SCX_DSP_RETRY to restart the selection against current
+ * state.
+ */
+static enum scx_dsp_verdict dispatch_core_pick(struct rq *rq, struct rq_flags *rf,
+ struct task_struct *prev)
+{
+ enum scx_dsp_verdict verdict;
+ u32 seq = rq->scx.lock_drop_seq;
+
+ /* another dispatch is in flight on @rq, let that handle it */
+ if (rq->scx.flags & SCX_RQ_IN_BALANCE)
+ return SCX_DSP_NONE;
+
+ rq_unpin_lock(rq, rf);
+
+ verdict = balance_one(rq, prev);
+
+ if (cpu_of(rq) == smp_processor_id()) {
+ maybe_queue_balance_callback(rq);
+
+ /* see dispatch_pick() */
+ if (unlikely(rq->scx.kick_sync_pending)) {
+ rq->scx.kick_sync_pending = false;
+ queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb,
+ kick_sync_wait_bal_cb);
+ }
+ } else if (unlikely(rq->scx.flags & SCX_RQ_BAL_CB_PENDING)) {
+ /*
+ * Balance callbacks must run in the context that queued them,
+ * so they can't be queued on another CPU's rq. Run the deferred
+ * work directly instead.
+ */
+ rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
+ run_deferred(rq);
+ }
+
+ rq_repin_lock(rq, rf);
+
+ /* if balance_one() released the rq lock, restart the selection */
+ if (rq->scx.lock_drop_seq != seq)
+ return SCX_DSP_RETRY;
+
+ /* see dispatch_pick() */
+ if (unlikely(verdict == SCX_DSP_PREV &&
+ prev->sched_class != &ext_sched_class)) {
+ WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED);
+ verdict = SCX_DSP_LOCAL;
+ }
+
+ return verdict;
+}
+#else /* CONFIG_SCHED_CORE */
+static enum scx_dsp_verdict dispatch_core_pick(struct rq *rq, struct rq_flags *rf,
+ struct task_struct *prev)
+{
+ return SCX_DSP_NONE;
+}
+#endif /* CONFIG_SCHED_CORE */
+
+static struct task_struct *
+do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
+{
+ struct task_struct *prev = rq->curr;
+ enum scx_dsp_verdict verdict;
+ struct task_struct *p;
+
+ /* see kick_sync_wait_bal_cb() */
+ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
+
+ rq_modified_begin(rq, &ext_sched_class);
+
+ if (sched_core_enabled(rq))
+ verdict = dispatch_core_pick(rq, rf, prev);
+ else
+ verdict = dispatch_pick(rq, rf, prev);
+
+ if (verdict == SCX_DSP_RETRY)
+ return RETRY_TASK;
+
/*
- * If any higher-priority sched class enqueued a runnable task on
- * this rq during balance_one(), abort and return RETRY_TASK, so
- * that the scheduler loop can restart.
+ * If any higher-priority sched class enqueued a runnable task on this
+ * rq during balance_one(), abort and return RETRY_TASK, so that the
+ * scheduler loop can restart.
*
* If @force_scx is true, always try to pick a SCHED_EXT task,
* regardless of any higher-priority sched classes activity.
@@ -3218,19 +3352,11 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
if (!force_scx && rq_modified_above(rq, &ext_sched_class))
return RETRY_TASK;
- keep_prev = rq->scx.flags & SCX_RQ_BAL_KEEP;
- if (unlikely(keep_prev &&
- prev->sched_class != &ext_sched_class)) {
- WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED);
- keep_prev = false;
- }
-
/*
- * If balance_one() is telling us to keep running @prev, replenish slice
- * if necessary and keep running @prev. Otherwise, pop the first one
- * from the local DSQ.
+ * If we're keeping @prev, replenish slice if necessary and keep running
+ * @prev. Otherwise, pop the first one from the local DSQ.
*/
- if (keep_prev) {
+ if (verdict == SCX_DSP_PREV) {
p = prev;
if (!p->scx.slice)
refill_task_slice_dfl(scx_task_sched(p), p);
@@ -3305,6 +3431,8 @@ void ext_server_init(struct rq *rq)
*
* When ops.core_sched_before() is enabled, @p->scx.core_sched_at is used to
* implement FIFO ordering within each local DSQ. See pick_task_scx().
+ *
+ * Return: %true if @a should run after @b.
*/
bool scx_prio_less(const struct task_struct *a, const struct task_struct *b,
bool in_fi)
@@ -3313,6 +3441,10 @@ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b,
struct scx_sched *sch_b = scx_task_sched(b);
/*
+ * scx_prio_less() returns whether @a should run after @b while
+ * ops.core_sched_before() returns whether its first argument should run
+ * before the second. Swap the arguments.
+ *
* The const qualifiers are dropped from task_struct pointers when
* calling ops.core_sched_before(). Accesses are controlled by the
* verifier.
@@ -3321,8 +3453,8 @@ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b,
!scx_bypassing(sch_a, task_cpu(a)))
return SCX_CALL_OP_2TASKS_RET(sch_a, core_sched_before,
task_rq(a),
- (struct task_struct *)a,
- (struct task_struct *)b);
+ (struct task_struct *)b,
+ (struct task_struct *)a);
else
return time_after64(a->scx.core_sched_at, b->scx.core_sched_at);
}
@@ -3877,7 +4009,7 @@ void scx_post_fork(struct task_struct *p)
void scx_cancel_fork(struct task_struct *p)
{
- if (scx_enabled()) {
+ if (scx_init_task_enabled) {
struct rq *rq;
struct rq_flags rf;
@@ -4258,8 +4390,10 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag
task_rq = task_rq(p);
if (locked_rq != task_rq) {
- if (locked_rq)
+ if (locked_rq) {
+ scx_rq_lock_drop(locked_rq);
raw_spin_rq_unlock(locked_rq);
+ }
if (unlikely(!raw_spin_rq_trylock(task_rq))) {
raw_spin_unlock(&dsq->lock);
raw_spin_rq_lock(task_rq);
@@ -4285,6 +4419,7 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag
p->scx.flags &= ~SCX_TASK_REENQ_REASON_MASK;
if (!(++nr_enqueued % SCX_TASK_ITER_BATCH)) {
+ scx_rq_lock_drop(locked_rq);
raw_spin_rq_unlock(locked_rq);
locked_rq = NULL;
cpu_relax();
@@ -4297,8 +4432,10 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag
raw_spin_unlock(&dsq->lock);
if (locked_rq != rq) {
- if (locked_rq)
+ if (locked_rq) {
+ scx_rq_lock_drop(locked_rq);
raw_spin_rq_unlock(locked_rq);
+ }
raw_spin_rq_lock(rq);
}
}
@@ -4309,7 +4446,7 @@ static void process_deferred_reenq_users(struct rq *rq)
while (true) {
struct scx_dispatch_q *dsq;
- u64 reenq_flags;
+ u64 dsq_id, reenq_flags;
scoped_guard (raw_spinlock, &rq->scx.deferred_reenq_lock) {
struct scx_deferred_reenq_user *dru =
@@ -4332,7 +4469,12 @@ static void process_deferred_reenq_users(struct rq *rq)
/* see schedule_dsq_reenq() */
smp_mb();
- BUG_ON(dsq->id & SCX_DSQ_FLAG_BUILTIN);
+ /* destroy_dsq() may have raced and invalidated @dsq, nothing to reenq */
+ dsq_id = READ_ONCE(dsq->id);
+ if (unlikely(dsq_id == SCX_DSQ_INVALID))
+ continue;
+
+ BUG_ON(dsq_id & SCX_DSQ_FLAG_BUILTIN);
reenq_user(rq, dsq, reenq_flags);
}
}
@@ -5350,6 +5492,7 @@ resume:
if (!(nr_balanced % SCX_BYPASS_LB_BATCH) && n) {
list_move_tail(&cursor.node, &n->scx.dsq_list.node);
raw_spin_unlock(&donor_dsq->lock);
+ scx_rq_lock_drop(donor_rq);
raw_spin_rq_unlock_irq(donor_rq);
cpu_relax();
raw_spin_rq_lock_irq(donor_rq);
@@ -5360,6 +5503,7 @@ resume:
list_del_init(&cursor.node);
raw_spin_unlock(&donor_dsq->lock);
+ scx_rq_lock_drop(donor_rq);
raw_spin_rq_unlock_irq(donor_rq);
return nr_balanced;
@@ -5573,7 +5717,7 @@ static void disable_bypass_dsp(struct scx_sched *sch)
*
* - ops.dispatch() is ignored.
*
- * - balance_one() does not set %SCX_RQ_BAL_KEEP on non-zero slice as slice
+ * - balance_one() does not report %SCX_DSP_PREV on non-zero slice as slice
* can't be trusted. Whenever a tick triggers, the running task is rotated to
* the tail of the queue with core_sched_at touched.
*
@@ -5650,6 +5794,7 @@ static void scx_bypass(struct scx_sched *sch, bool bypass)
* sees scx_bypassing() before moving tasks to SCX.
*/
if (!scx_enabled()) {
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock(rq);
continue;
}
@@ -5676,6 +5821,7 @@ static void scx_bypass(struct scx_sched *sch, bool bypass)
if (cpu_online(cpu) || cpu == smp_processor_id())
resched_curr(rq);
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock(rq);
}
@@ -8403,6 +8549,7 @@ static bool kick_one_cpu(s32 cpu, struct rq *this_rq, unsigned long *ksyncs)
cpumask_clear_cpu(cpu, this_scx->cpus_to_wait);
}
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock_irqrestore(rq, flags);
return should_wait;
@@ -8419,6 +8566,7 @@ static void kick_one_cpu_if_idle(s32 cpu, struct rq *this_rq)
(cpu_online(cpu) || cpu == cpu_of(this_rq)))
resched_curr(rq);
+ scx_rq_lock_drop(rq);
raw_spin_rq_unlock_irqrestore(rq, flags);
}
@@ -8456,6 +8604,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work)
raw_spin_rq_lock(this_rq);
this_scx->kick_sync_pending = true;
resched_curr(this_rq);
+ scx_rq_lock_drop(this_rq);
raw_spin_rq_unlock(this_rq);
}
}
@@ -8865,9 +9014,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
{
struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq;
struct scx_sched *sch;
- struct rq *this_rq, *src_rq, *locked_rq;
+ struct rq *p_rq, *src_rq, *locked_rq;
bool dispatched = false;
- bool in_balance;
unsigned long flags;
/*
@@ -8897,24 +9045,28 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
}
/*
- * Can be called from either ops.dispatch() locking this_rq() or any
- * context where no rq lock is held. If latter, lock @p's task_rq which
- * we'll likely need anyway.
+ * Can be called from either ops.dispatch() holding the dispatched rq's
+ * lock or any context where no rq lock is held. If latter, lock @p's
+ * task_rq which we'll likely need anyway.
*/
src_rq = task_rq(p);
local_irq_save(flags);
- this_rq = this_rq();
- in_balance = this_rq->scx.flags & SCX_RQ_IN_BALANCE;
- if (in_balance) {
- if (this_rq != src_rq)
- switch_rq_lock(this_rq, src_rq);
+ /*
+ * Under core scheduling, dispatch can run for a sibling rq, so the
+ * locked rq is not necessarily this CPU's.
+ */
+ locked_rq = scx_locked_rq();
+
+ if (locked_rq) {
+ if (locked_rq != src_rq)
+ switch_rq_lock(locked_rq, src_rq);
} else {
raw_spin_rq_lock(src_rq);
}
- locked_rq = src_rq;
+ p_rq = src_rq;
raw_spin_lock(&src_dsq->lock);
/* did someone else get to it while we dropped the locks? */
@@ -8924,7 +9076,7 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
}
/* @p is still on $src_dsq and stable, determine the destination */
- dst_dsq = find_dsq_for_dispatch(sch, this_rq, dsq_id, task_cpu(p));
+ dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p));
/*
* Apply vtime and slice updates before moving so that the new time is
@@ -8937,14 +9089,15 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
p->scx.slice = kit->slice;
/* execute move */
- locked_rq = move_task_between_dsqs(sch, p, enq_flags, src_dsq, dst_dsq);
+ p_rq = move_task_between_dsqs(sch, p, enq_flags, src_dsq, dst_dsq);
dispatched = true;
out:
- if (in_balance) {
- if (this_rq != locked_rq)
- switch_rq_lock(locked_rq, this_rq);
+ if (locked_rq) {
+ if (locked_rq != p_rq)
+ switch_rq_lock(p_rq, locked_rq);
} else {
- raw_spin_rq_unlock_irqrestore(locked_rq, flags);
+ scx_rq_lock_drop(p_rq);
+ raw_spin_rq_unlock_irqrestore(p_rq, flags);
}
kit->cursor.flags &= ~(__SCX_DSQ_ITER_HAS_SLICE |
@@ -9182,7 +9335,7 @@ __bpf_kfunc bool scx_bpf_dsq_move_vtime(struct bpf_iter_scx_dsq *it__iter,
*/
__bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux)
{
- struct rq *this_rq = this_rq();
+ struct rq *rq = scx_locked_rq();
struct scx_sched *parent, *child;
guard(rcu)();
@@ -9201,8 +9354,8 @@ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *
return false;
}
- return scx_dispatch_sched(child, this_rq, this_rq->scx.sub_dispatch_prev,
- true);
+ return scx_dispatch_sched(child, rq, rq->scx.sub_dispatch_prev, true) !=
+ SCX_DSP_NONE;
}
#endif /* CONFIG_EXT_SUB_SCHED */
@@ -9423,9 +9576,11 @@ static void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags)
if (raw_spin_rq_trylock(target_rq)) {
if (can_skip_idle_kick(target_rq)) {
+ scx_rq_lock_drop(target_rq);
raw_spin_rq_unlock(target_rq);
goto out;
}
+ scx_rq_lock_drop(target_rq);
raw_spin_rq_unlock(target_rq);
}
cpumask_set_cpu(cpu, this_rq->scx.cpus_to_kick_if_idle);
@@ -9496,6 +9651,10 @@ __bpf_kfunc s32 scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux *
*
* Return the number of tasks in the DSQ matching @dsq_id. If not found,
* -%ENOENT is returned.
+ *
+ * %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler
+ * operation is locked to - e.g. the rq being dispatched for in ops.dispatch() -
+ * or the calling CPU's when no rq is locked.
*/
__bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux)
{
@@ -9512,7 +9671,7 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux
}
if (dsq_id == SCX_DSQ_LOCAL) {
- ret = READ_ONCE(this_rq()->scx.local_dsq.nr);
+ ret = READ_ONCE((scx_locked_rq() ?: this_rq())->scx.local_dsq.nr);
goto out;
} else if ((dsq_id & SCX_DSQ_LOCAL_ON) == SCX_DSQ_LOCAL_ON) {
s32 cpu = scx_cpu_ret(sch, dsq_id & SCX_DSQ_LOCAL_CPU_MASK);
@@ -9691,10 +9850,15 @@ __bpf_kfunc struct task_struct *scx_bpf_dsq_peek(u64 dsq_id,
* - User DSQs
*
* Re-enqueues are performed asynchronously. Can be called from anywhere.
+ *
+ * %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler
+ * operation is locked to - e.g. the rq being dispatched for in ops.dispatch() -
+ * or the calling CPU's when no rq is locked.
*/
__bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags,
const struct bpf_prog_aux *aux)
{
+ struct rq *locked_rq = scx_locked_rq();
struct scx_sched *sch;
struct scx_dispatch_q *dsq;
@@ -9713,8 +9877,8 @@ __bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags,
if (!(reenq_flags & __SCX_REENQ_FILTER_MASK))
reenq_flags |= SCX_REENQ_ANY;
- dsq = find_dsq_for_dispatch(sch, this_rq(), dsq_id, smp_processor_id());
- schedule_dsq_reenq(sch, dsq, reenq_flags, scx_locked_rq());
+ dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, smp_processor_id());
+ schedule_dsq_reenq(sch, dsq, reenq_flags, locked_rq);
}
/**
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 56acf502ba26..f76e6f7f4a01 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -784,7 +784,6 @@ enum scx_rq_flags {
*/
SCX_RQ_ONLINE = 1 << 0,
SCX_RQ_CAN_STOP_TICK = 1 << 1,
- SCX_RQ_BAL_KEEP = 1 << 3, /* balance decided to keep current */
SCX_RQ_CLK_VALID = 1 << 5, /* RQ clock is fresh and valid */
SCX_RQ_BAL_CB_PENDING = 1 << 6, /* must queue a cb after dispatching */
@@ -804,6 +803,9 @@ struct scx_rq {
bool cpu_released;
u32 flags;
u32 nr_immed; /* ENQ_IMMED tasks on local_dsq */
+#ifdef CONFIG_SCHED_CORE
+ u32 lock_drop_seq; /* nr dispatch lock releases */
+#endif
u64 clock; /* current per-rq clock -- see scx_bpf_now() */
cpumask_var_t cpus_to_kick;
cpumask_var_t cpus_to_kick_if_idle;
@@ -1358,6 +1360,7 @@ struct rq {
unsigned int core_forceidle_seq;
unsigned int core_forceidle_occupation;
u64 core_forceidle_start;
+ unsigned int core_pick_in_flight;
#endif /* CONFIG_SCHED_CORE */
/* Scratch cpumask to be temporarily used under rq_lock */
diff --git a/kernel/signal.c b/kernel/signal.c
index bbc0fd4cc4d7..c53f202da9da 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1181,6 +1181,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;
@@ -1194,6 +1195,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/taskstats.c b/kernel/taskstats.c
index 2cd0172d0516..05a905e64fa1 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/itimer.c b/kernel/time/itimer.c
index 7c6110e964e7..03a32dffc56c 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 b1b5ec43c0f2..696bb119c56e 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2943,10 +2943,12 @@ static int __do_adjtimex(struct tk_data *tkd, struct __kernel_timex *txc,
return ret;
add_device_randomness(txc, sizeof(*txc));
- if (!aux_clock)
+ if (!aux_clock) {
ktime_get_real_ts64(&ts);
- else
- tk_get_aux_ts64(tkd->timekeeper.id, &ts);
+ } else {
+ if (!tk_get_aux_ts64(tkd->timekeeper.id, &ts))
+ return -ENODEV;
+ }
add_device_randomness(&ts, sizeof(ts));
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 655a8c6cd84d..ae9abf14688e 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -2492,6 +2492,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/ring_buffer.c b/kernel/trace/ring_buffer.c
index 2667992f0aa2..e3136a5ed4ff 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -391,6 +391,17 @@ static __always_inline unsigned int rb_page_size(struct buffer_page *bpage)
return rb_data_page_size(bpage->page);
}
+/**
+ * rb_page_capacity - Get the capacity of a buffer page
+ * @bpage: The buffer page
+ *
+ * Return: The maximum size available for events in the given buffer page.
+ */
+static __always_inline unsigned int rb_page_capacity(struct buffer_page *bpage)
+{
+ return (PAGE_SIZE << bpage->order) - BUF_PAGE_HDR_SIZE;
+}
+
static void free_buffer_page(struct buffer_page *bpage)
{
/* Range pages are not to be freed */
@@ -507,7 +518,7 @@ struct ring_buffer_per_cpu {
raw_spinlock_t reader_lock; /* serialize readers */
arch_spinlock_t lock;
struct lock_class_key lock_key;
- struct buffer_data_page *free_page;
+ struct buffer_data_read_page free_page;
unsigned long nr_pages;
unsigned int current_context;
struct list_head *pages;
@@ -586,11 +597,42 @@ struct trace_buffer {
struct ring_buffer_meta *meta;
- unsigned int subbuf_size;
unsigned int subbuf_order;
unsigned int max_data_size;
};
+static __always_inline unsigned int rb_subbuf_size(struct trace_buffer *buffer)
+{
+ return PAGE_SIZE << buffer->subbuf_order;
+}
+
+/**
+ * rb_subbuf_capacity - Get the capacity of a subbuffer
+ * @buffer: A trace buffer
+ *
+ * Unsafe to use without holding trace_buffer::mutex or with resizing enabled.
+ * Consider rb_page_capacity() instead.
+ *
+ * Return: The maximum size available for events in a trace buffer subbuffer.
+ */
+static __always_inline unsigned int rb_subbuf_capacity(struct trace_buffer *buffer)
+{
+ return rb_subbuf_size(buffer) - BUF_PAGE_HDR_SIZE;
+}
+
+/**
+ * rb_subbuf_start - Get the start address of a subbuffer
+ * @buffer: A trace buffer
+ * @addr: An address of an event on a subbuffer
+ *
+ * Return: The start of the subbuffer for where @addr sits
+ */
+static __always_inline
+unsigned long rb_subbuf_start(struct trace_buffer *buffer, unsigned long addr)
+{
+ return addr & ~((unsigned long)(rb_subbuf_size(buffer) - 1));
+}
+
struct ring_buffer_iter {
struct ring_buffer_per_cpu *cpu_buffer;
unsigned long head;
@@ -630,7 +672,7 @@ int ring_buffer_print_page_header(struct trace_buffer *buffer, struct trace_seq
trace_seq_printf(s, "\tfield: char data;\t"
"offset:%u;\tsize:%u;\tsigned:%u;\n",
(unsigned int)offsetof(typeof(field), data),
- (unsigned int)(buffer ? buffer->subbuf_size :
+ (unsigned int)(buffer ? rb_subbuf_capacity(buffer) :
PAGE_SIZE - BUF_PAGE_HDR_SIZE),
(unsigned int)is_signed_type(char));
@@ -1620,7 +1662,7 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs)
*/
static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu)
{
- int subbuf_size = buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+ int subbuf_size = rb_subbuf_size(buffer);
struct ring_buffer_cpu_meta *meta;
struct ring_buffer_meta *bmeta;
unsigned long ptr;
@@ -2432,8 +2474,8 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
bpage->id = i + 1;
cpu_buffer->subbuf_ids[i + 1] = bpage;
} else {
- int order = cpu_buffer->buffer->subbuf_order;
- bpage->page = alloc_cpu_data(cpu_buffer->cpu, order);
+ bpage->page = alloc_cpu_data(cpu_buffer->cpu,
+ cpu_buffer->buffer->subbuf_order);
if (!bpage->page)
goto free_pages;
}
@@ -2556,8 +2598,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
bpage->range = 1;
cpu_buffer->subbuf_ids[0] = bpage;
} else {
- int order = cpu_buffer->buffer->subbuf_order;
- bpage->page = alloc_cpu_data(cpu, order);
+ bpage->page = alloc_cpu_data(cpu, bpage->order);
if (!bpage->page)
goto fail_free_reader;
}
@@ -2631,7 +2672,7 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
free_buffer_page(bpage);
}
- free_page((unsigned long)cpu_buffer->free_page);
+ free_pages((unsigned long)cpu_buffer->free_page.data, cpu_buffer->free_page.order);
kfree(cpu_buffer);
}
@@ -2731,10 +2772,9 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
buffer->subbuf_order = order;
subbuf_size = (PAGE_SIZE << order);
- buffer->subbuf_size = subbuf_size - BUF_PAGE_HDR_SIZE;
/* Max payload is buffer page size - header (8bytes) */
- buffer->max_data_size = buffer->subbuf_size - (sizeof(u32) * 2);
+ buffer->max_data_size = rb_subbuf_capacity(buffer) - (sizeof(u32) * 2);
buffer->flags = flags;
buffer->clock = trace_clock_local;
@@ -2818,9 +2858,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
if (nr_pages < 2)
goto fail_free_buffers;
} else {
-
/* need at least two pages */
- nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size);
+ nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer));
if (nr_pages < 2)
nr_pages = 2;
}
@@ -3203,7 +3242,7 @@ static void update_pages_handler(struct work_struct *work)
* @size: the new size.
* @cpu_id: the cpu buffer to resize
*
- * Minimum size is 2 * buffer->subbuf_size.
+ * Minimum size is 2 * rb_subbuf_capacity(buffer).
*
* Returns 0 on success and < 0 on failure.
*/
@@ -3225,12 +3264,6 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
!cpumask_test_cpu(cpu_id, buffer->cpumask))
return 0;
- nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size);
-
- /* we need a minimum of two pages */
- if (nr_pages < 2)
- nr_pages = 2;
-
/*
* Keep CPUs from coming online while resizing to synchronize
* with new per CPU buffers being created.
@@ -3241,6 +3274,12 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
mutex_lock(&buffer->mutex);
atomic_inc(&buffer->resizing);
+ nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer));
+
+ /* we need a minimum of two pages */
+ if (nr_pages < 2)
+ nr_pages = 2;
+
if (cpu_id == RING_BUFFER_ALL_CPUS) {
/*
* Don't succeed if resizing is disabled, as a reader might be
@@ -3513,7 +3552,7 @@ rb_event_index(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event
{
unsigned long addr = (unsigned long)event;
- addr &= (PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1;
+ addr &= (unsigned long)rb_subbuf_size(cpu_buffer->buffer) - 1;
return addr - BUF_PAGE_HDR_SIZE;
}
@@ -3755,8 +3794,8 @@ static inline void
rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
unsigned long tail, struct rb_event_info *info)
{
- unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
struct buffer_page *tail_page = info->tail_page;
+ unsigned long bsize = rb_page_capacity(tail_page);
struct ring_buffer_event *event;
unsigned long length = info->length;
@@ -4101,8 +4140,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
new_index = rb_event_index(cpu_buffer, event);
old_index = new_index + rb_event_ts_length(event);
- addr = (unsigned long)event;
- addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1);
+ addr = rb_subbuf_start(cpu_buffer->buffer, (unsigned long)event);
bpage = READ_ONCE(cpu_buffer->tail_page);
@@ -4767,7 +4805,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
tail = write - info->length;
/* See if we shot pass the end of this buffer page */
- if (unlikely(write > cpu_buffer->buffer->subbuf_size)) {
+ if (unlikely(write > rb_page_capacity(tail_page))) {
check_buffer(cpu_buffer, info, CHECK_FULL_PAGE);
return rb_move_tail(cpu_buffer, tail, info);
}
@@ -5012,7 +5050,7 @@ rb_decrement_entry(struct ring_buffer_per_cpu *cpu_buffer,
struct buffer_page *bpage = cpu_buffer->commit_page;
struct buffer_page *start;
- addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1);
+ addr = rb_subbuf_start(cpu_buffer->buffer, addr);
/* Do the likely case first */
if (likely(bpage->page == (void *)addr)) {
@@ -5753,8 +5791,11 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
prev_reader = cpu_buffer->subbuf_ids[cpu_buffer->meta_page->reader.id];
- WARN_ON_ONCE(cpu_buffer->remote->swap_reader_page(cpu_buffer->cpu,
- cpu_buffer->remote->priv));
+ if (cpu_buffer->remote->swap_reader_page(cpu_buffer->cpu,
+ cpu_buffer->remote->priv)) {
+ pr_warn_ratelimited("Remote reader page swap failed\n");
+ return NULL;
+ }
/* nr_pages doesn't include the reader page */
if (WARN_ON_ONCE(cpu_buffer->meta_page->reader.id > cpu_buffer->nr_pages))
return NULL;
@@ -5799,7 +5840,6 @@ static struct buffer_page *
__rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
{
int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3;
- unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
struct buffer_page *reader = NULL;
unsigned long overwrite;
unsigned long flags;
@@ -5947,7 +5987,7 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
#define USECS_WAIT 1000000
for (nr_loops = 0; nr_loops < USECS_WAIT; nr_loops++) {
/* If the write is past the end of page, a writer is still updating it */
- if (likely(!reader || rb_page_write(reader) <= bsize))
+ if (likely(!reader || rb_page_write(reader) <= rb_page_capacity(reader)))
break;
udelay(1);
@@ -6380,36 +6420,44 @@ EXPORT_SYMBOL_GPL(ring_buffer_consume);
struct ring_buffer_iter *
ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags)
{
+ struct ring_buffer_iter *iter __free(kfree) = kzalloc_obj(*iter, flags);
struct ring_buffer_per_cpu *cpu_buffer;
- struct ring_buffer_iter *iter;
-
- if (!cpumask_test_cpu(cpu, buffer->cpumask))
- return NULL;
- iter = kzalloc_obj(*iter, flags);
if (!iter)
return NULL;
- /* Holds the entire event: data and meta data */
- iter->event_size = buffer->subbuf_size;
- iter->event = kmalloc(iter->event_size, flags);
- if (!iter->event) {
- kfree(iter);
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return NULL;
- }
cpu_buffer = buffer->buffers[cpu];
- iter->cpu_buffer = cpu_buffer;
+ /*
+ * Only KDB is using GFP_ATOMIC, for the others, lock the buffer to
+ * prevent concurrent resizing.
+ */
+ if (gfpflags_allow_blocking(flags))
+ mutex_lock(&buffer->mutex);
atomic_inc(&cpu_buffer->resize_disabled);
+ if (gfpflags_allow_blocking(flags))
+ mutex_unlock(&buffer->mutex);
+
+ /* Holds the entire event: data and meta data. */
+ iter->event_size = rb_page_capacity(READ_ONCE(cpu_buffer->reader_page));
+ iter->event = kmalloc(iter->event_size, flags);
+ if (!iter->event) {
+ atomic_dec(&cpu_buffer->resize_disabled);
+ return NULL;
+ }
+ iter->cpu_buffer = cpu_buffer;
+
guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
arch_spin_lock(&cpu_buffer->lock);
rb_iter_reset(iter);
arch_spin_unlock(&cpu_buffer->lock);
- return iter;
+ return_ptr(iter);
}
EXPORT_SYMBOL_GPL(ring_buffer_read_start);
@@ -6463,7 +6511,7 @@ unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu)
if (!cpumask_test_cpu(cpu, buffer->cpumask))
return 0;
- return buffer->subbuf_size * buffer->buffers[cpu]->nr_pages;
+ return rb_subbuf_capacity(buffer) * buffer->buffers[cpu]->nr_pages;
}
EXPORT_SYMBOL_GPL(ring_buffer_size);
@@ -6962,9 +7010,9 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
local_irq_save(flags);
arch_spin_lock(&cpu_buffer->lock);
- if (cpu_buffer->free_page) {
- bpage->data = cpu_buffer->free_page;
- cpu_buffer->free_page = NULL;
+ if (cpu_buffer->free_page.data) {
+ *bpage = cpu_buffer->free_page;
+ cpu_buffer->free_page.data = NULL;
}
arch_spin_unlock(&cpu_buffer->lock);
@@ -6973,7 +7021,7 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
if (bpage->data) {
rb_init_data_page(bpage->data);
} else {
- bpage->data = alloc_cpu_data(cpu, cpu_buffer->buffer->subbuf_order);
+ bpage->data = alloc_cpu_data(cpu, bpage->order);
if (!bpage->data) {
kfree(bpage);
return ERR_PTR(-ENOMEM);
@@ -7016,8 +7064,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
local_irq_save(flags);
arch_spin_lock(&cpu_buffer->lock);
- if (!cpu_buffer->free_page) {
- cpu_buffer->free_page = dpage;
+ if (!cpu_buffer->free_page.data) {
+ cpu_buffer->free_page = *data_page;
dpage = NULL;
}
@@ -7094,15 +7142,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
if (!data_page || !data_page->data)
return -1;
- if (data_page->order != buffer->subbuf_order)
- return -1;
-
dpage = data_page->data;
if (!dpage)
return -1;
guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
+ if (data_page->order != cpu_buffer->reader_page->order)
+ return -1;
+
reader = rb_get_reader_page(cpu_buffer);
if (!reader)
return -1;
@@ -7228,7 +7276,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
* missed events, then record it there.
*/
if (missed_events > 0 &&
- buffer->subbuf_size - size >= sizeof(missed_events)) {
+ rb_page_capacity(reader) - size >= sizeof(missed_events)) {
memcpy(&dpage->data[size], &missed_events,
sizeof(missed_events));
local_add(RB_MISSED_STORED, &dpage->commit);
@@ -7248,8 +7296,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
/*
* This page may be off to user land. Zero it out here.
*/
- if (size < buffer->subbuf_size)
- memset(&dpage->data[size], 0, buffer->subbuf_size - size);
+ if (size < rb_page_capacity(reader))
+ memset(&dpage->data[size], 0, rb_page_capacity(reader) - size);
return read;
}
@@ -7275,7 +7323,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
*/
int ring_buffer_subbuf_size_get(struct trace_buffer *buffer)
{
- return buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+ return rb_subbuf_size(buffer);
}
EXPORT_SYMBOL_GPL(ring_buffer_subbuf_size_get);
@@ -7320,7 +7368,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
{
struct ring_buffer_per_cpu *cpu_buffer;
struct buffer_page *bpage, *tmp;
- int old_order, old_size;
+ unsigned int old_capacity;
+ int old_order;
int nr_pages;
int psize;
int err;
@@ -7329,9 +7378,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
if (!buffer || order < 0)
return -EINVAL;
- if (buffer->subbuf_order == order)
- return 0;
-
psize = (1 << order) * PAGE_SIZE;
if (psize <= BUF_PAGE_HDR_SIZE)
return -EINVAL;
@@ -7340,18 +7386,21 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
if (psize > RB_WRITE_MASK + 1)
return -EINVAL;
- old_order = buffer->subbuf_order;
- old_size = buffer->subbuf_size;
-
/* prevent another thread from changing buffer sizes */
guard(mutex)(&buffer->mutex);
+
+ old_order = buffer->subbuf_order;
+ if (old_order == order)
+ return 0;
+
+ old_capacity = rb_subbuf_capacity(buffer);
+
atomic_inc(&buffer->record_disabled);
/* Make sure all commits have finished */
synchronize_rcu();
buffer->subbuf_order = order;
- buffer->subbuf_size = psize - BUF_PAGE_HDR_SIZE;
/* Make sure all new buffers are allocated, before deleting the old ones */
for_each_buffer_cpu(buffer, cpu) {
@@ -7367,8 +7416,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
}
/* Update the number of pages to match the new size */
- nr_pages = old_size * buffer->buffers[cpu]->nr_pages;
- nr_pages = DIV_ROUND_UP(nr_pages, buffer->subbuf_size);
+ nr_pages = old_capacity * buffer->buffers[cpu]->nr_pages;
+ nr_pages = DIV_ROUND_UP(nr_pages, rb_subbuf_capacity(buffer));
/* we need a minimum of two pages */
if (nr_pages < 2)
@@ -7390,7 +7439,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
}
for_each_buffer_cpu(buffer, cpu) {
- struct buffer_data_page *old_free_data_page;
+ struct buffer_data_read_page old_free_data_page;
struct list_head old_pages;
unsigned long flags;
@@ -7431,8 +7480,10 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update;
cpu_buffer->nr_pages_to_update = 0;
+ arch_spin_lock(&cpu_buffer->lock);
old_free_data_page = cpu_buffer->free_page;
- cpu_buffer->free_page = NULL;
+ cpu_buffer->free_page.data = NULL;
+ arch_spin_unlock(&cpu_buffer->lock);
rb_head_page_activate(cpu_buffer);
@@ -7443,7 +7494,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
- free_pages((unsigned long)old_free_data_page, old_order);
+ free_pages((unsigned long)old_free_data_page.data, old_free_data_page.order);
rb_check_pages(cpu_buffer);
}
@@ -7454,7 +7505,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
error:
buffer->subbuf_order = old_order;
- buffer->subbuf_size = old_size;
atomic_dec(&buffer->record_disabled);
@@ -7532,7 +7582,7 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer,
meta->meta_struct_len = sizeof(*meta);
meta->nr_subbufs = nr_subbufs;
- meta->subbuf_size = cpu_buffer->buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+ meta->subbuf_size = rb_subbuf_size(cpu_buffer->buffer);
meta->meta_page_size = meta->subbuf_size;
rb_update_meta_page(cpu_buffer);
@@ -7894,7 +7944,7 @@ consume:
* missed events, then record it there.
*/
commit = rb_page_size(reader);
- if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
+ if (rb_subbuf_capacity(buffer) - commit >= sizeof(missed_events)) {
memcpy(&dpage->data[commit], &missed_events,
sizeof(missed_events));
local_add(RB_MISSED_STORED, &dpage->commit);
@@ -7926,7 +7976,7 @@ consume:
out:
/* Some archs do not have data cache coherency between kernel and user-space */
flush_kernel_vmap_range(cpu_buffer->reader_page->page,
- buffer->subbuf_size + BUF_PAGE_HDR_SIZE);
+ rb_subbuf_size(buffer));
rb_update_meta_page(cpu_buffer);
diff --git a/kernel/trace/simple_ring_buffer.c b/kernel/trace/simple_ring_buffer.c
index f4642f5adda3..49913bb0057a 100644
--- a/kernel/trace/simple_ring_buffer.c
+++ b/kernel/trace/simple_ring_buffer.c
@@ -160,8 +160,8 @@ int simple_ring_buffer_swap_reader_page(struct simple_rb_per_cpu *cpu_buffer)
overrun = cpu_buffer->meta->overrun;
} while (!simple_bpage_unset_head_link(last, reader, SIMPLE_RB_LINK_NORMAL) && retry--);
- if (!retry)
- return -EINVAL;
+ if (retry < 0)
+ return -EBUSY;
cpu_buffer->head_page = simple_bpage_from_link(reader->link.next);
cpu_buffer->head_page->link.prev = &reader->link;
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 395238b2b715..525062941ee3 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8214,6 +8214,8 @@ buffer_subbuf_size_write(struct file *filp, const char __user *ubuf,
/* Do not allow tracing while changing the order of the ring buffer */
tracing_stop_tr(tr);
+ trace_access_lock(RING_BUFFER_ALL_CPUS);
+
old_order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer);
if (old_order == order)
goto out;
@@ -8253,6 +8255,7 @@ buffer_subbuf_size_write(struct file *filp, const char __user *ubuf,
#endif
(*ppos)++;
out:
+ trace_access_unlock(RING_BUFFER_ALL_CPUS);
if (ret)
cnt = ret;
tracing_start_tr(tr);
@@ -9726,7 +9729,8 @@ __init static void enable_instances(void)
tr = trace_array_create_systems(name, NULL, addr, size);
if (IS_ERR(tr)) {
- pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str);
+ pr_warn("Tracing: Failed to create instance buffer '%s' (%ld)\n", name,
+ PTR_ERR(tr));
continue;
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 3650d84d4f16..28167b229419 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -4901,6 +4901,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 58d28cd1afa3..6bf00710fd10 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6641,8 +6641,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 8c82ecb735f4..6b89d225b189 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -868,6 +868,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/lib/iov_iter.c b/lib/iov_iter.c
index c2484551a4e8..34a52e9ba9e1 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1904,6 +1904,8 @@ static unsigned int get_contig_folio_len(struct page **pages,
* @max_size: maximum size to extract from @iter
* @nr_vecs: number of vectors in @bv (on in and output)
* @max_vecs: maximum vectors in @bv, including those filled before calling
+ * @mem_align_mask: reject with -EINVAL if the source address or
+ * length is not aligned to this mask
* @extraction_flags: flags to qualify request
*
* Like iov_iter_extract_pages(), but returns physically contiguous ranges
@@ -1915,14 +1917,19 @@ static unsigned int get_contig_folio_len(struct page **pages,
*/
ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
size_t max_size, unsigned short *nr_vecs,
- unsigned short max_vecs, iov_iter_extraction_t extraction_flags)
+ unsigned short max_vecs, unsigned mem_align_mask,
+ iov_iter_extraction_t extraction_flags)
{
+ unsigned long start = (unsigned long)iter_iov_addr(iter);
unsigned short entries_left = max_vecs - *nr_vecs;
unsigned short nr_pages, i = 0;
size_t left, offset, len;
struct page **pages;
ssize_t size;
+ if ((start | iter_iov_len(iter)) & mem_align_mask)
+ return -EINVAL;
+
/*
* Move page array up in the allocated memory for the bio vecs as far as
* possible so that we can start filling biovecs from the beginning
diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c
index dfb4f2358cab..ca5c615d5bef 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 9a8b4916540c..bfe7bef80f34 100644
--- a/lib/xarray.c
+++ b/lib/xarray.c
@@ -1053,6 +1053,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 {
struct xa_node *node;
diff --git a/mm/compaction.c b/mm/compaction.c
index f08765ade014..65fd08aabb2c 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1381,12 +1381,44 @@ static bool suitable_migration_source(struct compact_control *cc,
if (pageblock_skip_persistent(page))
return false;
- if ((cc->mode != MIGRATE_ASYNC) || !cc->direct_compaction)
+ /*
+ * Background compaction produces blocks for the zone at
+ * large, with no particular allocation context. Allow all
+ * block types, including CMA.
+ */
+ if (!cc->direct_compaction)
return true;
block_mt = get_pageblock_migratetype(page);
- if (cc->migratetype == MIGRATE_MOVABLE)
+ /*
+ * CMA pages can only be taken by ALLOC_CMA requests. For anybody
+ * else, vacating a CMA block consumes free pages the caller
+ * could have used, and produces free pages it cannot.
+ */
+ if (is_migrate_cma(block_mt) && !(cc->alloc_flags & ALLOC_CMA))
+ return false;
+
+ /*
+ * Per default, scans are restricted to blocks compatible with
+ * the request, to prevent cross-contamination. Once
+ * compaction priority escalates to synchronous scans, though,
+ * scan all blocks to try to make forward progress. For
+ * movable request, this likely helps little: there shouldn't
+ * be many migratable pages inside non-movable blocks besides
+ * allocator fallbacks. For non-movable requests, this helps a
+ * lot, as they can finally scan movable blocks.
+ */
+ if (cc->mode != MIGRATE_ASYNC)
+ return true;
+
+ /*
+ * Prevent <pageblock_order unmovable/reclaimable requests from
+ * polluting movable blocks through fallbacks. Whole-block production
+ * (directly requested, or defrag_mode) is exempt as the allocator
+ * claims and converts these.
+ */
+ if (cc->migratetype == MIGRATE_MOVABLE || cc->order >= pageblock_order)
return is_migrate_movable(block_mt);
else
return block_mt == cc->migratetype;
@@ -1974,12 +2006,12 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
return pfn;
/*
- * Only allow kcompactd and direct requests for movable pages to
- * quickly clear out a MOVABLE pageblock for allocation. This
- * reduces the risk that a large movable pageblock is freed for
- * an unmovable/reclaimable small allocation.
+ * Prevent <pageblock_order unmovable/reclaimable requests from
+ * polluting movable blocks through fallbacks. Whole-block production
+ * is exempt as the allocator claims and converts these.
*/
- if (cc->direct_compaction && cc->migratetype != MIGRATE_MOVABLE)
+ if (cc->direct_compaction && cc->migratetype != MIGRATE_MOVABLE &&
+ cc->order < pageblock_order)
return pfn;
/*
@@ -2770,9 +2802,8 @@ out:
static enum compact_result compact_zone_order(struct zone *zone, int order,
gfp_t gfp_mask, enum compact_priority prio,
unsigned int alloc_flags, int highest_zoneidx,
- struct page **capture)
+ struct capture_control *capc)
{
- enum compact_result ret;
struct compact_control cc = {
.order = order,
.search_order = order,
@@ -2787,54 +2818,24 @@ static enum compact_result compact_zone_order(struct zone *zone, int order,
.ignore_skip_hint = (prio == MIN_COMPACT_PRIORITY),
.ignore_block_suitable = (prio == MIN_COMPACT_PRIORITY)
};
- struct capture_control capc = {
- .cc = &cc,
- .page = NULL,
- };
- /*
- * Make sure the structs are really initialized before we expose the
- * capture control, in case we are interrupted and the interrupt handler
- * frees a page.
- */
- barrier();
- WRITE_ONCE(current->capture_control, &capc);
-
- ret = compact_zone(&cc, &capc);
-
- /*
- * Make sure we hide capture control first before we read the captured
- * page pointer, otherwise an interrupt could free and capture a page
- * and we would leak it.
- */
- WRITE_ONCE(current->capture_control, NULL);
- *capture = READ_ONCE(capc.page);
- /*
- * Technically, it is also possible that compaction is skipped but
- * the page is still captured out of luck(IRQ came and freed the page).
- * Returning COMPACT_SUCCESS in such cases helps in properly accounting
- * the COMPACT[STALL|FAIL] when compaction is skipped.
- */
- if (*capture)
- ret = COMPACT_SUCCESS;
-
- return ret;
+ return compact_zone(&cc, capc);
}
/**
* try_to_compact_pages - Direct compact to satisfy a high-order allocation
* @gfp_mask: The GFP mask of the current allocation
- * @order: The order of the current allocation
+ * @order: The order to try to make available
* @alloc_flags: The allocation flags of the current allocation
* @ac: The context of current allocation
* @prio: Determines how hard direct compaction should try to succeed
- * @capture: Pointer to free page created by compaction will be stored here
+ * @capc: Free page capture bypassing the freelist
*
* This is the main entry point for direct page compaction.
*/
enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
unsigned int alloc_flags, const struct alloc_context *ac,
- enum compact_priority prio, struct page **capture)
+ enum compact_priority prio, struct capture_control *capc)
{
struct zoneref *z;
struct zone *zone;
@@ -2861,8 +2862,17 @@ enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
continue;
}
+ WRITE_ONCE(capc->zone, zone);
+
status = compact_zone_order(zone, order, gfp_mask, prio,
- alloc_flags, ac->highest_zoneidx, capture);
+ alloc_flags, ac->highest_zoneidx, capc);
+
+ WRITE_ONCE(capc->zone, NULL);
+
+ /* Stop if a page has been captured */
+ if (READ_ONCE(capc->page))
+ status = COMPACT_SUCCESS;
+
rc = max(status, rc);
/* The allocation should succeed, stop compacting */
diff --git a/mm/gup.c b/mm/gup.c
index 0692119b7904..bde05664fe9a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2273,6 +2273,7 @@ static unsigned long collect_longterm_unpinnable_folios(
for (folio = pofs_get_folio(pofs, i); folio;
folio = pofs_next_folio(folio, pofs, &i)) {
+ const int pin_refs = folio_has_pincount(folio) ? 1 : GUP_PIN_COUNTING_BIAS;
if (folio_is_longterm_pinnable(folio))
continue;
@@ -2287,15 +2288,20 @@ static unsigned long collect_longterm_unpinnable_folios(
continue;
}
+ /*
+ * We drain not only to make the folio_isolate_lru() succeed,
+ * but also to remove any other folio references from LRU
+ * caches.
+ */
if (drained == 0 && folio_may_be_lru_cached(folio) &&
folio_ref_count(folio) !=
- folio_expected_ref_count(folio) + 1) {
+ folio_expected_ref_count(folio) + pin_refs) {
lru_add_drain();
drained = 1;
}
if (drained == 1 && folio_may_be_lru_cached(folio) &&
folio_ref_count(folio) !=
- folio_expected_ref_count(folio) + 1) {
+ folio_expected_ref_count(folio) + pin_refs) {
lru_add_drain_all();
drained = 2;
}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 5d94fa4c74fd..2860fb43ff57 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2340,8 +2340,8 @@ bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
goto out;
if (unlikely(!pmd_present(orig_pmd))) {
- VM_BUG_ON(thp_migration_supported() &&
- !pmd_is_migration_entry(orig_pmd));
+ VM_WARN_ON_ONCE(!pmd_is_migration_entry(orig_pmd) &&
+ !pmd_is_device_private_entry(orig_pmd));
goto out;
}
@@ -4027,34 +4027,42 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
XA_STATE(xas, &folio->mapping->i_pages, folio->index);
struct folio *end_folio = folio_next(folio);
bool is_anon = folio_test_anon(folio);
+ struct mem_cgroup *memcg, *old_memcg;
struct address_space *mapping = NULL;
struct anon_vma *anon_vma = NULL;
int old_order = folio_order(folio);
struct folio *new_folio, *next;
int nr_shmem_dropped = 0;
enum ttu_flags ttu_flags = 0;
- int ret;
pgoff_t end = 0;
+ int ret;
VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio);
if (folio != page_folio(split_at) || folio != page_folio(lock_at)) {
ret = -EINVAL;
- goto out;
+ goto out_no_memcg;
}
if (new_order >= old_order) {
ret = -EINVAL;
- goto out;
+ goto out_no_memcg;
}
ret = folio_check_splittable(folio, new_order, split_type);
if (ret) {
VM_WARN_ONCE(ret == -EINVAL, "Tried to split an unsplittable folio");
- goto out;
+ goto out_no_memcg;
}
+ /*
+ * switch to folio's memcg as xarray node allocation can happen and
+ * needs to charge to it.
+ */
+ memcg = get_mem_cgroup_from_folio(folio);
+ old_memcg = set_active_memcg(memcg);
+
if (is_anon) {
/*
* The caller does not necessarily hold an mmap_lock that would
@@ -4197,6 +4205,10 @@ out_unlock:
if (mapping)
i_mmap_unlock_read(mapping);
out:
+ /* restore to caller's old_memcg */
+ set_active_memcg(old_memcg);
+ mem_cgroup_put(memcg);
+out_no_memcg:
xas_destroy(&xas);
if (is_pmd_order(old_order))
count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index e93c4d2456aa..5c9c6a842c41 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3353,7 +3353,7 @@ static void __init gather_bootmem_prealloc_parallel(unsigned long start,
gather_bootmem_prealloc_node(nid);
}
-static void __init gather_bootmem_prealloc(void)
+void __init hugetlb_bootmem_struct_page_init(void)
{
struct padata_mt_job job = {
.thread_fn = gather_bootmem_prealloc_parallel,
@@ -3365,6 +3365,31 @@ static void __init gather_bootmem_prealloc(void)
.max_threads = num_node_state(N_MEMORY),
.numa_aware = true,
};
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+ struct zone *zone;
+
+ for_each_zone(zone) {
+ for (int i = 0; i < NR_VMEMMAP_TAILS; i++) {
+ struct page *tail, *p;
+ unsigned int order;
+
+ tail = zone->vmemmap_tails[i];
+ if (!tail)
+ continue;
+
+ order = i + VMEMMAP_TAIL_MIN_ORDER;
+ p = page_to_virt(tail);
+ /*
+ * prep_and_add_bootmem_folios() can access pageblock
+ * flags on bootmem HugeTLB pages, so initialize the
+ * shared tail struct pages here before bootmem folios
+ * start using them.
+ */
+ for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++)
+ init_compound_tail(p + j, NULL, order, zone);
+ }
+ }
+#endif
padata_do_multithreaded(&job);
}
@@ -3557,7 +3582,7 @@ static unsigned long __init hugetlb_pages_alloc_boot(struct hstate *h)
* - For gigantic pages, this is called early in the boot process and
* pages are allocated from memblock allocated or something similar.
* Gigantic pages are actually added to pools later with the routine
- * gather_bootmem_prealloc.
+ * hugetlb_bootmem_struct_page_init.
* - For non-gigantic pages, this is called later in the boot process after
* all of mm is up and functional. Pages are allocated from buddy and
* then added to hugetlb pools.
@@ -4127,7 +4152,6 @@ static int __init hugetlb_init(void)
}
hugetlb_init_hstates();
- gather_bootmem_prealloc();
report_hugepages();
hugetlb_sysfs_init();
@@ -5157,6 +5181,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
bool adjust_reservation;
unsigned long last_addr_mask;
+ i_mmap_assert_write_locked(vma->vm_file->f_mapping);
WARN_ON(!is_vm_hugetlb_page(vma));
BUG_ON(start & ~huge_page_mask(h));
BUG_ON(end & ~huge_page_mask(h));
@@ -5248,7 +5273,10 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
/*
* Restore the reservation for anonymous page, otherwise the
- * backing page could be stolen by someone.
+ * backing page could be stolen by someone. Restore only on the
+ * last unmap, otherwise the owner could empty its resv map
+ * while the folio is still mapped by a child. Note that holding
+ * i_mmap_lock_write is needed to check the number of mappings.
* If there we are freeing a surplus, do not set the restore
* reservation bit.
*/
@@ -5256,7 +5284,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
spin_lock_irq(&hugetlb_lock);
if (!h->surplus_huge_pages && __vma_private_lock(vma) &&
- folio_test_anon(folio)) {
+ !folio_mapped(folio) && folio_test_anon(folio)) {
folio_set_hugetlb_restore_reserve(folio);
/* Reservation to be adjusted after the spin lock */
adjust_reservation = true;
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 133b46dfb09f..ea6af85bfec1 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -635,12 +635,12 @@ static void __hugetlb_vmemmap_optimize_folios(struct hstate *h,
* mirrored tail page structs RO.
*/
spfn = (unsigned long)&folio->page;
- epfn = spfn + pages_per_huge_page(h);
+ epfn = spfn + hugetlb_vmemmap_size(h);
vmemmap_wrprotect_hvo(spfn, epfn, folio_nid(folio),
HUGETLB_VMEMMAP_RESERVE_SIZE);
- register_page_bootmem_memmap(pfn_to_section_nr(spfn),
+ register_page_bootmem_memmap(pfn_to_section_nr(folio_pfn(folio)),
&folio->page,
- HUGETLB_VMEMMAP_RESERVE_SIZE);
+ HUGETLB_VMEMMAP_RESERVE_PAGES);
continue;
}
@@ -870,27 +870,10 @@ static const struct ctl_table hugetlb_vmemmap_sysctls[] = {
static int __init hugetlb_vmemmap_init(void)
{
const struct hstate *h;
- struct zone *zone;
/* HUGETLB_VMEMMAP_RESERVE_SIZE should cover all used struct pages */
BUILD_BUG_ON(__NR_USED_SUBPAGE > HUGETLB_VMEMMAP_RESERVE_PAGES);
- for_each_zone(zone) {
- for (int i = 0; i < NR_VMEMMAP_TAILS; i++) {
- struct page *tail, *p;
- unsigned int order;
-
- tail = zone->vmemmap_tails[i];
- if (!tail)
- continue;
-
- order = i + VMEMMAP_TAIL_MIN_ORDER;
- p = page_to_virt(tail);
- for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++)
- init_compound_tail(p + j, NULL, order, zone);
- }
- }
-
for_each_hstate(h) {
if (hugetlb_vmemmap_optimizable(h)) {
register_sysctl_init("vm", hugetlb_vmemmap_sysctls);
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a2..5acba3470659 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1059,7 +1059,15 @@ struct compact_control {
* immediately when one is created during the free path.
*/
struct capture_control {
- struct compact_control *cc;
+ struct zone *zone;
+ int migratetype;
+ /*
+ * Allocation request order. May differ from the compaction
+ * order: defrag_mode promotes sub-block allocations to
+ * pageblock-order compaction; capture still matches at the
+ * original allocation order so prep_new_page() is consistent.
+ */
+ int order;
struct page *page;
};
diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
index 6958aa713c67..16f4e67beee8 100644
--- a/mm/kasan/quarantine.c
+++ b/mm/kasan/quarantine.c
@@ -355,7 +355,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/kmemleak.c b/mm/kmemleak.c
index e196f53f9b46..e3c9dbde014b 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1525,22 +1525,25 @@ static int scan_should_stop(void)
/*
* Scan a memory block (exclusive range) for valid pointers and add those
- * found to the gray list.
+ * found to the gray list. Return non-zero if the scan was interrupted.
*/
-static void scan_block(void *_start, void *_end,
- struct kmemleak_object *scanned)
+static int scan_block(void *_start, void *_end,
+ struct kmemleak_object *scanned)
{
unsigned long *ptr;
unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER);
unsigned long *end = _end - (BYTES_PER_POINTER - 1);
unsigned long flags;
+ int stop = 0;
raw_spin_lock_irqsave(&kmemleak_lock, flags);
for (ptr = start; ptr < end; ptr++) {
unsigned long pointer;
- if (scan_should_stop())
+ if (scan_should_stop()) {
+ stop = 1;
break;
+ }
kasan_disable_current();
pointer = *(unsigned long *)kasan_reset_tag((void *)ptr);
@@ -1550,6 +1553,8 @@ static void scan_block(void *_start, void *_end,
pointer_update_refs(scanned, pointer, OBJECT_PERCPU);
}
raw_spin_unlock_irqrestore(&kmemleak_lock, flags);
+
+ return stop;
}
/*
@@ -1564,7 +1569,7 @@ static void scan_large_block(void *start, void *end)
next = min(start + MAX_SCAN_SIZE, end);
scan_block(start, next, NULL);
start = next;
- cond_resched();
+ cond_resched_tasks_rcu_qs();
}
}
#endif
@@ -1599,7 +1604,7 @@ static void scan_object(struct kmemleak_object *object)
scan_block(start, end, object);
raw_spin_unlock_irqrestore(&object->lock, flags);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
if (!(object->flags & OBJECT_ALLOCATED))
break;
@@ -1621,7 +1626,7 @@ static void scan_object(struct kmemleak_object *object)
break;
raw_spin_unlock_irqrestore(&object->lock, flags);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
} while (object->flags & OBJECT_ALLOCATED);
} else {
@@ -1649,7 +1654,7 @@ static void scan_gray_list(void)
*/
object = list_entry(gray_list.next, typeof(*object), gray_list);
while (&object->gray_list != &gray_list) {
- cond_resched();
+ cond_resched_tasks_rcu_qs();
/* may add new objects to the list */
if (!scan_should_stop())
@@ -1684,7 +1689,7 @@ static void kmemleak_cond_resched(struct kmemleak_object *object)
raw_spin_unlock_irq(&kmemleak_lock);
rcu_read_unlock();
- cond_resched();
+ cond_resched_tasks_rcu_qs();
rcu_read_lock();
raw_spin_lock_irq(&kmemleak_lock);
@@ -1697,6 +1702,43 @@ unlock_put:
}
/*
+ * Scan all task kernel stacks, rescheduling between tasks. Each task is looked
+ * up and pinned within its own RCU read-side section, so no lock is held across
+ * the scan and the walk cannot trip the soft lockup watchdog.
+ */
+static void kmemleak_scan_task_stacks(void)
+{
+ struct pid *pid;
+ int nr = 1;
+ int stop = 0;
+
+ do {
+ struct task_struct *p = NULL;
+
+ rcu_read_lock();
+ pid = find_ge_pid(nr, &init_pid_ns);
+ if (pid) {
+ nr = pid_nr(pid) + 1;
+ p = pid_task(pid, PIDTYPE_PID);
+ if (p)
+ get_task_struct(p);
+ }
+ rcu_read_unlock();
+
+ if (p) {
+ void *stack = try_get_task_stack(p);
+
+ if (stack) {
+ stop = scan_block(stack, stack + THREAD_SIZE, NULL);
+ put_task_stack(p);
+ }
+ put_task_struct(p);
+ }
+ cond_resched_tasks_rcu_qs();
+ } while (pid && !stop);
+}
+
+/*
* Print one leak inline. The hex dump is gated on OBJECT_ALLOCATED so it
* does not touch user memory that was freed concurrently; the rest of the
* report (backtrace, comm, pid) is always emitted since the kmemleak_object
@@ -1866,7 +1908,7 @@ static void kmemleak_scan(void)
struct page *page = pfn_to_online_page(pfn);
if (!(pfn & 63))
- cond_resched();
+ cond_resched_tasks_rcu_qs();
if (!page)
continue;
@@ -1885,19 +1927,8 @@ static void kmemleak_scan(void)
/*
* Scanning the task stacks (may introduce false negatives).
*/
- if (kmemleak_stack_scan) {
- struct task_struct *p, *g;
-
- rcu_read_lock();
- for_each_process_thread(g, p) {
- void *stack = try_get_task_stack(p);
- if (stack) {
- scan_block(stack, stack + THREAD_SIZE, NULL);
- put_task_stack(p);
- }
- }
- rcu_read_unlock();
- }
+ if (kmemleak_stack_scan)
+ kmemleak_scan_task_stacks();
/*
* Scan the objects already referenced from the sections scanned
diff --git a/mm/madvise.c b/mm/madvise.c
index 77552b03d318..31ee9ca2b5c1 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -388,8 +388,8 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
goto huge_unlock;
if (unlikely(!pmd_present(orig_pmd))) {
- VM_BUG_ON(thp_migration_supported() &&
- !pmd_is_migration_entry(orig_pmd));
+ VM_WARN_ON_ONCE(!pmd_is_migration_entry(orig_pmd) &&
+ !pmd_is_device_private_entry(orig_pmd));
goto huge_unlock;
}
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index 765069211567..a1776cfacd68 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -1181,13 +1181,13 @@ static ssize_t memcg_write_event_control(struct kernfs_open_file *of,
event->unregister_event = mem_cgroup_usage_unregister_event;
} else if (!strcmp(name, "memory.oom_control")) {
pr_warn_once("oom_control is deprecated and will be removed. "
- "Please report your usecase to linux-mm-@kvack.org"
+ "Please report your usecase to linux-mm@kvack.org"
" if you depend on this functionality.\n");
event->register_event = mem_cgroup_oom_register_event;
event->unregister_event = mem_cgroup_oom_unregister_event;
} else if (!strcmp(name, "memory.pressure_level")) {
pr_warn_once("pressure_level is deprecated and will be removed. "
- "Please report your usecase to linux-mm-@kvack.org "
+ "Please report your usecase to linux-mm@kvack.org "
"if you depend on this functionality.\n");
event->register_event = vmpressure_register_event;
event->unregister_event = vmpressure_unregister_event;
@@ -2040,7 +2040,7 @@ static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css,
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
pr_warn_once("oom_control is deprecated and will be removed. "
- "Please report your usecase to linux-mm-@kvack.org if you "
+ "Please report your usecase to linux-mm@kvack.org if you "
"depend on this functionality.\n");
/* cannot set to root cgroup and only 0 and 1 are allowed */
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6dc4888a90f3..02f5b414f531 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4180,9 +4180,11 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
memcg->memory.track_failcnt = !memcg_on_dfl;
+ memcg->memsw.track_failcnt = !memcg_on_dfl;
WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
page_counter_init(&memcg->kmem, &parent->kmem, false);
page_counter_init(&memcg->tcpmem, &parent->tcpmem, false);
+ memcg->tcpmem.track_failcnt = !memcg_on_dfl;
#endif
} else {
init_memcg_stats();
@@ -4438,6 +4440,7 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent,
int index = memcg_stats_index(MEMCG_KMEM);
memcg->vmstats->state[index] += kmem;
+ memcg->vmstats->state_local[index] += kmem;
if (parent)
parent->vmstats->state_pending[index] += kmem;
}
@@ -4455,9 +4458,11 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent,
int index = memcg_stats_index(NR_SLAB_RECLAIMABLE_B);
lstats->state[index] += slab;
+ lstats->state_local[index] += slab;
if (plstats)
plstats->state_pending[index] += slab;
memcg->vmstats->state[index] += slab;
+ memcg->vmstats->state_local[index] += slab;
if (parent)
parent->vmstats->state_pending[index] += slab;
}
@@ -4466,9 +4471,11 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent,
int index = memcg_stats_index(NR_SLAB_UNRECLAIMABLE_B);
lstats->state[index] += slab;
+ lstats->state_local[index] += slab;
if (plstats)
plstats->state_pending[index] += slab;
memcg->vmstats->state[index] += slab;
+ memcg->vmstats->state_local[index] += slab;
if (parent)
parent->vmstats->state_pending[index] += slab;
}
@@ -4788,6 +4795,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;
@@ -4839,6 +4849,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/mempolicy.c b/mm/mempolicy.c
index 36699fabd3c2..11cf9705975a 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -653,12 +653,14 @@ static void queue_folios_pmd(pmd_t *pmd, struct mm_walk *walk)
{
struct folio *folio;
struct queue_pages *qp = walk->private;
+ pmd_t pmdval = pmdp_get(pmd);
- if (unlikely(pmd_is_migration_entry(*pmd))) {
- qp->nr_failed++;
+ if (unlikely(!pmd_present(pmdval))) {
+ if (pmd_is_migration_entry(pmdval))
+ qp->nr_failed++;
return;
}
- folio = pmd_folio(*pmd);
+ folio = pmd_folio(pmdval);
if (is_huge_zero_folio(folio)) {
walk->action = ACTION_CONTINUE;
return;
@@ -2057,24 +2059,15 @@ struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
bool vma_policy_mof(struct vm_area_struct *vma)
{
struct mempolicy *pol;
+ pgoff_t ilx;
+ bool mof;
- if (vma->vm_ops && vma->vm_ops->get_policy) {
- bool ret = false;
- pgoff_t ilx; /* ignored here */
-
- pol = vma->vm_ops->get_policy(vma, vma->vm_start, &ilx);
- if (pol && (pol->flags & MPOL_F_MOF))
- ret = true;
- mpol_cond_put(pol);
-
- return ret;
- }
-
- pol = vma->vm_policy;
+ pol = __get_vma_policy(vma, vma->vm_start, &ilx);
if (!pol)
pol = get_task_policy(current);
-
- return pol->flags & MPOL_F_MOF;
+ mof = pol->flags & MPOL_F_MOF;
+ mpol_cond_put(pol);
+ return mof;
}
bool apply_policy_zone(struct mempolicy *policy, enum zone_type zone)
diff --git a/mm/migrate.c b/mm/migrate.c
index dd15a84b2a52..35381ce36a8f 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -371,7 +371,11 @@ static bool remove_migration_pte(struct folio *folio,
continue;
}
#endif
- old_pte = ptep_get(pvmw.pte);
+ if (folio_test_hugetlb(folio))
+ old_pte = huge_ptep_get(vma->vm_mm, pvmw.address,
+ pvmw.pte);
+ else
+ old_pte = ptep_get(pvmw.pte);
if (rmap_walk_arg->map_unused_to_zeropage &&
try_to_map_unused_to_zeropage(&pvmw, folio, old_pte, idx))
continue;
@@ -1830,7 +1834,7 @@ static int migrate_pages_batch(struct list_head *from,
is_thp = folio_test_pmd_mappable(folio);
nr_pages = folio_nr_pages(folio);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
/*
* The rare folio on the deferred split list should
diff --git a/mm/migrate_device.c b/mm/migrate_device.c
index 908d2d4ec43a..162d29b2807a 100644
--- a/mm/migrate_device.c
+++ b/mm/migrate_device.c
@@ -1183,6 +1183,13 @@ static void __migrate_device_pages(unsigned long *src_pfns,
MIGRATE_PFN_COMPOUND);
goto next;
}
+
+ /*
+ * reset nr so that only first after-split folio
+ * is processed below
+ */
+ VM_WARN_ON_ONCE(folio_test_large(folio));
+ nr = 1;
} else if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) &&
(dst_pfns[i] & MIGRATE_PFN_COMPOUND) &&
!(src_pfns[i] & MIGRATE_PFN_COMPOUND)) {
@@ -1222,6 +1229,12 @@ static void __migrate_device_pages(unsigned long *src_pfns,
folio = page_folio(migrate_pfn_to_page(src_pfns[i+j]));
newfolio = page_folio(migrate_pfn_to_page(dst_pfns[i+j]));
+ /*
+ * folio_free_swap() removed the folio from the swap
+ * cache. Refresh the saved mapping before migration.
+ */
+ mapping = folio_mapping(folio);
+
r = folio_migrate_mapping(mapping, newfolio, folio, extra_cnt);
if (r)
src_pfns[i+j] &= ~MIGRATE_PFN_MIGRATE;
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 498d62c4ece3..ab4285081869 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -2214,10 +2214,13 @@ bool __init deferred_grow_zone(struct zone *zone, unsigned int order)
}
/*
- * There were no pages to initialize and free which means the zone's
- * memory map is completely initialized.
+ * The loop only tests spfn before entering an iteration, so on exit it
+ * may point up to a section past the end of the zone. When it does,
+ * the rest of the zone has already been handed to
+ * deferred_init_memmap_chunk() and nothing is left to initialize.
*/
- pgdat->first_deferred_pfn = nr_pages ? spfn : ULONG_MAX;
+ pgdat->first_deferred_pfn =
+ spfn < zone_end_pfn(zone) ? spfn : ULONG_MAX;
pgdat_resize_unlock(pgdat, &flags);
@@ -2330,6 +2333,7 @@ void __init page_alloc_init_late(void)
/* Reinit limits that are based on free pages after the kernel is up */
files_maxfiles_init();
#endif
+ hugetlb_bootmem_struct_page_init();
/* Accounting of total+free memory is stable at this point. */
mem_init_print_info();
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ee902a468c2f..f92055827ae9 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -721,14 +721,14 @@ static inline struct capture_control *task_capc(struct zone *zone)
return unlikely(capc) &&
!(current->flags & PF_KTHREAD) &&
!capc->page &&
- capc->cc->zone == zone ? capc : NULL;
+ capc->zone == zone ? capc : NULL;
}
static inline bool
compaction_capture(struct capture_control *capc, struct page *page,
int order, int migratetype)
{
- if (!capc || order != capc->cc->order)
+ if (!capc || order != capc->order)
return false;
/* Do not accidentally pollute CMA or isolated regions*/
@@ -744,12 +744,12 @@ compaction_capture(struct capture_control *capc, struct page *page,
* have trouble finding a high-order free page.
*/
if (order < pageblock_order && migratetype == MIGRATE_MOVABLE &&
- capc->cc->migratetype != MIGRATE_MOVABLE)
+ capc->migratetype != MIGRATE_MOVABLE)
return false;
- if (migratetype != capc->cc->migratetype)
- trace_mm_page_alloc_extfrag(page, capc->cc->order, order,
- capc->cc->migratetype, migratetype);
+ if (migratetype != capc->migratetype)
+ trace_mm_page_alloc_extfrag(page, capc->order, order,
+ capc->migratetype, migratetype);
capc->page = page;
return true;
@@ -4146,18 +4146,67 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct page *page = NULL;
unsigned long pflags;
unsigned int noreclaim_flag;
+ struct capture_control capc = {
+ .zone = NULL,
+ .migratetype = ac->migratetype,
+ .order = order,
+ .page = NULL,
+ };
+ int compact_order = order;
- if (!order)
+ /*
+ * If fallbacks are not permitted (defrag_mode), we either
+ * need to reclaim space in a block of matching type, or clear
+ * out an entire block to allow __rmqueue_claim() to convert.
+ *
+ * Reclaim by itself is primarily freeing space in movable
+ * blocks, since that's where the LRU pages live. So this
+ * works for movable requests, but not for others.
+ *
+ * For those, promote the order to help make blocks, instead
+ * of spinning in reclaim alone unproductively.
+ */
+ if ((alloc_flags & ALLOC_NOFRAGMENT) && ac->migratetype != MIGRATE_MOVABLE)
+ compact_order = max(order, pageblock_order);
+
+ if (!compact_order)
return NULL;
psi_memstall_enter(&pflags);
delayacct_compact_start();
+ fs_reclaim_acquire(gfp_mask);
noreclaim_flag = memalloc_noreclaim_save();
- *compact_result = try_to_compact_pages(gfp_mask, order, alloc_flags, ac,
- prio, &page);
+ /*
+ * Make sure the structs are really initialized before we expose the
+ * capture control, in case we are interrupted and the interrupt handler
+ * frees a page.
+ */
+ barrier();
+ WRITE_ONCE(current->capture_control, &capc);
+
+ *compact_result = try_to_compact_pages(gfp_mask, compact_order,
+ alloc_flags, ac, prio, &capc);
+
+ /*
+ * Make sure we hide capture control first before we read the captured
+ * page pointer, otherwise an interrupt could free and capture a page
+ * and we would leak it.
+ */
+ WRITE_ONCE(current->capture_control, NULL);
+ page = READ_ONCE(capc.page);
+
+ /*
+ * Technically, it is also possible that compaction is skipped but
+ * the page is still captured out of luck(IRQ came and freed the page).
+ * Returning COMPACT_SUCCESS in such cases helps in properly accounting
+ * the COMPACT[STALL|FAIL] when compaction is skipped.
+ */
+ if (page)
+ *compact_result = COMPACT_SUCCESS;
memalloc_noreclaim_restore(noreclaim_flag);
+ fs_reclaim_release(gfp_mask);
psi_memstall_leave(&pflags);
delayacct_compact_end();
@@ -4182,7 +4231,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct zone *zone = page_zone(page);
zone->compact_blockskip_flush = false;
- compaction_defer_reset(zone, order, true);
+ compaction_defer_reset(zone, compact_order, true);
count_vm_event(COMPACTSUCCESS);
return page;
}
@@ -4422,9 +4471,14 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
struct page *page = NULL;
unsigned long pflags;
bool drained = false;
+ int reclaim_order = order;
+
+ /* Match the slowpath compaction promotion in __alloc_pages_direct_compact */
+ if ((alloc_flags & ALLOC_NOFRAGMENT) && ac->migratetype != MIGRATE_MOVABLE)
+ reclaim_order = max(order, pageblock_order);
psi_memstall_enter(&pflags);
- *did_some_progress = __perform_reclaim(gfp_mask, order, ac);
+ *did_some_progress = __perform_reclaim(gfp_mask, reclaim_order, ac);
if (unlikely(!(*did_some_progress)))
goto out;
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 2dddcb6510aa..d8d264eb01b0 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -513,6 +513,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
{
#ifdef CONFIG_MEMCG
unsigned long memcg_data;
+ struct obj_cgroup *objcg;
struct mem_cgroup *memcg;
bool online;
char name[80];
@@ -522,11 +523,14 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
if (!memcg_data || PageTail(page))
goto out_unlock;
- if (memcg_data & MEMCG_DATA_OBJEXTS)
+ if (memcg_data & MEMCG_DATA_OBJEXTS) {
ret += scnprintf(kbuf + ret, count - ret,
"Slab cache page\n");
+ goto out_unlock;
+ }
- memcg = page_memcg_check(page);
+ objcg = (void *)(memcg_data & ~OBJEXTS_FLAGS_MASK);
+ memcg = objcg ? obj_cgroup_memcg(objcg) : NULL;
if (!memcg)
goto out_unlock;
@@ -534,7 +538,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
cgroup_name(memcg->css.cgroup, name, sizeof(name));
ret += scnprintf(kbuf + ret, count - ret,
"Charged %sto %smemcg %s\n",
- PageMemcgKmem(page) ? "(via objcg) " : "",
+ (memcg_data & MEMCG_DATA_KMEM) ? "(via objcg) " : "",
online ? "" : "offline ",
name);
out_unlock:
diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c
index bac2eb5de63d..d7670ba4147b 100644
--- a/mm/page_vma_mapped.c
+++ b/mm/page_vma_mapped.c
@@ -107,7 +107,13 @@ again:
static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)
{
unsigned long pfn;
- pte_t ptent = ptep_get(pvmw->pte);
+ pte_t ptent;
+
+ if (is_vm_hugetlb_page(pvmw->vma))
+ ptent = huge_ptep_get(pvmw->vma->vm_mm, pvmw->address,
+ pvmw->pte);
+ else
+ ptent = ptep_get(pvmw->pte);
if (pvmw->flags & PVMW_MIGRATION) {
const softleaf_t entry = softleaf_from_pte(ptent);
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 5d87c632a255..d3bfece31933 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -126,6 +126,7 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
pmd = pmd_offset(pud, addr);
do {
again:
+ walk->action = ACTION_SUBTREE;
next = pmd_addr_end(addr, end);
if (pmd_none(*pmd)) {
if (has_install)
@@ -138,8 +139,6 @@ again:
continue;
}
- walk->action = ACTION_SUBTREE;
-
/*
* This implies that each ->pmd_entry() handler
* needs to know about pmd_trans_huge() pmds
@@ -196,6 +195,7 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
pud = pud_offset(p4d, addr);
do {
again:
+ walk->action = ACTION_SUBTREE;
next = pud_addr_end(addr, end);
if (pud_none(*pud)) {
if (has_install)
@@ -208,8 +208,6 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
continue;
}
- walk->action = ACTION_SUBTREE;
-
if (ops->pud_entry)
err = ops->pud_entry(pud, addr, next, walk);
if (err)
diff --git a/mm/rmap.c b/mm/rmap.c
index 1c77d5dc06e9..143cf80d95f4 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -2095,14 +2095,19 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
/* Unexpected PMD-mapped THP? */
VM_BUG_ON_FOLIO(!pvmw.pte, folio);
- /*
- * Handle PFN swap PTEs, such as device-exclusive ones, that
- * actually map pages.
- */
- pteval = ptep_get(pvmw.pte);
+ address = pvmw.address;
+ if (folio_test_hugetlb(folio)) {
+ pteval = huge_ptep_get(mm, address, pvmw.pte);
+ } else {
+ pteval = ptep_get(pvmw.pte);
+ }
if (likely(pte_present(pteval))) {
pfn = pte_pfn(pteval);
} else {
+ /*
+ * Handle PFN swap PTEs, such as device-exclusive ones,
+ * that actually map pages.
+ */
const softleaf_t entry = softleaf_from_pte(pteval);
pfn = softleaf_to_pfn(entry);
@@ -2110,7 +2115,6 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
}
subpage = folio_page(folio, pfn - folio_pfn(folio));
- address = pvmw.address;
anon_exclusive = folio_test_anon(folio) &&
PageAnonExclusive(subpage);
@@ -2501,14 +2505,18 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
/* Unexpected PMD-mapped THP? */
VM_BUG_ON_FOLIO(!pvmw.pte, folio);
- /*
- * Handle PFN swap PTEs, such as device-exclusive ones, that
- * actually map pages.
- */
- pteval = ptep_get(pvmw.pte);
+ address = pvmw.address;
+ if (folio_test_hugetlb(folio))
+ pteval = huge_ptep_get(mm, address, pvmw.pte);
+ else
+ pteval = ptep_get(pvmw.pte);
if (likely(pte_present(pteval))) {
pfn = pte_pfn(pteval);
} else {
+ /*
+ * Handle PFN swap PTEs, such as device-exclusive ones,
+ * that actually map pages.
+ */
const softleaf_t entry = softleaf_from_pte(pteval);
pfn = softleaf_to_pfn(entry);
@@ -2516,7 +2524,6 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
}
subpage = folio_page(folio, pfn - folio_pfn(folio));
- address = pvmw.address;
anon_exclusive = folio_test_anon(folio) &&
PageAnonExclusive(subpage);
diff --git a/mm/slub.c b/mm/slub.c
index 0337e60db5ac..5e88c520522b 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -333,14 +333,20 @@ enum track_item { TRACK_ALLOC, TRACK_FREE };
#ifdef SLAB_SUPPORTS_SYSFS
static int sysfs_slab_add(struct kmem_cache *);
+static int __init slab_kset_init(void);
+static void __init slab_sysfs_process_aliases(void);
#else
static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; }
+static inline int slab_kset_init(void) { return 0; }
+static inline void slab_sysfs_process_aliases(void) { }
#endif
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_SLUB_DEBUG)
static void debugfs_slab_add(struct kmem_cache *);
+static void __init slab_debugfs_root_init(void);
#else
static inline void debugfs_slab_add(struct kmem_cache *s) { }
+static inline void slab_debugfs_root_init(void) { }
#endif
enum add_mode {
@@ -5147,7 +5153,7 @@ void kmem_cache_return_sheaf(struct kmem_cache *s, gfp_t gfp,
* simply flush and free it.
*/
if (!barn || data_race(barn->nr_full) >= MAX_FULL_SHEAVES ||
- refill_sheaf(s, sheaf, gfp)) {
+ refill_sheaf(s, sheaf, gfp | __GFP_NOMEMALLOC | __GFP_NOWARN)) {
sheaf_flush_unused(s, sheaf);
free_empty_sheaf(s, sheaf);
return;
@@ -9734,28 +9740,20 @@ int sysfs_slab_alias(struct kmem_cache *s, const char *name)
return 0;
}
-static int __init slab_sysfs_init(void)
+static int __init slab_kset_init(void)
{
- struct kmem_cache *s;
- int err;
-
- mutex_lock(&slab_mutex);
-
slab_kset = kset_create_and_add("slab", NULL, kernel_kobj);
if (!slab_kset) {
- mutex_unlock(&slab_mutex);
pr_err("Cannot register slab subsystem.\n");
return -ENOMEM;
}
- slab_state = FULL;
+ return 0;
+}
- list_for_each_entry(s, &slab_caches, list) {
- err = sysfs_slab_add(s);
- if (err)
- pr_err("SLUB: Unable to add boot slab %s to sysfs\n",
- s->name);
- }
+static void __init slab_sysfs_process_aliases(void)
+{
+ int err;
while (alias_list) {
struct saved_alias *al = alias_list;
@@ -9767,12 +9765,41 @@ static int __init slab_sysfs_init(void)
al->name);
kfree(al);
}
+}
+#endif /* SLAB_SUPPORTS_SYSFS */
+
+#if defined(SLAB_SUPPORTS_SYSFS) || \
+ (defined(CONFIG_SLUB_DEBUG) && defined(CONFIG_DEBUG_FS))
+static int __init slab_late_init(void)
+{
+ struct kmem_cache *s;
+ int err;
+ mutex_lock(&slab_mutex);
+
+ err = slab_kset_init();
+ if (err)
+ goto out;
+
+ slab_debugfs_root_init();
+ slab_state = FULL;
+
+ list_for_each_entry(s, &slab_caches, list) {
+ if (sysfs_slab_add(s))
+ pr_err("SLUB: Unable to add boot slab %s to sysfs\n",
+ s->name);
+
+ if (s->flags & SLAB_STORE_USER)
+ debugfs_slab_add(s);
+ }
+
+ slab_sysfs_process_aliases();
+out:
mutex_unlock(&slab_mutex);
- return 0;
+ return err;
}
-late_initcall(slab_sysfs_init);
-#endif /* SLAB_SUPPORTS_SYSFS */
+late_initcall(slab_late_init);
+#endif
#if defined(CONFIG_SLUB_DEBUG) && defined(CONFIG_DEBUG_FS)
static int slab_debugfs_show(struct seq_file *seq, void *v)
@@ -9964,23 +9991,16 @@ static void debugfs_slab_add(struct kmem_cache *s)
void debugfs_slab_release(struct kmem_cache *s)
{
+ if (unlikely(!slab_debugfs_root))
+ return;
+
debugfs_lookup_and_remove(s->name, slab_debugfs_root);
}
-static int __init slab_debugfs_init(void)
+static void __init slab_debugfs_root_init(void)
{
- struct kmem_cache *s;
-
slab_debugfs_root = debugfs_create_dir("slab", NULL);
-
- list_for_each_entry(s, &slab_caches, list)
- if (s->flags & SLAB_STORE_USER)
- debugfs_slab_add(s);
-
- return 0;
-
}
-__initcall(slab_debugfs_init);
#endif
/*
* The /proc/slabinfo ABI
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index ebd3ac997f64..50df73b8f747 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -342,8 +342,8 @@ static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *
*
* Any initialization done here will be overwritten by memmap_init().
*
- * hugetlb_vmemmap_init() will take care of initialization after
- * memmap_init().
+ * hugetlb_bootmem_struct_page_init() will take care of initialization
+ * after memmap_init().
*/
p = vmemmap_alloc_block_zero(PAGE_SIZE, node);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 8538659acb9e..5bb24031e913 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1893,11 +1893,11 @@ struct swap_info_struct *get_swap_device(swp_entry_t entry)
return si;
bad_nofile:
- pr_err("%s: %s%08lx\n", __func__, Bad_file, entry.val);
+ pr_err_ratelimited("%s: %s%08lx\n", __func__, Bad_file, entry.val);
out:
return NULL;
put_out:
- pr_err("%s: %s%08lx\n", __func__, Bad_offset, entry.val);
+ pr_err_ratelimited("%s: %s%08lx\n", __func__, Bad_offset, entry.val);
percpu_ref_put(&si->users);
return NULL;
}
@@ -2196,7 +2196,14 @@ void swap_free_hibernation_slot(swp_entry_t entry)
ci = swap_cluster_lock(si, offset);
__swap_cluster_put_entry(ci, offset % SWAPFILE_CLUSTER);
- __swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1);
+ /*
+ * A slot with a folio in the swap cache is freed when the folio
+ * leaves the cache, the same rule swap_put_entries_cluster() follows.
+ * Readahead can put a folio here, and freeing the slot now would
+ * leave that folio with no entry behind it.
+ */
+ if (!swp_tb_is_folio(__swap_table_get(ci, offset % SWAPFILE_CLUSTER)))
+ __swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1);
swap_cluster_unlock(ci);
/* In theory readahead might add it to the swap cache by accident */
@@ -3864,7 +3871,7 @@ int swap_dup_entry_direct(swp_entry_t entry)
si = swap_entry_to_info(entry);
if (WARN_ON_ONCE(!si)) {
- pr_err("%s%08lx\n", Bad_file, entry.val);
+ pr_err_ratelimited("%s%08lx\n", Bad_file, entry.val);
return -EINVAL;
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index f4fa227a8d7f..31c6425b5fdc 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3360,7 +3360,7 @@ struct vm_struct *remove_vm_area(const void *addr)
static inline void set_area_direct_map(const struct vm_struct *area,
int (*set_direct_map)(struct page *page))
{
- int i;
+ unsigned long i;
/* HUGE_VMALLOC passes small pages to set_direct_map */
for (i = 0; i < area->nr_pages; i++)
@@ -3376,7 +3376,7 @@ static void vm_reset_perms(struct vm_struct *area)
unsigned long start = ULONG_MAX, end = 0;
unsigned int page_order = vm_area_page_order(area);
int flush_dmap = 0;
- int i;
+ unsigned long i;
/*
* Find the start and end range of the direct mappings to make sure that
@@ -3449,10 +3449,10 @@ void vfree_atomic(const void *addr)
* Caller is responsible for unmapping (vunmap_range) and KASAN
* poisoning before calling this.
*/
-static void vm_area_free_pages(struct vm_struct *vm, unsigned int start_idx,
- unsigned int end_idx)
+static void vm_area_free_pages(struct vm_struct *vm, unsigned long start_idx,
+ unsigned long end_idx)
{
- unsigned int i;
+ unsigned long i;
if (!(vm->flags & VM_MAP_PUT_PAGES)) {
for (i = start_idx; i < end_idx; i++)
@@ -3664,12 +3664,12 @@ static inline gfp_t vmalloc_gfp_adjust(gfp_t flags, const bool large)
return flags;
}
-static inline unsigned int
+static inline unsigned long
vm_area_alloc_pages(gfp_t gfp, int nid,
- unsigned int order, unsigned int nr_pages, struct page **pages)
+ unsigned int order, unsigned long nr_pages, struct page **pages)
{
- unsigned int nr_allocated = 0;
- unsigned int nr_remaining = nr_pages;
+ unsigned long nr_allocated = 0;
+ unsigned long nr_remaining = nr_pages;
unsigned int max_attempt_order = MAX_PAGE_ORDER;
struct page *page;
int i;
@@ -3717,7 +3717,7 @@ vm_area_alloc_pages(gfp_t gfp, int nid,
if (!order) {
while (nr_allocated < nr_pages) {
unsigned int nr, nr_pages_request;
- int i;
+ unsigned long i;
/*
* A maximum allowed request is hard-coded and is 100
@@ -3725,7 +3725,7 @@ vm_area_alloc_pages(gfp_t gfp, int nid,
* long preemption off scenario in the bulk-allocator
* so the range is [1:100].
*/
- nr_pages_request = min(100U, nr_pages - nr_allocated);
+ nr_pages_request = min(100UL, nr_pages - nr_allocated);
/* memory allocation should consider mempolicy, we can't
* wrongly use nearest node when nid == NUMA_NO_NODE,
@@ -3871,12 +3871,12 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
unsigned long addr = (unsigned long)area->addr;
unsigned long size = get_vm_area_size(area);
unsigned long array_size;
- unsigned int nr_small_pages = size >> PAGE_SHIFT;
+ unsigned long nr_small_pages = size >> PAGE_SHIFT;
unsigned int page_order;
unsigned int flags;
int ret;
- array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
+ array_size = nr_small_pages * sizeof(struct page *);
/* __GFP_NOFAIL and "noblock" flags are mutually exclusive. */
if (!gfpflags_allow_blocking(gfp_mask))
@@ -4374,7 +4374,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
}
if (size <= old_size) {
- unsigned int new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
/* Zero out "freed" memory, potentially for future realloc. */
if (want_init_on_free() || want_init_on_alloc(flags))
@@ -4403,7 +4403,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
!(vm->flags & (VM_FLUSH_RESET_PERMS | VM_USERMAP)) &&
gfp_has_io_fs(flags)) {
unsigned long addr = (unsigned long)kasan_reset_tag(p);
- unsigned int old_nr_pages = vm->nr_pages;
+ unsigned long old_nr_pages = vm->nr_pages;
/*
* Use the node lock to synchronize with concurrent
@@ -4416,16 +4416,13 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
spin_unlock(&vn->busy.lock);
/* Notify kmemleak of the reduced allocation size before unmapping. */
- kmemleak_free_part(
- (void *)addr + ((unsigned long)new_nr_pages
- << PAGE_SHIFT),
- (unsigned long)(old_nr_pages - new_nr_pages)
- << PAGE_SHIFT);
+ kmemleak_free_part((void *)addr +
+ (new_nr_pages << PAGE_SHIFT),
+ (old_nr_pages - new_nr_pages)
+ << PAGE_SHIFT);
- vunmap_range(addr + ((unsigned long)new_nr_pages
- << PAGE_SHIFT),
- addr + ((unsigned long)old_nr_pages
- << PAGE_SHIFT));
+ vunmap_range(addr + (new_nr_pages << PAGE_SHIFT),
+ addr + (old_nr_pages << PAGE_SHIFT));
vm_area_free_pages(vm, new_nr_pages, old_nr_pages);
}
@@ -5250,7 +5247,7 @@ bool vmalloc_dump_obj(void *object)
struct vmap_area *va;
struct vmap_node *vn;
unsigned long addr;
- unsigned int nr_pages;
+ unsigned long nr_pages;
addr = PAGE_ALIGN((unsigned long) object);
vn = addr_to_node(addr);
@@ -5270,7 +5267,7 @@ bool vmalloc_dump_obj(void *object)
nr_pages = vm->nr_pages;
spin_unlock(&vn->busy.lock);
- pr_cont(" %u-page vmalloc region starting at %#lx allocated at %pS\n",
+ pr_cont(" %lu-page vmalloc region starting at %#lx allocated at %pS\n",
nr_pages, addr, caller);
return true;
@@ -5288,16 +5285,17 @@ bool vmalloc_dump_obj(void *object)
static void show_numa_info(struct seq_file *m, struct vm_struct *v,
unsigned int *counters)
{
- unsigned int nr;
unsigned int step = 1U << vm_area_page_order(v);
+ unsigned long i;
+ unsigned int nr;
if (!counters)
return;
memset(counters, 0, nr_node_ids * sizeof(unsigned int));
- for (nr = 0; nr < v->nr_pages; nr += step)
- counters[page_to_nid(v->pages[nr])] += step;
+ for (i = 0; i < v->nr_pages; i += step)
+ counters[page_to_nid(v->pages[i])] += step;
for_each_node_state(nr, N_HIGH_MEMORY)
if (counters[nr])
seq_printf(m, " N%u=%u", nr, counters[nr]);
@@ -5355,7 +5353,7 @@ static int vmalloc_info_show(struct seq_file *m, void *p)
seq_printf(m, " %pS", v->caller);
if (v->nr_pages)
- seq_printf(m, " pages=%d", v->nr_pages);
+ seq_printf(m, " pages=%lu", v->nr_pages);
if (v->phys_addr)
seq_printf(m, " phys=%pa", &v->phys_addr);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 56708d1d2dfd..ee3efc6213f4 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -198,6 +198,13 @@ struct scan_control {
*/
int vm_swappiness = 60;
+static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
+{
+ if (sc->proactive && sc->proactive_swappiness)
+ return *sc->proactive_swappiness;
+ return mem_cgroup_swappiness(memcg);
+}
+
#ifdef CONFIG_MEMCG
/* Returns true for reclaim through cgroup limits or cgroup interfaces. */
@@ -238,13 +245,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
#endif
return false;
}
-
-static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
-{
- if (sc->proactive && sc->proactive_swappiness)
- return *sc->proactive_swappiness;
- return mem_cgroup_swappiness(memcg);
-}
#else
static bool cgroup_reclaim(struct scan_control *sc)
{
@@ -260,11 +260,6 @@ static bool writeback_throttling_sane(struct scan_control *sc)
{
return true;
}
-
-static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg)
-{
- return READ_ONCE(vm_swappiness);
-}
#endif
static void set_task_reclaim_state(struct task_struct *task,
@@ -4573,7 +4568,6 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent,
static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc,
int tier_idx)
{
- bool success;
int gen = folio_lru_gen(folio);
int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
@@ -4585,15 +4579,9 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
- /* unevictable */
- if (!folio_evictable(folio)) {
- success = lru_gen_del_folio(lruvec, folio, true);
- VM_WARN_ON_ONCE_FOLIO(!success, folio);
- folio_set_unevictable(folio);
- lruvec_add_folio(lruvec, folio);
- __count_vm_events(UNEVICTABLE_PGCULLED, delta);
- return true;
- }
+ /* unevictable: let it through and the generic path will cull it */
+ if (!folio_evictable(folio))
+ return false;
/* promoted */
if (gen != lru_gen_from_seq(lrugen->min_seq[type])) {
@@ -4843,11 +4831,9 @@ retry:
list_for_each_entry_safe_reverse(folio, next, &list, lru) {
DEFINE_MIN_SEQ(lruvec);
- if (!folio_evictable(folio)) {
- list_del(&folio->lru);
- folio_putback_lru(folio);
+ /* move_folios_to_lru() culls unevictable folios via folio_putback_lru() */
+ if (!folio_evictable(folio))
continue;
- }
/* retry folios that may have missed folio_rotate_reclaimable() */
if (!skip_retry && !folio_test_active(folio) && !folio_mapped(folio) &&
@@ -5927,7 +5913,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/mm/zsmalloc.c b/mm/zsmalloc.c
index 83f5820c45f9..41b03a9ecec2 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -478,6 +478,11 @@ static int get_size_class_index(int size)
return min_t(int, ZS_SIZE_CLASSES - 1, idx);
}
+static struct size_class *lookup_size_class(struct zs_pool *pool, size_t size)
+{
+ return pool->size_class[get_size_class_index(size + ZS_HANDLE_SIZE)];
+}
+
static inline void class_stat_add(struct size_class *class, int type,
unsigned long cnt)
{
@@ -1021,7 +1026,7 @@ unsigned int zs_lookup_class_index(struct zs_pool *pool, unsigned int size)
{
struct size_class *class;
- class = pool->size_class[get_size_class_index(size)];
+ class = lookup_size_class(pool, size);
return class->index;
}
@@ -1311,9 +1316,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp,
if (!handle)
return (unsigned long)ERR_PTR(-ENOMEM);
- /* extra space in chunk to keep the handle */
- size += ZS_HANDLE_SIZE;
- class = pool->size_class[get_size_class_index(size)];
+ class = lookup_size_class(pool, size);
/* class->lock effectively protects the zpage migration */
spin_lock(&class->lock);
diff --git a/mm/zswap.c b/mm/zswap.c
index 761cd699e0a3..0bdb999e46b6 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1356,11 +1356,12 @@ static void shrink_worker(struct work_struct *w)
} while (memcg && !mem_cgroup_tryget_online(memcg));
spin_unlock(&zswap_shrink_lock);
- if (!memcg) {
- /*
- * Continue shrinking without incrementing failures if
- * we found candidate memcgs in the last tree walk.
- */
+ /*
+ * A NULL memcg ends a full hierarchy pass (except when memcg is
+ * disabled, where it is always NULL: fall through to the root LRU).
+ * Count a failure only if the last pass found no candidates.
+ */
+ if (!memcg && !mem_cgroup_disabled()) {
if (!attempts && ++failures == MAX_RECLAIM_RETRIES)
break;
@@ -1379,7 +1380,7 @@ static void shrink_worker(struct work_struct *w)
* and failures.
*/
if (ret == -ENOENT)
- continue;
+ goto resched;
++attempts;
if (ret && ++failures == MAX_RECLAIM_RETRIES)
diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c
index 1de5f9df6eec..a55696820b22 100644
--- a/net/bluetooth/eir.c
+++ b/net/bluetooth/eir.c
@@ -369,6 +369,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))) {
@@ -381,7 +382,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 b1f911fd4ad6..7e4d737a795f 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1381,7 +1381,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 5ba9fe8261ec..f346505c3f88 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -4091,7 +4091,7 @@ static int 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);
}
return err;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 2b41ad873c16..f502be73d8c5 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5724,10 +5724,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
- /* 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 8ccc3b22ef5b..f44f02ddb94e 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5374,6 +5374,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);
@@ -5558,6 +5559,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 aa2ce78f56a2..75bfd5938b2e 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -2277,6 +2277,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/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 9cdfea666a2c..5d150e8623d5 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -2213,9 +2213,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)) {
@@ -2247,6 +2251,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/ceph/osd_client.c b/net/ceph/osd_client.c
index 28d76c2f6b3e..f36ce5ae7568 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>
@@ -5802,6 +5803,31 @@ static inline void convert_extent_map(struct ceph_sparse_read *sr)
}
#endif
+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)
@@ -5852,6 +5878,10 @@ next_op:
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 d6282f0bcff8..cf34b35c9a90 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -517,6 +517,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/skbuff.c b/net/core/skbuff.c
index ba3dbac80fb4..71bb4a3b5776 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1417,10 +1417,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);
}
diff --git a/net/core/xdp.c b/net/core/xdp.c
index 0194e69da339..1d679e8fd649 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -585,7 +585,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();
@@ -602,7 +602,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/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 9d114bd575f9..5b1f180485d4 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -317,7 +317,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/ipip.c b/net/ipv4/ipip.c
index b643194f57d2..ddf62b45566b 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -248,7 +248,7 @@ static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto)
tun_dst = ip_tun_rx_dst(skb, flags, 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 1d9a4ac14fce..e5f2b1c6150d 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2213,6 +2213,9 @@ int ip_mr_input(struct sk_buff *skb)
if (IPCB(skb)->flags & IPSKB_FORWARDED)
goto dont_forward;
+ if (!local)
+ skb_orphan(skb);
+
mrt = ipmr_rt_fib_lookup(net, skb);
if (IS_ERR(mrt)) {
kfree_skb(skb);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 9c677eb1d1a6..51941ad656a3 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_buff *skb)
(__be32 *)addr);
}
-static int ipv6_srh_rcv(struct sk_buff *skb)
+static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
{
struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(skb->dev);
struct ipv6_sr_hdr *hdr;
- struct inet6_dev *idev;
struct in6_addr *addr;
int accept_seg6;
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
- idev = __in6_dev_get(skb->dev);
- if (!idev) {
- kfree_skb(skb);
- return -1;
- }
-
accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled),
READ_ONCE(idev->cnf.seg6_enabled));
@@ -485,12 +478,11 @@ looped_back:
return -1;
}
-static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
+static int ipv6_rpl_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
{
struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(skb->dev);
- struct inet6_dev *idev;
struct ipv6hdr *oldhdr;
unsigned int chdr_len;
unsigned char *buf;
@@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
u64 n = 0;
u32 r;
- idev = __in6_dev_get(skb->dev);
-
accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
READ_ONCE(idev->cnf.rpl_seg_enabled));
if (!accept_rpl_seg) {
@@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
switch (hdr->type) {
case IPV6_SRCRT_TYPE_4:
/* segment routing */
- return ipv6_srh_rcv(skb);
+ if (!idev)
+ goto disabled;
+ return ipv6_srh_rcv(skb, idev);
case IPV6_SRCRT_TYPE_3:
/* rpl segment routing */
- return ipv6_rpl_srh_rcv(skb);
+ if (!idev)
+ goto disabled;
+ return ipv6_rpl_srh_rcv(skb, idev);
default:
break;
}
@@ -837,6 +831,10 @@ unknown_rh:
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
(&hdr->type) - skb_network_header(skb));
return -1;
+
+disabled:
+ kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
+ return -1;
}
static const struct inet6_protocol rthdr_protocol = {
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index b843116e9b70..200d0ba1a40e 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1137,13 +1137,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;
@@ -1171,8 +1169,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_input.c b/net/ipv6/ip6_input.c
index 8972863c93ee..d332ec60f915 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -622,6 +622,7 @@ int ip6_mc_input(struct sk_buff *skb)
if (deliver) {
skb2 = skb_clone(skb, GFP_ATOMIC);
} else {
+ skb_orphan(skb);
skb2 = skb;
skb = NULL;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index ebf83f090376..cc96bb8b706e 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1236,19 +1236,8 @@ route_lookup:
*/
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)
@@ -1525,8 +1514,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 e2056bd0df3c..a698f322db1c 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -6029,7 +6029,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 95ea0b62729a..7b5212220185 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 a38b24fb8384..19b7fa8d1a2a 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1131,7 +1131,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,
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index ea047bab65e7..4e5cc9da6e06 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2079,6 +2079,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/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index 59457c0c14aa..fbdd3d055dbc 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -248,8 +248,8 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
kfree(tunnel);
goto out;
}
- ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
- L2TP_CMD_TUNNEL_CREATE);
+ l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
+ L2TP_CMD_TUNNEL_CREATE);
l2tp_tunnel_put(tunnel);
out:
@@ -305,8 +305,8 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info
goto out;
}
- ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
- tunnel, L2TP_CMD_TUNNEL_MODIFY);
+ l2tp_tunnel_notify(&l2tp_nl_family, info,
+ tunnel, L2TP_CMD_TUNNEL_MODIFY);
l2tp_tunnel_put(tunnel);
@@ -645,8 +645,8 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
session = l2tp_session_get(net, tunnel->sock, tunnel->version,
tunnel_id, session_id);
if (session) {
- ret = l2tp_session_notify(&l2tp_nl_family, info, session,
- L2TP_CMD_SESSION_CREATE);
+ l2tp_session_notify(&l2tp_nl_family, info, session,
+ L2TP_CMD_SESSION_CREATE);
l2tp_session_put(session);
}
}
@@ -710,8 +710,8 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf
if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
- ret = l2tp_session_notify(&l2tp_nl_family, info,
- session, L2TP_CMD_SESSION_MODIFY);
+ l2tp_session_notify(&l2tp_nl_family, info,
+ session, L2TP_CMD_SESSION_MODIFY);
l2tp_session_put(session);
diff --git a/net/mctp/route.c b/net/mctp/route.c
index 1f3dccbb7aed..b19c63a5691a 100644
--- a/net/mctp/route.c
+++ b/net/mctp/route.c
@@ -998,14 +998,29 @@ int mctp_route_lookup(struct net *net, unsigned int dnet,
mtu = mtu ?: rt->mtu;
if (rt->dst_type == MCTP_ROUTE_DIRECT) {
- mctp_eid_t saddr = mctp_dev_saddr(rt->dev);
+ mctp_eid_t saddr;
+
+ /* rt->dev may be going away concurrently: its last
+ * reference is dropped in mctp_dev_put(), which frees
+ * mdev->addrs that mctp_dev_saddr() reads, and
+ * mctp_dst_from_route() takes a reference on it. Pin
+ * it before use, and skip a device that is already
+ * dead rather than resurrecting it.
+ */
+ if (!refcount_inc_not_zero(&rt->dev->refs))
+ break;
+
+ saddr = mctp_dev_saddr(rt->dev);
/* cannot do gateway-ed routes without a src */
- if (saddr == MCTP_ADDR_NULL && depth != 0)
+ if (saddr == MCTP_ADDR_NULL && depth != 0) {
+ mctp_dev_put(rt->dev);
break;
+ }
if (dst)
mctp_dst_from_route(dst, daddr, saddr, mtu, rt);
+ mctp_dev_put(rt->dev);
rc = 0;
break;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 961be5054a03..17b78dcbf8ab 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -221,6 +221,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),
@@ -231,6 +232,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/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/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 95697d4e16e6..37c90580ecb8 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -933,10 +933,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);
@@ -1585,40 +1589,55 @@ 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_obj(*ovs_net->ct_limit_info);
- if (!ovs_net->ct_limit_info)
+ info = kmalloc_obj(*info);
+ 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_objs(struct hlist_head, CT_LIMIT_HASH_BUCKETS);
- 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, sizeof(u32));
+ info->data = nf_conncount_init(net, 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, info->data);
for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) {
struct hlist_head *head = &info->limits[i];
@@ -1626,7 +1645,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);
@@ -1665,12 +1684,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);
@@ -1679,6 +1699,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(
@@ -1695,6 +1716,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();
}
@@ -1709,12 +1731,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);
@@ -1723,6 +1746,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(
@@ -1730,6 +1754,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();
}
@@ -1773,6 +1798,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,
@@ -1796,12 +1822,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;
}
@@ -1816,6 +1840,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)
@@ -1828,19 +1853,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;
}
@@ -1850,7 +1872,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,
@@ -1863,8 +1884,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;
@@ -1884,7 +1905,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,
@@ -1897,8 +1917,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;
@@ -1918,7 +1938,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,
@@ -1932,18 +1952,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);
@@ -2001,6 +2022,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;
@@ -2010,18 +2032,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 ae69b2cabab9..b601e4859de9 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -285,6 +285,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;
}
@@ -601,8 +602,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);
@@ -1473,33 +1472,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:
@@ -2756,6 +2756,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;
@@ -2766,7 +2773,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);
@@ -2790,6 +2797,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 696640e88fa7..b2c2b8da12d4 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -164,7 +164,10 @@ struct dp_upcall_info {
* 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: A hash table of conntrack zone connection limits.
+ * @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 {
@@ -172,7 +175,8 @@ struct ovs_net {
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
bool xt_label;
};
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index ef2b4bf51564..4539dc2c6d38 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1278,7 +1278,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/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 424f10a6fdba..94716406d602 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1332,6 +1332,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 34ffe6c945a4..e4354d58f20b 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 @@ out:
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 00403175b740..cff910cedbfc 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -3233,7 +3233,8 @@ 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.h b/net/smc/smc.h
index 52145df83f6e..427b6d63b993 100644
--- a/net/smc/smc.h
+++ b/net/smc/smc.h
@@ -277,9 +277,9 @@ struct smc_connection {
* 0 for SMC-R, 32 for SMC-D
*/
u64 peer_token; /* SMC-D token of peer */
- u8 killed : 1; /* abnormal termination */
- u8 freed : 1; /* normal termination */
- u8 out_of_sync : 1; /* out of sync with peer */
+ u8 killed; /* abnormal termination */
+ u8 freed; /* normal termination */
+ u8 out_of_sync; /* out of sync with peer */
};
struct smc_sock { /* smc sock container */
diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
index b4208cb186c5..04aedd957543 100644
--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -1148,13 +1148,13 @@ again:
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;
@@ -1209,14 +1209,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,
@@ -1268,11 +1270,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)
@@ -1525,12 +1526,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 aa6d83af55ed..481b0495b0d3 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -157,6 +157,7 @@ struct smc_llc_msg_confirm_rkey { /* type 0x06 */
};
#define SMC_LLC_DEL_RKEY_MAX 8
+#define SMC_LLC_DEL_RKEY_V2_INLINE 9
#define SMC_LLC_FLAG_RKEY_RETRY 0x10
#define SMC_LLC_FLAG_RKEY_NEG 0x20
@@ -177,6 +178,15 @@ struct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */
__be32 rkey[];
};
+/* the leading rkeys of a DELETE_RKEY_V2 fit into union smc_llc_msg */
+struct smc_llc_msg_delete_rkey_v2_inline { /* type 0x29 */
+ struct smc_llc_hdr hd;
+ u8 num_rkeys;
+ u8 num_inval_rkeys;
+ u8 reserved[2];
+ __be32 rkey[SMC_LLC_DEL_RKEY_V2_INLINE];
+};
+
union smc_llc_msg {
struct smc_llc_msg_confirm_link confirm_link;
struct smc_llc_msg_add_link add_link;
@@ -186,6 +196,7 @@ union smc_llc_msg {
struct smc_llc_msg_confirm_rkey confirm_rkey;
struct smc_llc_msg_delete_rkey delete_rkey;
+ struct smc_llc_msg_delete_rkey_v2_inline delete_rkey_v2;
struct smc_llc_msg_test_link test_link;
struct {
@@ -194,15 +205,25 @@ union smc_llc_msg {
} raw;
};
+static_assert(SMC_LLC_DEL_RKEY_V2_INLINE ==
+ (sizeof(union smc_llc_msg) -
+ offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)) /
+ sizeof(__be32));
+static_assert(offsetof(struct smc_llc_msg_delete_rkey_v2_inline, rkey) ==
+ offsetof(struct smc_llc_msg_delete_rkey_v2, rkey));
+
#define SMC_LLC_FLAG_RESP 0x80
struct smc_llc_qentry {
struct list_head list;
struct smc_link *link;
+ u16 body_len;
union smc_llc_msg msg;
+ u8 body[] __counted_by(body_len);
};
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+ u32 byte_len);
struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
{
@@ -998,15 +1019,20 @@ static int smc_llc_cli_conf_link(struct smc_link *link,
static void smc_llc_save_add_link_rkeys(struct smc_link *link,
struct smc_link *link_new,
- u8 *llc_msg)
+ struct smc_llc_qentry *qentry)
{
+ const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt);
struct smc_llc_msg_add_link_v2_ext *ext;
struct smc_link_group *lgr = link->lgr;
int max, i;
- ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
- SMC_WR_TX_SIZE);
+ /* the rkey count itself is only there if enough bytes arrived */
+ if (qentry->body_len < rt_off)
+ return;
+ ext = (struct smc_llc_msg_add_link_v2_ext *)qentry->body;
max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ max = min_t(u32, max, (qentry->body_len - rt_off) /
+ sizeof(ext->rt[0]));
down_write(&lgr->rmbs_lock);
for (i = 0; i < max; i++) {
smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
@@ -1099,9 +1125,7 @@ int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
if (rc)
goto out_clear_lnk;
if (lgr->smc_version == SMC_V2) {
- u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
- (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
- smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg);
+ smc_llc_save_add_link_rkeys(link, lnk_new, qentry);
} else {
rc = smc_llc_cli_rkey_exchange(link, lnk_new);
if (rc) {
@@ -1481,7 +1505,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
}
add_llc = &qentry->msg.add_link;
if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
- smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+ smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
rc = -ENOLINK;
goto out_err;
}
@@ -1492,7 +1516,8 @@ int smc_llc_srv_add_link(struct smc_link *link,
lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
}
smc_llc_save_add_link_info(link_new, add_llc);
- smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+ /* add_llc still points into qentry, so only detach it here */
+ smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
rc = smc_ib_ready_link(link_new);
if (rc)
@@ -1501,9 +1526,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
if (rc)
goto out_err;
if (lgr->smc_version == SMC_V2) {
- u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
- (u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc;
- smc_llc_save_add_link_rkeys(link, link_new, llc_msg);
+ smc_llc_save_add_link_rkeys(link, link_new, qentry);
} else {
rc = smc_llc_srv_rkey_exchange(link, link_new);
if (rc)
@@ -1512,14 +1535,14 @@ int smc_llc_srv_add_link(struct smc_link *link,
rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
if (rc)
goto out_err;
- kfree(ini);
- return 0;
+ goto out;
out_err:
if (link_new) {
link_new->state = SMC_LNK_INACTIVE;
smcr_link_clear(link_new, false);
}
out:
+ kfree(qentry);
kfree(ini);
if (send_req_add_link_resp)
smc_llc_send_req_add_link_response(req_qentry);
@@ -1552,7 +1575,8 @@ void smc_llc_add_link_local(struct smc_link *link)
add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK;
smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc));
/* no dev and port needed */
- smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
+ smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc,
+ sizeof(union smc_llc_msg));
}
/* worker to process an add link message */
@@ -1588,7 +1612,8 @@ void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
del_llc.link_num = del_link_id;
del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
- smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
+ smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc,
+ sizeof(union smc_llc_msg));
}
static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
@@ -1810,19 +1835,28 @@ static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
link = qentry->link;
if (lgr->smc_version == SMC_V2) {
- struct smc_llc_msg_delete_rkey_v2 *llcv2;
+ struct smc_llc_msg_delete_rkey_v2_inline *llcv2;
- if (smc_link_shared_v2_rxbuf(link)) {
- memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
- llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
- } else {
- llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc;
- }
+ /* The leading SMC_LLC_DEL_RKEY_V2_INLINE rkeys are declared in
+ * the message itself, any further ones were received into
+ * qentry->body.
+ */
+ llcv2 = &qentry->msg.delete_rkey_v2;
llcv2->num_inval_rkeys = 0;
max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ max = min_t(u32, max, SMC_LLC_DEL_RKEY_V2_INLINE +
+ qentry->body_len / sizeof(__be32));
for (i = 0; i < max; i++) {
- if (smc_rtoken_delete(link, llcv2->rkey[i]))
+ __be32 rkey;
+
+ if (i < SMC_LLC_DEL_RKEY_V2_INLINE)
+ rkey = llcv2->rkey[i];
+ else
+ memcpy(&rkey, qentry->body +
+ (i - SMC_LLC_DEL_RKEY_V2_INLINE) *
+ sizeof(rkey), sizeof(rkey));
+ if (smc_rtoken_delete(link, rkey))
llcv2->num_inval_rkeys++;
}
memset(&llc->rkey[0], 0, sizeof(llc->rkey));
@@ -2064,18 +2098,52 @@ assign:
wake_up(&link->lgr->llc_msg_waiter);
}
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
+/* the longest tail either reader of qentry->body can use */
+static u32 smc_llc_max_body_len(union smc_llc_msg *llc)
+{
+ switch (llc->raw.hdr.common.llc_type) {
+ case SMC_LLC_ADD_LINK:
+ return offsetof(struct smc_llc_msg_add_link_v2_ext, rt) +
+ SMC_LLC_RKEYS_PER_MSG_V2 *
+ sizeof(struct smc_llc_msg_add_link_cont_rt);
+ case SMC_LLC_DELETE_RKEY:
+ return (SMC_LLC_RKEYS_PER_MSG_V2 -
+ SMC_LLC_DEL_RKEY_V2_INLINE) * sizeof(__be32);
+ default:
+ return 0;
+ }
+}
+
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+ u32 byte_len)
{
struct smc_link_group *lgr = link->lgr;
struct smc_llc_qentry *qentry;
unsigned long flags;
+ u16 body_len = 0;
+
+ /* V2 messages can be longer than the inline union smc_llc_msg. Carry
+ * the remainder in the qentry itself, so that its lifetime and its
+ * length match the message the peer actually sent.
+ */
+ if (lgr->smc_version == SMC_V2 && byte_len > SMC_WR_TX_SIZE)
+ body_len = min_t(u32, byte_len, SMC_WR_BUF_V2_SIZE) -
+ SMC_WR_TX_SIZE;
+ body_len = min_t(u32, body_len, smc_llc_max_body_len(llc));
- qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
+ qentry = kmalloc_flex(*qentry, body, body_len, GFP_ATOMIC);
if (!qentry)
return;
+ qentry->body_len = body_len;
qentry->link = link;
INIT_LIST_HEAD(&qentry->list);
memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
+ if (body_len) {
+ u8 *src = smc_link_shared_v2_rxbuf(link) ?
+ (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
+
+ memcpy(qentry->body, src + SMC_WR_TX_SIZE, body_len);
+ }
/* process responses immediately */
if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) &&
@@ -2107,7 +2175,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
return; /* invalid message */
}
- smc_llc_enqueue(link, llc);
+ smc_llc_enqueue(link, llc, wc->byte_len);
}
/***************************** worker, utils *********************************/
diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
index 5c9e4d8b57de..197fddc6271f 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/smc/smc_wr.c b/net/smc/smc_wr.c
index 59c92b46945c..97ba46893b17 100644
--- a/net/smc/smc_wr.c
+++ b/net/smc/smc_wr.c
@@ -602,9 +602,9 @@ static void smc_wr_init_sge(struct smc_link *lnk)
/* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE.
* Each ib_recv_wr gets 2 sges, the second one is a spillover buffer
- * and the same buffer for all sges. When a larger message arrived then
- * the content of the first small sge is copied to the beginning of
- * the larger spillover buffer, allowing easy data mapping.
+ * and the same buffer for all sges. The spillover sge starts at
+ * SMC_WR_TX_SIZE, so the leading bytes of that buffer are never
+ * written.
*/
for (i = 0; i < lnk->wr_rx_cnt; i++) {
int x = i * lnk->wr_rx_sge_cnt;
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 9d3fb6848f40..8ddc65e894da 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -2072,7 +2072,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_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index b5fb70419faa..4d12d49434c2 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -89,6 +89,9 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
dprintk("RPC: %s\n", __func__);
+ if (read_token->len < GSS_KRB5_TOK_HDR_LEN + cksum_len)
+ return GSS_S_DEFECTIVE_TOKEN;
+
memcpy(&be16_ptr, (char *) ptr, 2);
if (be16_to_cpu(be16_ptr) != KG2_TOK_MIC)
return GSS_S_DEFECTIVE_TOKEN;
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index ac4b32df42b9..d3f61c4b5a13 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -73,6 +73,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);
@@ -85,6 +87,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);
}
@@ -154,6 +158,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)
@@ -220,14 +227,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 0fa4778620d9..b7f70b1adb18 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -121,12 +121,6 @@ out:
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/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index fceee648d545..0549edae1ebe 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
return 0;
out_free_groups:
- groups_free(creds->cr_group_info);
+ put_group_info(creds->cr_group_info);
+ creds->cr_group_info = NULL;
return err;
}
@@ -230,6 +231,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
struct gssx_option_array *oa)
{
struct svc_cred *creds;
+ bool creds_decoded = false;
u32 count, i;
__be32 *p;
int err;
@@ -242,12 +244,12 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
/* we recognize only 1 currently: CREDS_VALUE */
- oa->count = 1;
-
oa->data = kmalloc_obj(struct gssx_option);
if (!oa->data)
return -ENOMEM;
+ oa->count = 1;
+
creds = kzalloc_obj(struct svc_cred);
if (!creds) {
err = -ENOMEM;
@@ -280,9 +282,14 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
if (length == sizeof(CREDS_VALUE) &&
memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
/* We have creds here. parse them */
+ if (creds_decoded) {
+ err = -EINVAL;
+ goto free_creds;
+ }
err = gssx_dec_linux_creds(xdr, creds);
if (err)
goto free_creds;
+ creds_decoded = true;
oa->data[0].value.len = 1; /* presence */
} else {
/* consume uninteresting buffer */
@@ -294,8 +301,10 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
free_creds:
+ free_svc_cred(creds);
kfree(creds);
free_oa:
+ oa->count = 0;
kfree(oa->data);
oa->data = NULL;
return err;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index d14209031e18..967e9d53080d 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -949,6 +949,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;
@@ -1466,7 +1468,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;
}
@@ -1573,6 +1574,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;
@@ -1942,6 +1946,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/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
index 0ffa4d01a938..1482b06e0f38 100644
--- a/net/sunrpc/backchannel_rqst.c
+++ b/net/sunrpc/backchannel_rqst.c
@@ -25,20 +25,39 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt)
}
/*
- * Helper function to nullify backchannel server pointer in transport.
- * We need to synchronize setting the pointer to NULL (done so after
- * the backchannel server is shutdown) with the usage of that pointer
- * by the backchannel request processing routines
- * xprt_complete_bc_request() and rpcrdma_bc_receive_call().
+ * Close the backchannel producer side, drain any requests still
+ * queued on sv_cb_list, then destroy the callback service.
*/
void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv)
{
- spin_lock(&xprt->bc_pa_lock);
+ struct svc_serv *bc_serv = *serv;
+ struct rpc_rqst *req;
+
+ xprt_svc_shutdown_bc(xprt);
+ while ((req = lwq_dequeue(&bc_serv->sv_cb_list, struct rpc_rqst,
+ rq_bc_list)) != NULL) {
+ atomic_dec(&req->rq_xprt->bc_slot_count);
+ xprt_free_bc_request(req);
+ }
svc_destroy(serv);
+}
+EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc);
+
+/*
+ * Clear the backchannel server pointer in the transport. The NULL
+ * store is serialized under bc_pa_lock against readers of
+ * xprt->bc_serv in xprt_complete_bc_request() and
+ * rpcrdma_bc_receive_call(). Clearing it before the callback service
+ * is stopped prevents a producer from enqueueing onto a service that
+ * is being torn down.
+ */
+void xprt_svc_shutdown_bc(struct rpc_xprt *xprt)
+{
+ spin_lock(&xprt->bc_pa_lock);
xprt->bc_serv = NULL;
spin_unlock(&xprt->bc_pa_lock);
}
-EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc);
+EXPORT_SYMBOL_GPL(xprt_svc_shutdown_bc);
/*
* Helper routines that track the number of preallocation elements
@@ -393,7 +412,12 @@ void xprt_enqueue_bc_request(struct rpc_rqst *req)
if (bc_serv) {
lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
+ spin_unlock(&xprt->bc_pa_lock);
+ return;
}
spin_unlock(&xprt->bc_pa_lock);
+
+ atomic_dec(&xprt->bc_slot_count);
+ xprt_free_bc_request(req);
}
EXPORT_SYMBOL_GPL(xprt_enqueue_bc_request);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 391037f15292..1bc04109d213 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -430,10 +430,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/clnt.c b/net/sunrpc/clnt.c
index efa26899bc7d..6cedc824cf82 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -96,7 +96,10 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
{
- rpc_remove_client_dir(clnt);
+ if (clnt->pipefs_sb) {
+ rpc_remove_client_dir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
}
static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
@@ -177,19 +180,28 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
}
static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
- struct super_block *sb)
+ struct super_block *sb)
{
+ int err = 0;
+
switch (event) {
case RPC_PIPEFS_MOUNT:
- return rpc_setup_pipedir_sb(sb, clnt);
+ clnt->pipefs_sb = sb;
+ err = rpc_setup_pipedir_sb(sb, clnt);
+ if (err)
+ clnt->pipefs_sb = NULL;
+ break;
case RPC_PIPEFS_UMOUNT:
- __rpc_clnt_remove_pipedir(clnt);
+ if (clnt->pipefs_sb == sb) {
+ __rpc_clnt_remove_pipedir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
break;
default:
printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
return -ENOTSUPP;
}
- return 0;
+ return err;
}
static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index ab88ce46afb5..1a3884a0376a 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -57,6 +57,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 ae9ec4bf34f7..f85914ccb01a 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -352,7 +352,7 @@ static int svc_pool_map_get_node(unsigned int pidx)
if (m->mode == SVC_POOL_PERNODE)
return m->pool_to[pidx];
}
- return numa_mem_id();
+ return NUMA_NO_NODE;
}
/*
* Set the given thread's cpus_allowed mask so that it
@@ -402,6 +402,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;
@@ -414,8 +415,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;
+
+ if (++pidx >= serv->sv_nrpools)
+ pidx = 0;
+ }
- return &serv->sv_pools[pidx % serv->sv_nrpools];
+ /* No pool has any threads; nothing can service the transport. */
+ return &serv->sv_pools[pidx];
}
static int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
@@ -476,6 +503,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
*/
@@ -540,12 +596,18 @@ __svc_create(struct svc_program *prog, int nprogs, struct svc_stat *stats,
INIT_LIST_HEAD(&pool->sp_all_threads);
init_llist_head(&pool->sp_idle_threads);
- 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;
}
/**
@@ -624,9 +686,7 @@ svc_destroy(struct svc_serv **servp)
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);
@@ -683,6 +743,15 @@ svc_release_buffer(struct svc_rqst *rqstp)
}
}
+static void svc_rqst_free_rcu(struct rcu_head *head)
+{
+ struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head);
+
+ kfree(rqstp->rq_resp);
+ kfree(rqstp->rq_argp);
+ kfree(rqstp);
+}
+
static void
svc_rqst_free(struct svc_rqst *rqstp)
{
@@ -691,10 +760,8 @@ svc_rqst_free(struct svc_rqst *rqstp)
svc_release_buffer(rqstp);
if (rqstp->rq_scratch_folio)
folio_put(rqstp->rq_scratch_folio);
- kfree(rqstp->rq_resp);
- kfree(rqstp->rq_argp);
kfree(rqstp->rq_auth_data);
- kfree_rcu(rqstp, rq_rcu_head);
+ call_rcu(&rqstp->rq_rcu_head, svc_rqst_free_rcu);
}
static struct svc_rqst *
@@ -711,7 +778,9 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
rqstp->rq_server = serv;
rqstp->rq_pool = pool;
- rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, node);
+ rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0,
+ node == NUMA_NO_NODE ?
+ numa_mem_id() : node);
if (!rqstp->rq_scratch_folio)
goto out_enomem;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index fa6a30b5f046..cb2ef428651f 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -2049,7 +2049,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/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c
index de49ad02053d..69166d5d9987 100644
--- a/net/sunrpc/xprtrdma/ib_client.c
+++ b/net/sunrpc/xprtrdma/ib_client.c
@@ -51,7 +51,11 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)
* to be invoked when the device is removed, unless this notification
* is unregistered first.
*
- * On failure, a negative errno is returned.
+ * On failure, a negative errno is returned. rn->rn_done is left
+ * NULL on every failure path (it is assigned only after xa_alloc
+ * and kref_get have both succeeded), so the @rn may safely be
+ * passed to rpcrdma_rn_unregister() without a separate
+ * registered/unregistered flag in the caller.
*/
int rpcrdma_rn_register(struct ib_device *device,
struct rpcrdma_notification *rn,
@@ -83,6 +87,10 @@ static void rpcrdma_rn_release(struct kref *kref)
* rpcrdma_rn_unregister - stop device removal notifications
* @device: monitored device
* @rn: notification object that no longer wishes to be notified
+ *
+ * It is safe to call this on an @rn whose registration never
+ * completed or failed; rn_done == NULL is treated as
+ * never-registered and the call is a no-op.
*/
void rpcrdma_rn_unregister(struct ib_device *device,
struct rpcrdma_notification *rn)
@@ -92,6 +100,20 @@ void rpcrdma_rn_unregister(struct ib_device *device,
if (!rd)
return;
+ /*
+ * rn_done is the registration sentinel: rpcrdma_rn_register
+ * assigns it last, after xa_alloc and kref_get have both
+ * succeeded. A NULL rn_done means this notification was
+ * never registered (or its registration failed) or has
+ * already been unregistered, and the call is a no-op.
+ * Without this guard, rn_index == 0 from a kzalloc'd
+ * parent would erase another caller's slot 0 and underflow
+ * rd_kref.
+ */
+ if (!rn->rn_done)
+ return;
+ rn->rn_done = NULL;
+
trace_rpcrdma_client_unregister(device, rn);
xa_erase(&rd->rd_xa, rn->rn_index);
kref_put(&rd->rd_kref, rpcrdma_rn_release);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_pcl.c b/net/sunrpc/xprtrdma/svc_rdma_pcl.c
index 1f8f7dad8b6f..18d1045799ce 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_pcl.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_pcl.c
@@ -213,7 +213,6 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
chunk = pcl_alloc_chunk(segcount, 0);
if (!chunk)
return false;
- list_add_tail(&chunk->ch_list, &pcl->cl_chunks);
for (j = 0; j < segcount; j++) {
segment = &chunk->ch_segments[j];
@@ -225,6 +224,7 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
chunk->ch_length += segment->rs_length;
chunk->ch_segcount++;
}
+ list_add_tail(&chunk->ch_list, &pcl->cl_chunks);
}
return true;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index fe9bf0371b6e..8e71ceec11df 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -440,11 +440,14 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp,
* to the first byte past the Read list. rc_read_pcl and
* rc_call_pcl cl_count fields are set to the number of
* Read segments in the list.
- * %false: Read list is corrupt. @rctxt's xdr_stream is left in an
- * unknown state.
+ * %false: Read list is corrupt or exceeds the page budget. @rctxt's
+ * xdr_stream is left in an unknown state.
*/
static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
{
+ unsigned int maxlen = rctxt->rc_maxpages << PAGE_SHIFT;
+ unsigned int total_len = 0;
+
rctxt->rc_call_pcl.cl_count = 0;
rctxt->rc_read_pcl.cl_count = 0;
while (xdr_item_is_present(p)) {
@@ -458,6 +461,11 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
xdr_decode_read_segment(p, &position, &handle,
&length, &offset);
+ if (length > maxlen)
+ return false;
+ total_len += length;
+ if (PAGE_ALIGN(total_len) > maxlen)
+ return false;
if (position) {
if (position & 3)
return false;
@@ -508,10 +516,13 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt)
return false;
/* Before trusting the segcount value enough to use it in
- * a computation, perform a simple range check. This is an
- * arbitrary but sensible limit (ie, not architectural).
+ * a computation, perform a simple range check. A zero
+ * segcount describes no remote buffer and is rejected so
+ * downstream consumers never see a degenerate ch_segcount==0
+ * chunk. The upper bound is an arbitrary but sensible limit
+ * (ie, not architectural).
*/
- if (unlikely(segcount > rctxt->rc_maxpages))
+ if (segcount == 0 || unlikely(segcount > rctxt->rc_maxpages))
return false;
p = xdr_inline_decode(&rctxt->rc_stream,
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index f7fd22cc4a59..9aaaade99e6e 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -795,7 +795,7 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
len -= seg_len;
if (len && ((head->rc_curpage + 1) > rqstp->rq_maxpages))
- goto out_overrun;
+ goto out_put;
}
ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
@@ -809,7 +809,8 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
cc->cc_sqecount += ret;
return 0;
-out_overrun:
+out_put:
+ svc_rdma_put_rw_ctxt(rdma, ctxt);
trace_svcrdma_page_overrun_err(&cc->cc_cid, head->rc_curpage);
return -EINVAL;
}
@@ -847,7 +848,7 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp,
* svc_rdma_copy_inline_range - Copy part of the inline content into pages
* @rqstp: RPC transaction context
* @head: context for ongoing I/O
- * @offset: offset into the Receive buffer of region to copy
+ * @offset: offset into the inline content of region to copy
* @remaining: length of region to copy
*
* Take a page at a time from rqstp->rq_pages and copy the inline
@@ -864,9 +865,13 @@ static int svc_rdma_copy_inline_range(struct svc_rqst *rqstp,
unsigned int offset,
unsigned int remaining)
{
- unsigned char *dst, *src = head->rc_recv_buf;
+ unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base;
+ unsigned int inline_len = head->rc_saved_arg.head[0].iov_len;
unsigned int page_no, numpages;
+ if (offset > inline_len || remaining > inline_len - offset)
+ return -EINVAL;
+
numpages = PAGE_ALIGN(head->rc_pageoff + remaining) >> PAGE_SHIFT;
for (page_no = 0; page_no < numpages; page_no++) {
unsigned int page_len;
@@ -917,9 +922,10 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
{
const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;
struct svc_rdma_chunk *chunk, *next;
- unsigned int start, length;
+ unsigned int inline_len, start, length;
int ret;
+ inline_len = head->rc_saved_arg.head[0].iov_len;
start = 0;
chunk = pcl_first_chunk(pcl);
length = chunk->ch_position;
@@ -937,6 +943,8 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
break;
start += length;
+ if (head->rc_readbytes > next->ch_position)
+ return -EINVAL;
length = next->ch_position - head->rc_readbytes;
ret = svc_rdma_copy_inline_range(rqstp, head, start, length);
if (ret < 0)
@@ -944,7 +952,9 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
}
start += length;
- length = head->rc_byte_len - start;
+ if (start > inline_len)
+ return -EINVAL;
+ length = inline_len - start;
return svc_rdma_copy_inline_range(rqstp, head, start, length);
}
@@ -969,8 +979,12 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp,
static int svc_rdma_read_data_item(struct svc_rqst *rqstp,
struct svc_rdma_recv_ctxt *head)
{
- return svc_rdma_build_read_chunk(rqstp, head,
- pcl_first_chunk(&head->rc_read_pcl));
+ struct svc_rdma_chunk *chunk = pcl_first_chunk(&head->rc_read_pcl);
+
+ if (chunk->ch_position > head->rc_saved_arg.head[0].iov_len)
+ return -EINVAL;
+
+ return svc_rdma_build_read_chunk(rqstp, head, chunk);
}
/**
@@ -996,17 +1010,20 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp,
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(rqstp, head, &dummy);
@@ -1015,6 +1032,8 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp,
head->rc_readbytes += dummy.rs_length;
length -= dummy.rs_length;
+ if (!length)
+ break;
offset = 0;
}
return ret;
@@ -1039,14 +1058,17 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
pcl_first_chunk(&head->rc_call_pcl);
const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;
struct svc_rdma_chunk *chunk, *next;
- unsigned int start, length;
+ unsigned int call_len, start, length;
int ret;
if (pcl_is_empty(pcl))
return svc_rdma_build_read_chunk(rqstp, head, call_chunk);
+ call_len = call_chunk->ch_length;
start = 0;
chunk = pcl_first_chunk(pcl);
+ if (chunk->ch_position > call_len)
+ return -EINVAL;
length = chunk->ch_position;
ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
@@ -1063,6 +1085,10 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
break;
start += length;
+ if (next->ch_position > call_len)
+ return -EINVAL;
+ if (head->rc_readbytes > next->ch_position)
+ return -EINVAL;
length = next->ch_position - head->rc_readbytes;
ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
@@ -1071,7 +1097,9 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp,
}
start += length;
- length = call_chunk->ch_length - start;
+ if (start > call_len)
+ return -EINVAL;
+ length = call_len - start;
return svc_rdma_read_chunk_range(rqstp, head, call_chunk,
start, length);
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 7f6d17bf8c1f..c09659b17351 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -825,20 +825,21 @@ static int svc_rdma_xb_count_sges(const struct xdr_buf *xdr,
}
/**
- * svc_rdma_pull_up_needed - Determine whether to use pull-up
+ * svc_rdma_check_pull_up - Determine whether to use pull-up
* @rdma: controlling transport
* @sctxt: send_ctxt for the Send WR
* @write_pcl: Write chunk list provided by client
* @xdr: xdr_buf containing RPC message to transmit
*
* Returns:
- * %true if pull-up must be used
- * %false otherwise
+ * %1 if pull-up must be used
+ * %0 if pull-up is not needed
+ * %-E2BIG if the reply is too large to be pulled up
*/
-static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma,
- const struct svc_rdma_send_ctxt *sctxt,
- const struct svc_rdma_pcl *write_pcl,
- const struct xdr_buf *xdr)
+static int svc_rdma_check_pull_up(const struct svcxprt_rdma *rdma,
+ const struct svc_rdma_send_ctxt *sctxt,
+ const struct svc_rdma_pcl *write_pcl,
+ const struct xdr_buf *xdr)
{
/* Resources needed for the transport header */
struct svc_rdma_pullup_data args = {
@@ -850,11 +851,22 @@ static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma,
ret = pcl_process_nonpayloads(write_pcl, xdr,
svc_rdma_xb_count_sges, &args);
if (ret < 0)
- return false;
+ return 0;
if (args.pd_length < RPCRDMA_PULLUP_THRESH)
- return true;
- return args.pd_num_sges >= rdma->sc_max_send_sges;
+ return 1;
+ if (args.pd_num_sges < rdma->sc_max_send_sges)
+ return 0;
+
+ /*
+ * The reply has too many SGEs to Send inline, so it has to be
+ * linearized into sc_xprt_buf. That buffer holds only
+ * sc_max_req_size bytes, so a larger reply cannot be pulled up.
+ * RFC 8166 Section 4.5.3 requires responding with ERR_CHUNK.
+ */
+ if (args.pd_length > rdma->sc_max_req_size)
+ return -E2BIG;
+ return 1;
}
/**
@@ -910,7 +922,7 @@ static int svc_rdma_xb_linearize(const struct xdr_buf *xdr,
* Assemble the elements of @xdr into the transport header buffer.
*
* Assumptions:
- * pull_up_needed has determined that @xdr will fit in the buffer.
+ * check_pull_up has determined that @xdr will fit in the buffer.
*
* Returns:
* %0 if pull-up was successful
@@ -945,6 +957,7 @@ static int svc_rdma_pull_up_reply_msg(const struct svcxprt_rdma *rdma,
*
* Returns:
* %0 if DMA mapping was successful.
+ * %-E2BIG if the reply is too large to be pulled up
* %-EMSGSIZE if a buffer manipulation problem occurred
* %-EIO if DMA mapping failed
*
@@ -960,6 +973,7 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
.md_rdma = rdma,
.md_ctxt = sctxt,
};
+ int ret;
/* Set up the (persistently-mapped) transport header SGE. */
sctxt->sc_send_wr.num_sge = 1;
@@ -974,7 +988,10 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
/* For pull-up, svc_rdma_send() will sync the transport header.
* No additional DMA mapping is necessary.
*/
- if (svc_rdma_pull_up_needed(rdma, sctxt, write_pcl, xdr))
+ ret = svc_rdma_check_pull_up(rdma, sctxt, write_pcl, xdr);
+ if (ret < 0)
+ return ret;
+ if (ret)
return svc_rdma_pull_up_reply_msg(rdma, sctxt, write_pcl, xdr);
return pcl_process_nonpayloads(write_pcl, xdr,
@@ -1162,7 +1179,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
&rctxt->rc_reply_pcl, sctxt,
&rqstp->rq_res);
if (ret < 0)
- goto reply_chunk;
+ goto send_err;
rc_size = ret;
}
@@ -1183,10 +1200,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp);
if (ret < 0)
- goto put_ctxt;
+ goto send_err;
return 0;
-reply_chunk:
+send_err:
if (ret != -E2BIG && ret != -EINVAL)
goto put_ctxt;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 7ca71741106b..093371f9d245 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -43,6 +43,7 @@
*/
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -245,12 +246,16 @@ svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt,
* structure for the listening endpoint.
*
* This function creates a new xprt for the new connection and enqueues it on
- * the accept queue for the listent xprt. When the listen thread is kicked, it
+ * the accept queue for the listen xprt. When the listen thread is kicked, it
* will call the recvfrom method on the listen xprt which will accept the new
* connection.
+ *
+ * Return values:
+ * %0: Do not destroy @new_cma_id
+ * %1: Destroy @new_cma_id (allocation failure)
*/
-static void handle_connect_req(struct rdma_cm_id *new_cma_id,
- struct rdma_conn_param *param)
+static int handle_connect_req(struct rdma_cm_id *new_cma_id,
+ struct rdma_conn_param *param)
{
struct svcxprt_rdma *listen_xprt = new_cma_id->context;
struct svcxprt_rdma *newxprt;
@@ -260,7 +265,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
listen_xprt->sc_xprt.xpt_net,
ibdev_to_node(new_cma_id->device));
if (!newxprt)
- return;
+ return 1;
newxprt->sc_cm_id = new_cma_id;
new_cma_id->context = newxprt;
svc_rdma_parse_connect_private(newxprt, param);
@@ -294,6 +299,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
set_bit(XPT_CONN, &listen_xprt->sc_xprt.xpt_flags);
svc_xprt_enqueue(&listen_xprt->sc_xprt);
+ return 0;
}
/**
@@ -317,14 +323,14 @@ static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id,
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
- handle_connect_req(cma_id, &event->param.conn);
- break;
+ return handle_connect_req(cma_id, &event->param.conn);
case RDMA_CM_EVENT_ADDR_CHANGE:
listen_id = svc_rdma_create_listen_id(cma_rdma->xpt_net,
sap, cma_xprt);
if (IS_ERR(listen_id)) {
pr_err("Listener dead, address change failed for device %s\n",
cma_id->device->name);
+ cma_xprt->sc_cm_id = NULL;
} else
cma_xprt->sc_cm_id = listen_id;
return 1;
@@ -387,7 +393,13 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
listen_id = svc_rdma_create_listen_id(net, sa, cma_xprt);
if (IS_ERR(listen_id)) {
- kfree(cma_xprt);
+ /* _svc_xprt_create() acquired one module reference and
+ * puts it on xpo_create failure. svc_xprt_free() puts
+ * a second one when the kref drops to zero. Take a
+ * compensating reference so both puts are balanced.
+ */
+ __module_get(cma_xprt->sc_xprt.xpt_class->xcl_owner);
+ svc_xprt_put(&cma_xprt->sc_xprt);
return ERR_CAST(listen_id);
}
cma_xprt->sc_cm_id = listen_id;
@@ -598,13 +610,26 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
return &newxprt->sc_xprt;
errout:
- /* Take a reference in case the DTO handler runs */
- svc_xprt_get(&newxprt->sc_xprt);
- if (newxprt->sc_qp && !IS_ERR(newxprt->sc_qp))
- ib_destroy_qp(newxprt->sc_qp);
- rdma_destroy_id(newxprt->sc_cm_id);
- rpcrdma_rn_unregister(dev, &newxprt->sc_rn);
- /* This call to put will destroy the transport */
+ /*
+ * Drop the kref_init birth reference. svc_xprt_free will
+ * dispatch xpo_free = svc_rdma_free, which tears down sc_qp,
+ * sc_sq_cq, sc_rq_cq, and sc_pd under existing IS_ERR/NULL
+ * guards, and sc_rn under the rn_done sentinel guard inside
+ * rpcrdma_rn_unregister.
+ *
+ * sc_cm_id is destroyed unconditionally by svc_rdma_free; that
+ * is safe here because sc_cm_id is non-NULL by caller invariant
+ * on every path that reaches this errout: handle_connect_req
+ * installs newxprt->sc_cm_id before queueing the new xprt for
+ * accept, and svc_rdma_accept has already dereferenced it above
+ * the first goto errout.
+ *
+ * svc_handle_xprt() drops its pre-acquired module reference when
+ * ->xpo_accept() returns NULL. Take a replacement reference before
+ * freeing @newxprt, because svc_xprt_free() drops the module
+ * reference associated with @newxprt.
+ */
+ __module_get(newxprt->sc_xprt.xpt_class->xcl_owner);
svc_xprt_put(&newxprt->sc_xprt);
return NULL;
}
@@ -614,7 +639,8 @@ static void svc_rdma_detach(struct svc_xprt *xprt)
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
- rdma_disconnect(rdma->sc_cm_id);
+ if (rdma->sc_cm_id)
+ rdma_disconnect(rdma->sc_cm_id);
/*
* Most close paths go through svc_rdma_xprt_deferred_close(),
@@ -634,10 +660,15 @@ static void svc_rdma_free(struct svc_xprt *xprt)
{
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
- struct ib_device *device = rdma->sc_cm_id->device;
+ struct ib_device *device;
might_sleep();
+ if (!rdma->sc_cm_id)
+ goto out_free;
+
+ device = rdma->sc_cm_id->device;
+
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
ib_drain_qp(rdma->sc_qp);
@@ -662,11 +693,13 @@ static void svc_rdma_free(struct svc_xprt *xprt)
if (rdma->sc_pd && !IS_ERR(rdma->sc_pd))
ib_dealloc_pd(rdma->sc_pd);
+ if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
+ rpcrdma_rn_unregister(device, &rdma->sc_rn);
+
/* Destroy the CM ID */
rdma_destroy_id(rdma->sc_cm_id);
- if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
- rpcrdma_rn_unregister(device, &rdma->sc_rn);
+out_free:
kfree(rdma);
}
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 359407aae03e..5f7955e4c404 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2648,7 +2648,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/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
index 96c9fe8d357c..4f9aa9c4c3aa 100644
--- a/net/vmw_vsock/virtio_transport.c
+++ b/net/vmw_vsock/virtio_transport.c
@@ -872,10 +872,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/rust/Makefile b/rust/Makefile
index 627ed79dc6f5..9211d82ac96d 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -350,7 +350,8 @@ rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \
rusttestlib-kernel: private rustc_target_flags = --extern ffi \
--extern build_error --extern macros --extern pin_init \
--extern bindings --extern uapi \
- --extern zerocopy --extern zerocopy_derive
+ --extern zerocopy=$(objtree)/$(obj)/test/libzerocopy.rlib \
+ --extern zerocopy_derive=$(objtree)/$(obj)/test/$(libzerocopy_derive_name)
rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \
rusttestlib-build_error rusttestlib-pin_init $(obj)/$(libmacros_name) \
$(obj)/bindings.o rusttestlib-zerocopy rusttestlib-zerocopy_derive FORCE
@@ -644,7 +645,8 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
-Zunstable-options \
$(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \
$(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \
- $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \
+ $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \
+ $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \
$(cmd_ld_single)) \
$(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \
$(cmd_objtool)
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
index ed943960f851..3566f0234ca4 100644
--- a/rust/kernel/bug.rs
+++ b/rust/kernel/bug.rs
@@ -8,6 +8,7 @@
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(CONFIG_DEBUG_BUGVERBOSE)]
macro_rules! warn_flags {
@@ -47,12 +48,17 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
+ if false {
+ _ = $file;
+ }
+
// SAFETY:
// - `flags` and `size` are all compile-time constants, preventing
// any invalid memory access.
@@ -73,14 +79,19 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, CONFIG_UML))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ }
+
// SAFETY: It is always safe to call `warn_slowpath_fmt()`
// with a valid null-terminated string.
unsafe {
$crate::bindings::warn_slowpath_fmt(
- $crate::c_str!(::core::file!()).as_char_ptr(),
+ $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())),
line!() as $crate::ffi::c_int,
$flags as $crate::ffi::c_uint,
::core::ptr::null(),
@@ -91,9 +102,15 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ _ = $flags;
+ }
+
// SAFETY: It is always safe to call `WARN_ON()`.
unsafe { $crate::bindings::WARN_ON(true) }
};
@@ -101,9 +118,14 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
-#[cfg(not(CONFIG_BUG))]
+#[cfg(any(testlib, not(CONFIG_BUG)))]
macro_rules! warn_flags {
- ($file:expr, $flags:expr) => {};
+ ($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ _ = $flags;
+ }
+ };
}
#[doc(hidden)]
@@ -118,14 +140,14 @@ macro_rules! warn_on {
let cond = $cond;
#[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)]
- const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
+ const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))]
- const _COND_STR: &str = file!();
+ const COND_STR: &str = file!();
if cond {
const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
- $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS);
+ $crate::warn_flags!(COND_STR, WARN_ON_FLAGS);
}
cond
}};
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 8ff8aedf251a..73ff8575712c 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -21,7 +21,8 @@ use crate::{
sync::{
aref::ARef,
rcu,
- Arc, //
+ Arc,
+ Completion, //
},
types::{
ForeignOwnable,
@@ -37,6 +38,8 @@ struct Inner<T> {
node: Opaque<bindings::devres_node>,
#[pin]
data: Revocable<T>,
+ #[pin]
+ revocation: Completion,
}
/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
@@ -53,6 +56,10 @@ struct Inner<T> {
/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
/// anymore.
///
+/// When a [`Devres`] is dropped, it is guaranteed that `T` has been fully dropped by the time
+/// [`Devres::drop`] returns, even if a concurrent revocation through the release callback is in
+/// progress.
+///
/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
/// [`Drop`] implementation.
///
@@ -218,6 +225,7 @@ impl<T: Send + 'static> Devres<T> {
};
}),
data <- Revocable::new(data),
+ revocation <- Completion::new(),
}),
GFP_KERNEL,
)?;
@@ -255,7 +263,14 @@ impl<T: Send + 'static> Devres<T> {
// SAFETY: `inner` is a valid `Inner<T>` pointer.
let inner = unsafe { &*inner };
- inner.data.revoke();
+ if inner.data.revoke() {
+ inner.revocation.complete_all();
+ } else {
+ // Devres::drop() is concurrently revoking; wait for it to finish `drop_in_place()`
+ // before returning to `devres_release_all()`, ensuring `T` is fully torn down before
+ // the device finishes unbinding.
+ inner.revocation.wait_for_completion();
+ }
}
#[allow(clippy::missing_safety_doc)]
@@ -355,6 +370,8 @@ impl<T: Send + 'static> Drop for Devres<T> {
// SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data
// anymore, hence it is safe not to wait for the grace period to finish.
if unsafe { self.data().revoke_nosync() } {
+ self.inner.revocation.complete_all();
+
// We revoked `self.data` before devres did, hence try to remove it.
if self.remove_node() {
// SAFETY: In `Self::new` we have taken an additional reference count of `self.data`
@@ -362,6 +379,10 @@ impl<T: Send + 'static> Drop for Devres<T> {
// this additional reference count.
drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.inner)) });
}
+ } else {
+ // The release callback is concurrently revoking; wait for it to finish
+ // `drop_in_place()` of the wrapped object before returning.
+ self.inner.revocation.wait_for_completion();
}
}
}
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 200def84fb69..556424b88fb9 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -1005,7 +1005,11 @@ impl<T: KnownSize + AsBytes + ?Sized> debugfs::BinaryWriter for Coherent<T> {
return Ok(0);
};
- let count = self.size().saturating_sub(offset_val).min(writer.len());
+ if offset_val >= self.size() {
+ return Ok(0);
+ }
+
+ let count = (self.size() - offset_val).min(writer.len());
writer.write_dma(self, offset_val, count)?;
diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs
index 73afbc51ba33..cd7d9664ff5b 100644
--- a/rust/kernel/fmt.rs
+++ b/rust/kernel/fmt.rs
@@ -43,7 +43,14 @@ use core::fmt::{
UpperExp,
UpperHex, //
};
-impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp);
+impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp);
+
+impl<T: ?Sized + Pointer> Pointer for Adapter<&T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(self.0, f)
+ }
+}
/// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types.
///
diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs
index 406e3a028c55..0f367264ee2e 100644
--- a/rust/kernel/list.rs
+++ b/rust/kernel/list.rs
@@ -249,7 +249,7 @@ pub use self::arc_field::{
/// assert_eq!(list.iter().count(), 3);
/// }
///
-/// // Pop the items from the list using `pop_front()` and verify the content.
+/// // Pop the items from the list using `pop_back()` and verify the content.
/// {
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 15));
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 32));
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index dafe77782d79..6d766c22e61b 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -13,7 +13,10 @@ use core::{
};
use kernel::{
- num::Integer,
+ num::{
+ Integer,
+ Unsigned, //
+ },
prelude::*, //
};
@@ -174,13 +177,16 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
/// // `u8` (regardless of the passed value).
/// // let _ = Bounded::<u32, 6>::from(10u8);
///
-/// // Booleans can be converted into single-bit `Bounded`s.
+/// // Booleans can be converted into unsigned `Bounded`s.
///
/// let v = Bounded::<u64, 1>::from(false);
/// assert_eq!(v.get(), 0);
///
/// let v = Bounded::<u64, 1>::from(true);
/// assert_eq!(v.get(), 1);
+///
+/// // This does not build because `i8` is signed.
+/// // let _ = Bounded::<i8, 2>::from(true);
/// ```
///
/// Infallible conversions from a [`Bounded`] to a primitive integer are also supported, and
@@ -203,12 +209,16 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
/// let _v = Bounded::<u32, 10>::new::<10>();
/// // assert_eq!(u8::from(_v), 10);
///
-/// // Single-bit `Bounded`s can be converted into a boolean.
+/// // Unsigned single-bit `Bounded`s can be converted into a boolean.
/// let v = Bounded::<u8, 1>::new::<1>();
/// assert_eq!(bool::from(v), true);
///
/// let v = Bounded::<u8, 1>::new::<0>();
/// assert_eq!(bool::from(v), false);
+///
+/// // This does not build because `i8` is signed.
+/// // let v = Bounded::<i8, 1>::new::<-1>();
+/// // let _ = bool::from(v);
/// ```
///
/// Fallible conversions from any primitive integer to any [`Bounded`] are also supported using the
@@ -485,6 +495,7 @@ where
/// assert_eq!(v_shifted.get(), 0xff);
/// ```
pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const_assert!(SHIFT < T::BITS);
const { assert!(RES + SHIFT >= N) }
// SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to
@@ -1077,31 +1088,33 @@ impl_into_primitive!(
i8 i16 i32 i64 isize
);
-// Single-bit `Bounded`s can be converted from/to a boolean.
+// Unsigned single-bit `Bounded`s can be converted to a boolean.
impl<T> From<Bounded<T, 1>> for bool
where
- T: Integer + Zeroable,
+ T: Integer<Signedness = Unsigned> + Zeroable,
{
fn from(value: Bounded<T, 1>) -> Self {
value.get() != Zeroable::zeroed()
}
}
+// Booleans can be converted to unsigned `Bounded`s.
+
impl<T, const N: u32> From<bool> for Bounded<T, N>
where
- T: Integer + From<bool>,
+ T: Integer<Signedness = Unsigned> + From<bool>,
{
fn from(value: bool) -> Self {
- // SAFETY: A boolean can be represented using a single bit, and thus fits within any
- // integer type for any `N` > 0.
+ // SAFETY: A boolean is represented by `0` or `1`, so it fits within any valid unsigned
+ // `Bounded` width.
unsafe { Self::__new(T::from(value)) }
}
}
impl<T> Bounded<T, 1>
where
- T: Integer + Zeroable,
+ T: Integer<Signedness = Unsigned> + Zeroable,
{
/// Converts this [`Bounded`] into a [`bool`].
///
diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
index b8463823aed9..54fc46d460b6 100644
--- a/rust/kernel/time.rs
+++ b/rust/kernel/time.rs
@@ -441,22 +441,25 @@ impl Delta {
/// to the value in the [`Delta`].
#[inline]
pub fn as_micros_ceil(self) -> i64 {
+ // Only positive values need to be rounded up: truncating division already
+ // rounds towards zero, i.e. up, for negative values.
+ //
+ // The usual `(nanos + d - 1) / d` is not used because the addition overflows
+ // once `nanos` exceeds `i64::MAX - (d - 1)`; saturating the addition instead
+ // would drop the rounding bias and return a result one unit too small.
let n = self.as_nanos();
- let n = if n >= 0 {
- n.saturating_add(NSEC_PER_USEC - 1)
- } else {
- n
- };
+
+ let (n, add) = if n > 0 { (n - 1, 1) } else { (n, 0) };
#[cfg(CONFIG_64BIT)]
{
- n / NSEC_PER_USEC
+ n / NSEC_PER_USEC + add
}
#[cfg(not(CONFIG_64BIT))]
// SAFETY: It is always safe to call `ktime_to_us()` with any value.
unsafe {
- bindings::ktime_to_us(n)
+ bindings::ktime_to_us(n) + add
}
}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 911745743246..0b71f759eb5c 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -346,7 +346,8 @@ quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \
$(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \
$(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \
- $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \
+ $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \
+ $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \
$(cmd_ld_single)) \
$(cmd_objtool)
diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh
index 551f1ebd0dcb..c30983562a2f 100755
--- a/scripts/rust_is_available.sh
+++ b/scripts/rust_is_available.sh
@@ -208,6 +208,20 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
exit 1
fi
+if [ "$bindgen_libclang_cversion" -ge 2200000 ] &&
+ [ "$rust_bindings_generator_cversion" -lt 7201 ]; then
+ # Distributions may have patched the issue.
+ if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then
+ echo >&2 "***"
+ echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22"
+ echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)."
+ echo >&2 "*** Your bindgen version: $rust_bindings_generator_version"
+ echo >&2 "*** Your libclang version: $bindgen_libclang_version"
+ echo >&2 "***"
+ warning=1
+ fi
+fi
+
# If the C compiler is Clang, then we can also check whether its version
# matches the `libclang` version used by the Rust bindings generator.
#
diff --git a/scripts/rust_is_available_bindgen_libclang_22.h b/scripts/rust_is_available_bindgen_libclang_22.h
new file mode 100644
index 000000000000..6b33544c14a8
--- /dev/null
+++ b/scripts/rust_is_available_bindgen_libclang_22.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+struct S;
+struct S {
+ int foo;
+};
diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py
index d6d54b7ea42a..e14cf2c5695e 100755
--- a/scripts/rust_is_available_test.py
+++ b/scripts/rust_is_available_test.py
@@ -54,16 +54,23 @@ else:
""")
@classmethod
- def generate_bindgen(cls, version_stdout, libclang_stderr):
+ def generate_bindgen(cls, version_stdout, libclang_stderr, libclang_22_patched=False):
if libclang_stderr is None:
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
else:
libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
+ if libclang_22_patched:
+ libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')"
+ else:
+ libclang_22_case = "pass"
+
return cls.generate_executable(f"""#!/usr/bin/env python3
import sys
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
{libclang_case}
+elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv):
+ {libclang_22_case}
else:
print({repr(version_stdout)})
""")
@@ -248,6 +255,27 @@ else:
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
+ def test_bindgen_bad_libclang_22(self):
+ for (bindgen_version, libclang_version, expected_not_patched) in (
+ ("0.71.1", "21.1.0", self.Expected.SUCCESS),
+ ("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
+ ("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
+
+ ("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
+
+ ("0.72.1", "22.0.0", self.Expected.SUCCESS),
+ ):
+ with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
+ cc = self.generate_clang(f"clang version {libclang_version}")
+ libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
+ bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
+ result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
+ if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
+ self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr)
+
+ bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True)
+ result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
+
def test_clang_matches_bindgen_libclang_different_bindgen(self):
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index 2b6098149b15..0e8b67159f56 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -222,13 +222,9 @@ static inline struct aa_label *begin_current_label_crit_section(void)
{
struct aa_label *label = aa_current_raw_label();
- might_sleep();
-
if (label_is_stale(label)) {
label = aa_get_newest_label(label);
- if (aa_replace_current_label(label) == 0)
- /* task cred will keep the reference */
- aa_put_label(label);
+ aa_schedule_stale_label_replacement();
}
return label;
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index b5a722a47fd2..37cb135de323 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -23,7 +23,7 @@ struct aa_ruleset;
#define LOCAL_VEC_ENTRIES 8
#define DEFINE_VEC(T, V) \
- struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \
+ struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES + 1]; \
struct aa_ ## T **(V)
#define vec_setup(T, V, N, GFP) \
@@ -31,10 +31,10 @@ struct aa_ruleset;
if ((N) <= LOCAL_VEC_ENTRIES) { \
typeof(N) i; \
(V) = (_ ## V ## _localtmp); \
- for (i = 0; i < (N); i++) \
+ for (i = 0; i <= (N); i++) \
(V)[i] = NULL; \
} else \
- (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \
+ (V) = kzalloc_objs(struct aa_ ## T *, (N) + 1, (GFP)); \
(V) ? 0 : -ENOMEM; \
})
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h
index b1aaaf60fa8b..6f26758ca10f 100644
--- a/security/apparmor/include/task.h
+++ b/security/apparmor/include/task.h
@@ -21,15 +21,22 @@ static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
* @onexec: profile to transition to on next exec (MAY BE NULL)
* @previous: profile the task may return to (MAY BE NULL)
* @token: magic value the task must know for returning to @previous_profile
+ * @label_replacement_tw: for aa_schedule_stale_label_replacement()
+ * @label_replacement_pending: is @label_replacement_tw pending?
+ *
+ * When changing this, check if aa_dup_task_ctx() needs to be updated.
*/
struct aa_task_ctx {
struct aa_label *nnp;
struct aa_label *onexec;
struct aa_label *previous;
u64 token;
+ struct callback_head label_replacement_tw;
+ bool label_replacement_pending;
};
int aa_replace_current_label(struct aa_label *label);
+void aa_schedule_stale_label_replacement(void);
void aa_set_current_onexec(struct aa_label *label, bool stack);
int aa_set_current_hat(struct aa_label *label, u64 token);
int aa_restore_previous_label(u64 cookie);
@@ -56,10 +63,10 @@ static inline void aa_free_task_ctx(struct aa_task_ctx *ctx)
static inline void aa_dup_task_ctx(struct aa_task_ctx *new,
const struct aa_task_ctx *old)
{
- *new = *old;
- aa_get_label(new->nnp);
- aa_get_label(new->previous);
- aa_get_label(new->onexec);
+ new->nnp = aa_get_label(old->nnp);
+ new->onexec = aa_get_label(old->onexec);
+ new->previous = aa_get_label(old->previous);
+ new->token = old->token;
}
/**
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index b9fb3738124e..e16ff4130bc2 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -14,6 +14,7 @@
#include <linux/gfp.h>
#include <linux/ptrace.h>
+#include <linux/task_work.h>
#include "include/path.h"
#include "include/audit.h"
@@ -89,6 +90,32 @@ int aa_replace_current_label(struct aa_label *label)
return 0;
}
+static void aa_replace_stale_label_tw_func(struct callback_head *tw)
+{
+ struct aa_task_ctx *ctx = task_ctx(current);
+ struct aa_label *label;
+
+ ctx->label_replacement_pending = false;
+ label = aa_current_raw_label();
+ if (!label_is_stale(label))
+ return;
+ label = aa_get_newest_label(label);
+ aa_replace_current_label(label);
+ aa_put_label(label);
+}
+
+/* replace the current task's stale label on syscall return */
+void aa_schedule_stale_label_replacement(void)
+{
+ struct aa_task_ctx *ctx = task_ctx(current);
+
+ if (ctx->label_replacement_pending)
+ return;
+ init_task_work(&ctx->label_replacement_tw, aa_replace_stale_label_tw_func);
+ if (task_work_add(current, &ctx->label_replacement_tw, TWA_RESUME) == 0)
+ ctx->label_replacement_pending = true;
+}
+
/**
* aa_set_current_onexec - set the tasks change_profile to happen onexec
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 13513819991e..8f57c6111e7e 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -987,9 +987,9 @@ err_put:
static void trusted_tpm_exit(void)
{
if (chip) {
+ unregister_key_type(&key_type_trusted);
put_device(&chip->dev);
kfree(digests);
- unregister_key_type(&key_type_trusted);
}
}
diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h
index 3f099555f059..315ea7e0fe50 100644
--- a/security/landlock/errata/abi-1.h
+++ b/security/landlock/errata/abi-1.h
@@ -22,3 +22,26 @@
* from their original mount points.
*/
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 f7e5e4ef9eac..8fc7f82a374a 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -20,6 +20,7 @@
#include <linux/falloc.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>
@@ -983,7 +984,8 @@ static int current_check_access_path(const struct path *const path,
return -EACCES;
}
-static __attribute_const__ access_mask_t get_mode_access(const umode_t mode)
+static __attribute_const__ access_mask_t get_mode_access(const umode_t mode,
+ const dev_t dev)
{
switch (mode & S_IFMT) {
case S_IFLNK:
@@ -991,6 +993,9 @@ static __attribute_const__ 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;
@@ -1007,6 +1012,13 @@ static __attribute_const__ access_mask_t get_mode_access(const umode_t mode)
}
}
+static 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 access_mask_t maybe_remove(const struct dentry *const dentry)
{
if (d_is_negative(dentry))
@@ -1093,6 +1105,7 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
* @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
@@ -1140,7 +1153,8 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
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_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), any_fs, NULL);
@@ -1159,18 +1173,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) {
/*
@@ -1520,7 +1541,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,
@@ -1531,7 +1552,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,
@@ -1544,7 +1566,8 @@ static int hook_path_mknod(const struct path *const dir,
struct dentry *const dentry, const umode_t mode,
const unsigned int dev)
{
- return current_check_access_path(dir, get_mode_access(mode));
+ return current_check_access_path(
+ 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_lsm.c b/security/smack/smack_lsm.c
index ff115068c5c0..bbe6cd6b03f7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1964,7 +1964,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;
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 06bfe09eae1a..fd5bbf4615e2 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1802,6 +1802,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 c217c427bf1e..b93291ad067a 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) {
dev_err(&devptr->dev, "specify port\n");
return -EINVAL;
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 36e9eab204ca..cefaa00b83e7 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -900,6 +900,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 dcc0899cfb99..03ed3c3c27fb 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -697,6 +697,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 3c28961091b1..cc9c325df910 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -846,6 +846,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 a204f42d1026..70e16105f1c4 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/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 48a2e7f2ebb1..3c18f7cae047 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -7442,6 +7442,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
@@ -7949,6 +7950,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ /* Lenovo Yoga Slim 7 14AKP10 shares PCI SSID 17aa:38b4 with Legion Slim 7
+ * 16IRH8; use codec SSID to distinguish them
+ */
+ HDA_CODEC_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
HDA_CODEC_QUIRK(0x17aa, 0x391d, "Lenovo Yoga 7 2-in-1 16AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
@@ -7989,7 +7994,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
- SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
@@ -8055,6 +8059,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c
index 2cf3664a35ff..a640292c4f10 100644
--- a/sound/hda/codecs/realtek/alc662.c
+++ b/sound/hda/codecs/realtek/alc662.c
@@ -852,6 +852,7 @@ static const struct hda_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB),
#if 0
/* Below is a quirk table taken from the old code.
diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c
index 4c7506d49f55..517bd151fcc3 100644
--- a/sound/hda/core/ext/stream.c
+++ b/sound/hda/core/ext/stream.c
@@ -210,8 +210,8 @@ void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
break;
udelay(3);
} while (--timeout);
- val &= ~AZX_PPLCCTL_STRST;
- writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+ AZX_PPLCCTL_STRST, 0);
udelay(3);
timeout = 50;
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 25a640304430..60cc0721d0a6 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1535,6 +1535,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)) {
@@ -1548,13 +1555,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/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index ae90e9f513d1..79858fc94806 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -846,6 +846,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.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 = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "M7601RM"),
}
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 549783d3e75e..dbdf51188cf5 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -206,10 +206,10 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
return ret;
check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
- & AW88261_BIT_SYSST_CHECK;
- if (check_val != AW88261_BIT_SYSST_CHECK) {
+ & AW88261_BIT_PLL_CHECK;
+ if (check_val != AW88261_BIT_PLL_CHECK) {
dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
- reg_val, AW88261_BIT_SYSST_CHECK);
+ reg_val, AW88261_BIT_PLL_CHECK);
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
return 0;
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 270ccf375f36..f16f9f8c40a2 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -194,12 +194,6 @@
AW88261_OTHS_OT_VALUE | \
AW88261_PLLS_LOCKED_VALUE))
-#define AW88261_BIT_SYSST_CHECK \
- (AW88261_BSTS_FINISHED_VALUE | \
- AW88261_SWS_SWITCHING_VALUE | \
- AW88261_CLKS_STABLE_VALUE | \
- AW88261_PLLS_LOCKED_VALUE)
-
#define AW88261_ULS_HMUTE_START_BIT (14)
#define AW88261_ULS_HMUTE_BITS_LEN (1)
#define AW88261_ULS_HMUTE_MASK \
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index 7299c6bfcf15..87e898cc35e4 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -26,94 +26,162 @@
#define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id)
-#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base, cif_ctrl) \
+#define RX_REG_DEFAULTS(id, rx_ctrl, rx_base, cif_ctrl) \
{ CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001 }, \
{ CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), cif_ctrl }, \
- { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }, \
+ { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }
+
+#define TX_REG_DEFAULTS(id, tx_ctrl, tx_base, cif_ctrl) \
{ CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001 }, \
{ CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), cif_ctrl }, \
{ CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl }
-#define ADMAIF_REG_DEFAULTS(id, chip) \
- REG_DEFAULTS((id) - 1, \
+#define ADMAIF_RX_REG_DEFAULTS(id, chip) \
+ RX_REG_DEFAULTS((id) - 1, \
chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT, \
+ chip ## _ADMAIF_RX_BASE, \
+ chip ## _ADMAIF_CIF_REG_DEFAULT)
+
+#define ADMAIF_TX_REG_DEFAULTS(id, chip) \
+ TX_REG_DEFAULTS((id) - 1, \
chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT, \
chip ## _ADMAIF_TX_BASE, \
- chip ## _ADMAIF_RX_BASE, \
chip ## _ADMAIF_CIF_REG_DEFAULT)
static const struct reg_default tegra186_admaif_reg_defaults[] = {
- {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003},
- ADMAIF_REG_DEFAULTS(1, TEGRA186),
- ADMAIF_REG_DEFAULTS(2, TEGRA186),
- ADMAIF_REG_DEFAULTS(3, TEGRA186),
- ADMAIF_REG_DEFAULTS(4, TEGRA186),
- ADMAIF_REG_DEFAULTS(5, TEGRA186),
- ADMAIF_REG_DEFAULTS(6, TEGRA186),
- ADMAIF_REG_DEFAULTS(7, TEGRA186),
- ADMAIF_REG_DEFAULTS(8, TEGRA186),
- ADMAIF_REG_DEFAULTS(9, TEGRA186),
- ADMAIF_REG_DEFAULTS(10, TEGRA186),
- ADMAIF_REG_DEFAULTS(11, TEGRA186),
- ADMAIF_REG_DEFAULTS(12, TEGRA186),
- ADMAIF_REG_DEFAULTS(13, TEGRA186),
- ADMAIF_REG_DEFAULTS(14, TEGRA186),
- ADMAIF_REG_DEFAULTS(15, TEGRA186),
- ADMAIF_REG_DEFAULTS(16, TEGRA186),
- ADMAIF_REG_DEFAULTS(17, TEGRA186),
- ADMAIF_REG_DEFAULTS(18, TEGRA186),
- ADMAIF_REG_DEFAULTS(19, TEGRA186),
- ADMAIF_REG_DEFAULTS(20, TEGRA186)
+ ADMAIF_RX_REG_DEFAULTS(1, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(2, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(3, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(4, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(5, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(6, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(7, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(8, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(9, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(10, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(11, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(12, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(13, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(14, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(15, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(16, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(17, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(18, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(19, TEGRA186),
+ ADMAIF_RX_REG_DEFAULTS(20, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(1, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(2, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(3, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(4, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(5, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(6, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(7, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(8, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(9, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(10, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(11, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(12, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(13, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(14, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(15, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(16, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(17, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(18, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(19, TEGRA186),
+ ADMAIF_TX_REG_DEFAULTS(20, TEGRA186),
+ {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003}
};
static const struct reg_default tegra210_admaif_reg_defaults[] = {
- {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003},
- ADMAIF_REG_DEFAULTS(1, TEGRA210),
- ADMAIF_REG_DEFAULTS(2, TEGRA210),
- ADMAIF_REG_DEFAULTS(3, TEGRA210),
- ADMAIF_REG_DEFAULTS(4, TEGRA210),
- ADMAIF_REG_DEFAULTS(5, TEGRA210),
- ADMAIF_REG_DEFAULTS(6, TEGRA210),
- ADMAIF_REG_DEFAULTS(7, TEGRA210),
- ADMAIF_REG_DEFAULTS(8, TEGRA210),
- ADMAIF_REG_DEFAULTS(9, TEGRA210),
- ADMAIF_REG_DEFAULTS(10, TEGRA210)
+ ADMAIF_RX_REG_DEFAULTS(1, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(2, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(3, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(4, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(5, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(6, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(7, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(8, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(9, TEGRA210),
+ ADMAIF_RX_REG_DEFAULTS(10, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(1, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(2, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(3, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(4, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(5, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(6, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(7, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(8, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(9, TEGRA210),
+ ADMAIF_TX_REG_DEFAULTS(10, TEGRA210),
+ {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003}
};
static const struct reg_default tegra264_admaif_reg_defaults[] = {
- {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA264_ADMAIF_GLOBAL_BASE), 0x00000003},
- ADMAIF_REG_DEFAULTS(1, TEGRA264),
- ADMAIF_REG_DEFAULTS(2, TEGRA264),
- ADMAIF_REG_DEFAULTS(3, TEGRA264),
- ADMAIF_REG_DEFAULTS(4, TEGRA264),
- ADMAIF_REG_DEFAULTS(5, TEGRA264),
- ADMAIF_REG_DEFAULTS(6, TEGRA264),
- ADMAIF_REG_DEFAULTS(7, TEGRA264),
- ADMAIF_REG_DEFAULTS(8, TEGRA264),
- ADMAIF_REG_DEFAULTS(9, TEGRA264),
- ADMAIF_REG_DEFAULTS(10, TEGRA264),
- ADMAIF_REG_DEFAULTS(11, TEGRA264),
- ADMAIF_REG_DEFAULTS(12, TEGRA264),
- ADMAIF_REG_DEFAULTS(13, TEGRA264),
- ADMAIF_REG_DEFAULTS(14, TEGRA264),
- ADMAIF_REG_DEFAULTS(15, TEGRA264),
- ADMAIF_REG_DEFAULTS(16, TEGRA264),
- ADMAIF_REG_DEFAULTS(17, TEGRA264),
- ADMAIF_REG_DEFAULTS(18, TEGRA264),
- ADMAIF_REG_DEFAULTS(19, TEGRA264),
- ADMAIF_REG_DEFAULTS(20, TEGRA264),
- ADMAIF_REG_DEFAULTS(21, TEGRA264),
- ADMAIF_REG_DEFAULTS(22, TEGRA264),
- ADMAIF_REG_DEFAULTS(23, TEGRA264),
- ADMAIF_REG_DEFAULTS(24, TEGRA264),
- ADMAIF_REG_DEFAULTS(25, TEGRA264),
- ADMAIF_REG_DEFAULTS(26, TEGRA264),
- ADMAIF_REG_DEFAULTS(27, TEGRA264),
- ADMAIF_REG_DEFAULTS(28, TEGRA264),
- ADMAIF_REG_DEFAULTS(29, TEGRA264),
- ADMAIF_REG_DEFAULTS(30, TEGRA264),
- ADMAIF_REG_DEFAULTS(31, TEGRA264),
- ADMAIF_REG_DEFAULTS(32, TEGRA264)
+ ADMAIF_RX_REG_DEFAULTS(1, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(2, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(3, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(4, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(5, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(6, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(7, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(8, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(9, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(10, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(11, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(12, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(13, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(14, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(15, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(16, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(17, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(18, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(19, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(20, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(21, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(22, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(23, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(24, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(25, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(26, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(27, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(28, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(29, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(30, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(31, TEGRA264),
+ ADMAIF_RX_REG_DEFAULTS(32, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(1, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(2, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(3, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(4, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(5, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(6, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(7, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(8, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(9, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(10, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(11, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(12, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(13, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(14, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(15, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(16, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(17, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(18, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(19, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(20, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(21, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(22, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(23, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(24, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(25, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(26, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(27, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(28, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(29, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(30, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(31, TEGRA264),
+ ADMAIF_TX_REG_DEFAULTS(32, TEGRA264),
+ {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA264_ADMAIF_GLOBAL_BASE), 0x00000003}
};
static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index ff8c72fc38c5..fdcc5d62b720 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -23,9 +23,9 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = {
{ TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 },
{ TEGRA210_I2S_TX_INT_MASK, 0x00000003 },
{ TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 },
+ { TEGRA210_I2S_ENABLE, 0x0 },
{ TEGRA210_I2S_CG, 0x1 },
{ TEGRA210_I2S_TIMING, 0x0000001f },
- { TEGRA210_I2S_ENABLE, 0x1 },
/*
* Below update does not have any effect on Tegra186 and Tegra194.
* On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update
@@ -38,13 +38,13 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = {
static const struct reg_default tegra264_i2s_reg_defaults[] = {
{ TEGRA210_I2S_RX_INT_MASK, 0x00000003 },
{ TEGRA210_I2S_RX_CIF_CTRL, 0x00003f00 },
+ { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 },
{ TEGRA264_I2S_TX_INT_MASK, 0x00000003 },
{ TEGRA264_I2S_TX_CIF_CTRL, 0x00003f00 },
+ { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 },
+ { TEGRA264_I2S_ENABLE, 0x0 },
{ TEGRA264_I2S_CG, 0x1 },
{ TEGRA264_I2S_TIMING, 0x0000001f },
- { TEGRA264_I2S_ENABLE, 0x1 },
- { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 },
- { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 },
};
static void tegra210_i2s_set_slot_ctrl(struct tegra210_i2s *i2s,
@@ -201,9 +201,21 @@ static int tegra210_i2s_runtime_resume(struct device *dev)
}
regcache_cache_only(i2s->regmap, false);
- regcache_sync(i2s->regmap);
+ err = regcache_sync(i2s->regmap);
+ if (err)
+ goto err;
+
+ err = regmap_write(i2s->regmap, i2s->soc_data->enable_reg, I2S_EN);
+ if (err)
+ goto err;
return 0;
+
+err:
+ regcache_cache_only(i2s->regmap, true);
+ clk_disable_unprepare(i2s->clk_i2s);
+
+ return err;
}
static void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s,
@@ -1133,6 +1145,7 @@ static const struct tegra_i2s_soc_data soc_data_tegra210 = {
.regmap_conf = &tegra210_regmap_conf,
.i2s_cmpnt = &tegra210_i2s_cmpnt,
.max_ch = TEGRA210_I2S_MAX_CHANNEL,
+ .enable_reg = TEGRA210_I2S_ENABLE,
.tx_offset = TEGRA210_I2S_TX_OFFSET,
.i2s_ctrl_offset = TEGRA210_I2S_CTRL_OFFSET,
.fsync_width_mask = I2S_CTRL_FSYNC_WIDTH_MASK,
@@ -1144,6 +1157,7 @@ static const struct tegra_i2s_soc_data soc_data_tegra264 = {
.regmap_conf = &tegra264_regmap_conf,
.i2s_cmpnt = &tegra264_i2s_cmpnt,
.max_ch = TEGRA264_I2S_MAX_CHANNEL,
+ .enable_reg = TEGRA264_I2S_ENABLE,
.tx_offset = TEGRA264_I2S_TX_OFFSET,
.i2s_ctrl_offset = TEGRA264_I2S_CTRL_OFFSET,
.fsync_width_mask = TEGRA264_I2S_CTRL_FSYNC_WIDTH_MASK,
diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h
index 42be2137342c..82292f96ab36 100644
--- a/sound/soc/tegra/tegra210_i2s.h
+++ b/sound/soc/tegra/tegra210_i2s.h
@@ -150,6 +150,7 @@ struct tegra_i2s_soc_data {
const struct regmap_config *regmap_conf;
const struct snd_soc_component_driver *i2s_cmpnt;
unsigned int max_ch;
+ unsigned int enable_reg;
unsigned int tx_offset;
unsigned int i2s_ctrl_offset;
unsigned int fsync_width_mask;
diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c
index f5d4a93dd6f1..1d78b4c1cc88 100644
--- a/sound/soc/tegra/tegra210_mbdrc.c
+++ b/sound/soc/tegra/tegra210_mbdrc.c
@@ -20,33 +20,33 @@
#define MBDRC_FILTER_REG(reg, id) \
((reg) + ((id) * TEGRA210_MBDRC_FILTER_PARAM_STRIDE))
-#define MBDRC_FILTER_REG_DEFAULTS(id) \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_IIR_CFG, id), 0x00000005}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_ATTACK, id), 0x3e48590c}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_RELEASE, id), 0x08414e9f}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_ATTACK, id), 0x7fffffff}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_THRESHOLD, id), 0x06145082}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_OUT_THRESHOLD, id), 0x060d379b}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_1ST, id), 0x0000a000}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_2ND, id), 0x00002000}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_3RD, id), 0x00000b33}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_4TH, id), 0x00000800}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_5TH, id), 0x0000019a}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_MAKEUP_GAIN, id), 0x00000002}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_INIT_GAIN, id), 0x00066666}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_ATTACK, id), 0x00d9ba0e}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_RELEASE, id), 0x3e48590c}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_RELEASE, id), 0x7ffff26a}, \
- { MBDRC_FILTER_REG(TEGRA210_MBDRC_CFG_RAM_CTRL, id), 0x4000}
+#define MBDRC_FILTER_PARAM_REG_DEFAULTS(reg, val) \
+ { MBDRC_FILTER_REG(reg, 0), val}, \
+ { MBDRC_FILTER_REG(reg, 1), val}, \
+ { MBDRC_FILTER_REG(reg, 2), val}
static const struct reg_default tegra210_mbdrc_reg_defaults[] = {
{ TEGRA210_MBDRC_CFG, 0x0030de51},
{ TEGRA210_MBDRC_CHANNEL_MASK, 0x00000003},
{ TEGRA210_MBDRC_FAST_FACTOR, 0x30000800},
- MBDRC_FILTER_REG_DEFAULTS(0),
- MBDRC_FILTER_REG_DEFAULTS(1),
- MBDRC_FILTER_REG_DEFAULTS(2),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IIR_CFG, 0x00000005),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_ATTACK, 0x3e48590c),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_RELEASE, 0x08414e9f),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_FAST_ATTACK, 0x7fffffff),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_THRESHOLD, 0x06145082),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_OUT_THRESHOLD, 0x060d379b),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_1ST, 0x0000a000),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_2ND, 0x00002000),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_3RD, 0x00000b33),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_4TH, 0x00000800),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_5TH, 0x0000019a),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_MAKEUP_GAIN, 0x00000002),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_INIT_GAIN, 0x00066666),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_GAIN_ATTACK, 0x00d9ba0e),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_GAIN_RELEASE, 0x3e48590c),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_FAST_RELEASE, 0x7ffff26a),
+ MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_CFG_RAM_CTRL, 0x4000),
};
/* Default MBDRC parameters */
diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c
index a69774578d69..96259a6cbc59 100644
--- a/sound/soc/tegra/tegra210_mixer.c
+++ b/sound/soc/tegra/tegra210_mixer.c
@@ -57,10 +57,10 @@ static const struct reg_default tegra210_mixer_reg_defaults[] = {
MIXER_TX_REG_DEFAULTS(3),
MIXER_TX_REG_DEFAULTS(4),
+ { TEGRA210_MIXER_ENABLE, 0x0 },
{ TEGRA210_MIXER_CG, 0x00000001},
{ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000},
{ TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000},
- { TEGRA210_MIXER_ENABLE, 0x1 },
};
/* Default gain parameters */
@@ -86,11 +86,15 @@ static int tegra210_mixer_runtime_suspend(struct device *dev)
static int tegra210_mixer_runtime_resume(struct device *dev)
{
struct tegra210_mixer *mixer = dev_get_drvdata(dev);
+ int err;
regcache_cache_only(mixer->regmap, false);
- regcache_sync(mixer->regmap);
+ err = regcache_sync(mixer->regmap);
+ if (err)
+ return err;
- return 0;
+ return regmap_write(mixer->regmap, TEGRA210_MIXER_ENABLE,
+ TEGRA210_MIXER_EN);
}
static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
index 9a0b85653c61..d3b7cab85699 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 bebb48cb9abc..c5c542d17ccc 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/fcp.c b/sound/usb/fcp.c
index cee5ec9d78a0..5fc2131b4561 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -494,7 +494,7 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
buf_size = init.step0_resp_size + init.step2_resp_size;
void *resp __free(kfree) =
- kmalloc(buf_size, GFP_KERNEL);
+ kzalloc(buf_size, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -1033,6 +1033,8 @@ static int fcp_init(struct usb_mixer_interface *mixer,
step0_resp, private->step0_resp_size);
if (err < 0)
return err;
+ if (err != private->step0_resp_size)
+ return -EIO;
err = fcp_init_notify(mixer);
if (err < 0)
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index f40bd2b04c29..f2f54b038168 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -119,7 +119,7 @@
#define __read_mostly
#ifndef __attribute_const__
-# define __attribute_const__
+# define __attribute_const__ __attribute__ ((__const__))
#endif
#ifndef __maybe_unused
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index f03dd59e7fca..3ab5b9f1c6a4 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -194,6 +194,8 @@ static bool is_rust_noreturn(const struct symbol *func)
*/
return str_ends_with(func->name, "_4core3num20from_str_radix_panic") ||
str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") ||
+ str_ends_with(func->name, "_4core3num28from_ascii_bytes_radix_panic") ||
+ str_ends_with(func->name, "_4core3str16slice_error_fail") ||
str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") ||
str_ends_with(func->name, "_4core6option13expect_failed") ||
str_ends_with(func->name, "_4core6option13unwrap_failed") ||
diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h
index 87f15f296234..bcc0b4c84fc0 100644
--- a/tools/sched_ext/include/scx/compat.bpf.h
+++ b/tools/sched_ext/include/scx/compat.bpf.h
@@ -31,7 +31,7 @@ struct cgroup *scx_bpf_task_cgroup___new(struct task_struct *p) __ksym __weak;
*
* v7.1: scx_bpf_dsq_move_to_local___v2() to add @enq_flags.
*/
-bool scx_bpf_dsq_move_to_local___v2(u64 dsq_id, u64 enq_flags) __ksym __weak;
+bool scx_bpf_dsq_move_to_local___v2___compat(u64 dsq_id, u64 enq_flags) __ksym __weak;
bool scx_bpf_dsq_move_to_local___v1(u64 dsq_id) __ksym __weak;
void scx_bpf_dsq_move_set_slice___new(struct bpf_iter_scx_dsq *it__iter, u64 slice) __ksym __weak;
void scx_bpf_dsq_move_set_vtime___new(struct bpf_iter_scx_dsq *it__iter, u64 vtime) __ksym __weak;
@@ -45,8 +45,8 @@ bool scx_bpf_dispatch_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct t
bool scx_bpf_dispatch_vtime_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct task_struct *p, u64 dsq_id, u64 enq_flags) __ksym __weak;
#define scx_bpf_dsq_move_to_local(dsq_id, enq_flags) \
- (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2) ? \
- scx_bpf_dsq_move_to_local___v2((dsq_id), (enq_flags)) : \
+ (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2___compat) ? \
+ scx_bpf_dsq_move_to_local___v2___compat((dsq_id), (enq_flags)) : \
(bpf_ksym_exists(scx_bpf_dsq_move_to_local___v1) ? \
scx_bpf_dsq_move_to_local___v1((dsq_id)) : \
scx_bpf_consume___old((dsq_id))))
@@ -414,10 +414,10 @@ static inline void scx_bpf_reenqueue_local(void)
}
/*
- * v6.20: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
+ * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
* will eventually deprecate scx_bpf_reenqueue_local().
*/
-void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags, const struct bpf_prog_aux *aux__prog) __ksym __weak;
+void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
static inline bool __COMPAT_has_generic_reenq(void)
{
@@ -427,7 +427,7 @@ static inline bool __COMPAT_has_generic_reenq(void)
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
{
if (bpf_ksym_exists(scx_bpf_dsq_reenq___compat))
- scx_bpf_dsq_reenq___compat(dsq_id, reenq_flags, NULL);
+ scx_bpf_dsq_reenq___compat(dsq_id, reenq_flags);
else if (dsq_id == SCX_DSQ_LOCAL && reenq_flags == 0)
scx_bpf_reenqueue_local();
else
diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h
index da4b459820fd..8e287c88ad90 100644
--- a/tools/sched_ext/include/scx/enum_defs.autogen.h
+++ b/tools/sched_ext/include/scx/enum_defs.autogen.h
@@ -143,7 +143,6 @@
#define HAVE___SCX_REENQ_TSR_MASK
#define HAVE_SCX_RQ_ONLINE
#define HAVE_SCX_RQ_CAN_STOP_TICK
-#define HAVE_SCX_RQ_BAL_KEEP
#define HAVE_SCX_RQ_CLK_VALID
#define HAVE_SCX_RQ_BAL_CB_PENDING
#define HAVE_SCX_RQ_IN_WAKEUP
diff --git a/tools/sched_ext/include/scx/enums.autogen.bpf.h b/tools/sched_ext/include/scx/enums.autogen.bpf.h
index dafccbb6b69d..d02abed94e8e 100644
--- a/tools/sched_ext/include/scx/enums.autogen.bpf.h
+++ b/tools/sched_ext/include/scx/enums.autogen.bpf.h
@@ -22,9 +22,6 @@ const volatile u64 __SCX_RQ_CAN_STOP_TICK __weak;
const volatile u64 __SCX_RQ_BAL_PENDING __weak;
#define SCX_RQ_BAL_PENDING __SCX_RQ_BAL_PENDING
-const volatile u64 __SCX_RQ_BAL_KEEP __weak;
-#define SCX_RQ_BAL_KEEP __SCX_RQ_BAL_KEEP
-
const volatile u64 __SCX_RQ_BYPASSING __weak;
#define SCX_RQ_BYPASSING __SCX_RQ_BYPASSING
diff --git a/tools/sched_ext/include/scx/enums.autogen.h b/tools/sched_ext/include/scx/enums.autogen.h
index bbd4901f4fce..c26934a3d8e9 100644
--- a/tools/sched_ext/include/scx/enums.autogen.h
+++ b/tools/sched_ext/include/scx/enums.autogen.h
@@ -11,7 +11,6 @@
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_ONLINE); \
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_CAN_STOP_TICK); \
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BAL_PENDING); \
- SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BAL_KEEP); \
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BYPASSING); \
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_CLK_VALID); \
SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_IN_WAKEUP); \
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index fd9a82a67627..0beac1abc587 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -749,7 +749,7 @@ static s64 task_qdist(struct task_struct *p)
bool BPF_STRUCT_OPS(qmap_core_sched_before,
struct task_struct *a, struct task_struct *b)
{
- return task_qdist(a) > task_qdist(b);
+ return task_qdist(a) < task_qdist(b);
}
/*
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 8c296dedf047..44cabdc54144 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -303,7 +303,7 @@ CATEGORY="compaction" run_test ./compaction_test
if command -v sudo &> /dev/null && sudo -u nobody ls ./on-fault-limit >/dev/null;
then
- CATEGORY="mlock" run_test sudo -u nobody ./on-fault-limit
+ CATEGORY="mlock" run_test sudo -u nobody sh -c ./on-fault-limit
else
echo "# SKIP ./on-fault-limit"
fi